From 5e8e93b53e1d43bbdba99869dfafbc59e34f88fd Mon Sep 17 00:00:00 2001 From: Guille Gonzalez Date: Wed, 31 Dec 2025 12:59:11 +0100 Subject: [PATCH] Add helpers --- .../Internal/Helpers/Platform.swift | 11 +++ .../Helpers/ReadWriteLockIsolated.swift | 67 +++++++++++++++ .../Internal/Helpers/Unicode.swift | 82 +++++++++++++++++++ Sources/SwiftUIMath/Untitled.swift | 6 -- 4 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 Sources/SwiftUIMath/Internal/Helpers/Platform.swift create mode 100644 Sources/SwiftUIMath/Internal/Helpers/ReadWriteLockIsolated.swift create mode 100644 Sources/SwiftUIMath/Internal/Helpers/Unicode.swift delete mode 100644 Sources/SwiftUIMath/Untitled.swift diff --git a/Sources/SwiftUIMath/Internal/Helpers/Platform.swift b/Sources/SwiftUIMath/Internal/Helpers/Platform.swift new file mode 100644 index 0000000..3b3f25f --- /dev/null +++ b/Sources/SwiftUIMath/Internal/Helpers/Platform.swift @@ -0,0 +1,11 @@ +// Derived from SwiftMath by Mike Griebling (MIT License) + +import SwiftUI + +#if canImport(UIKit) + typealias PlatformColor = UIColor + typealias PlatformBezierPath = UIBezierPath +#elseif canImport(AppKit) + typealias PlatformColor = NSColor + typealias PlatformBezierPath = NSBezierPath +#endif diff --git a/Sources/SwiftUIMath/Internal/Helpers/ReadWriteLockIsolated.swift b/Sources/SwiftUIMath/Internal/Helpers/ReadWriteLockIsolated.swift new file mode 100644 index 0000000..9a9df95 --- /dev/null +++ b/Sources/SwiftUIMath/Internal/Helpers/ReadWriteLockIsolated.swift @@ -0,0 +1,67 @@ +import Foundation + +@dynamicMemberLookup +final class ReadWriteLockIsolated: @unchecked Sendable { + private var _value: Value + private let lock: UnsafeMutablePointer = .create() + + init(_ value: @autoclosure @Sendable () throws -> Value) rethrows { + self._value = try value() + } + + deinit { + lock.destroy() + } + + subscript(dynamicMember keyPath: KeyPath) -> Subject { + self.lock.sync { + self._value[keyPath: keyPath] + } + } + + func withValue( + _ operation: @Sendable (inout Value) throws -> T + ) rethrows -> T { + try self.lock.sync { + var value = self._value + defer { self._value = value } + return try operation(&value) + } + } + + func setValue(_ newValue: @autoclosure @Sendable () throws -> Value) rethrows { + try self.lock.sync { + self._value = try newValue() + } + } +} + +extension ReadWriteLockIsolated where Value: Sendable { + var value: Value { + self.lock.sync { + self._value + } + } +} + +extension UnsafeMutablePointer where Pointee == pthread_rwlock_t { + fileprivate static func create() -> Self { + // allocate on the heap to create a stable pointer + let lock = Self.allocate(capacity: 1) + lock.initialize(to: pthread_rwlock_t()) + pthread_rwlock_init(lock, nil) + return lock + } + + fileprivate func destroy() { + pthread_rwlock_destroy(self) + self.deinitialize(count: 1) + self.deallocate() + } + + fileprivate func sync(work: () throws -> R) rethrows -> R { + pthread_rwlock_wrlock(self) + defer { pthread_rwlock_unlock(self) } + return try work() + } +} diff --git a/Sources/SwiftUIMath/Internal/Helpers/Unicode.swift b/Sources/SwiftUIMath/Internal/Helpers/Unicode.swift new file mode 100644 index 0000000..087dd64 --- /dev/null +++ b/Sources/SwiftUIMath/Internal/Helpers/Unicode.swift @@ -0,0 +1,82 @@ +// Derived from SwiftMath by Mike Griebling (MIT License) + +import Foundation + +extension String { + static let multiplication = "\u{00D7}" + static let division = "\u{00F7}" + static let fractionSlash = "\u{2044}" + static let whiteSquare = "\u{25A1}" + static let blackSquare = "\u{25A0}" + static let lessEqual = "\u{2264}" + static let greaterEqual = "\u{2265}" + static let notEqual = "\u{2260}" + static let squareRoot = "\u{221A}" + static let cubeRoot = "\u{221B}" + static let infinity = "\u{221E}" + static let angle = "\u{2220}" + static let degree = "\u{00B0}" +} + +extension UTF32Char { + static let capitalGreekStart = UTF32Char(0x0391) + static let capitalGreekEnd = UTF32Char(0x03A9) + static let lowerGreekStart = UTF32Char(0x03B1) + static let lowerGreekEnd = UTF32Char(0x03C9) + static let planksConstant = UTF32Char(0x210e) + static let lowerItalicStart = UTF32Char(0x1D44E) + static let capitalItalicStart = UTF32Char(0x1D434) + static let greekLowerItalicStart = UTF32Char(0x1D6FC) + static let greekCapitalItalicStart = UTF32Char(0x1D6E2) + static let greekSymbolItalicStart = UTF32Char(0x1D716) + + static let mathCapitalBoldStart = UTF32Char(0x1D400) + static let mathLowerBoldStart = UTF32Char(0x1D41A) + static let greekCapitalBoldStart = UTF32Char(0x1D6A8) + static let greekLowerBoldStart = UTF32Char(0x1D6C2) + static let greekSymbolBoldStart = UTF32Char(0x1D6DC) + static let numberBoldStart = UTF32Char(0x1D7CE) + + static let mathCapitalBoldItalicStart = UTF32Char(0x1D468) + static let mathLowerBoldItalicStart = UTF32Char(0x1D482) + static let greekCapitalBoldItalicStart = UTF32Char(0x1D71C) + static let greekLowerBoldItalicStart = UTF32Char(0x1D736) + static let greekSymbolBoldItalicStart = UTF32Char(0x1D750) + + static let mathCapitalScriptStart = UTF32Char(0x1D49C) + static let mathCapitalTTStart = UTF32Char(0x1D670) + static let mathLowerTTStart = UTF32Char(0x1D68A) + static let numberTTStart = UTF32Char(0x1D7F6) + static let mathCapitalSansSerifStart = UTF32Char(0x1D5A0) + static let mathLowerSansSerifStart = UTF32Char(0x1D5BA) + static let numberSansSerifStart = UTF32Char(0x1D7E2) + static let mathCapitalFrakturStart = UTF32Char(0x1D504) + static let mathLowerFrakturStart = UTF32Char(0x1D51E) + static let mathCapitalBlackboardStart = UTF32Char(0x1D538) + static let mathLowerBlackboardStart = UTF32Char(0x1D552) + static let numberBlackboardStart = UTF32Char(0x1D7D8) +} + +extension Character { + var utf32: UTF32Char { self.unicodeScalars.map { $0.value }.reduce(0, +) } + var isLowerEnglish: Bool { self >= "a" && self <= "z" } + var isUpperEnglish: Bool { self >= "A" && self <= "Z" } + var isNumber: Bool { self >= "0" && self <= "9" } + + var isLowerGreek: Bool { + (UTF32Char.lowerGreekStart...UTF32Char.lowerGreekEnd).contains(self.utf32) + } + + var isCapitalGreek: Bool { + (UTF32Char.capitalGreekStart...UTF32Char.capitalGreekEnd).contains(self.utf32) + } + + var greekSymbolOrder: UInt32? { + let greekSymbols: [UTF32Char] = [0x03F5, 0x03D1, 0x03F0, 0x03D5, 0x03F1, 0x03D6] + let index = greekSymbols.firstIndex(of: self.utf32) + if let pos = index { return UInt32(pos) } + return nil + } + + var isGreekSymbol: Bool { self.greekSymbolOrder != nil } +} diff --git a/Sources/SwiftUIMath/Untitled.swift b/Sources/SwiftUIMath/Untitled.swift deleted file mode 100644 index 15fdc1e..0000000 --- a/Sources/SwiftUIMath/Untitled.swift +++ /dev/null @@ -1,6 +0,0 @@ -// -// Untitled.swift -// swiftui-math -// -// Created by Guille Gonzalez on 31/12/25. -//