diff --git a/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample.xcodeproj/project.pbxproj b/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample.xcodeproj/project.pbxproj index 812de4f..f9798d9 100644 --- a/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample.xcodeproj/project.pbxproj +++ b/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample.xcodeproj/project.pbxproj @@ -126,7 +126,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1500; - LastUpgradeCheck = 1500; + LastUpgradeCheck = 1530; TargetAttributes = { B7AFA5562ADF4997009C7154 = { CreatedOnToolsVersion = 15.0; @@ -219,6 +219,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -285,6 +286,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -335,7 +337,7 @@ SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,7"; }; @@ -366,6 +368,7 @@ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,7"; }; diff --git a/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample.xcodeproj/xcshareddata/xcschemes/ForceDirectedGraphExample.xcscheme b/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample.xcodeproj/xcshareddata/xcschemes/ForceDirectedGraphExample.xcscheme index b075a52..4964ef3 100644 --- a/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample.xcodeproj/xcshareddata/xcschemes/ForceDirectedGraphExample.xcscheme +++ b/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample.xcodeproj/xcshareddata/xcschemes/ForceDirectedGraphExample.xcscheme @@ -1,6 +1,6 @@ some View { @@ -36,7 +36,7 @@ struct MiserableGraph: View { RoundedRectangle(cornerSize: .init(width: 12, height: 12)) .fill(.foreground) .shadow(radius: 1.5, y: 1.0) - } + } .padding() } @@ -48,12 +48,17 @@ struct MiserableGraph: View { Series(graphData.nodes) { node in NodeMark(id: node.id) - .symbol(.circle) - .symbolSize(radius: 8.0) - .stroke() - .richLabel(node.id, offset: .zero) { - self.getLabel(node.id) - } + .symbol(.circle) + .symbolSize(radius: 8.0) + .foregroundStyle(colors[node.group % colors.count]) + .stroke() + .richLabel(node.id, offset: .zero) { + if (graphData.links.filter({ l in + return l.source == node.id || l.target == node.id} + ).count > 12) { + self.getLabel(node.id) + } + } } Series(graphData.links) { l in @@ -68,12 +73,9 @@ struct MiserableGraph: View { stiffness: .weightedByDegree(k: { _, _ in 1.0}) ) } - .opacity(opacity) - .animation(.easeInOut, value: opacity) - .ignoresSafeArea() .toolbar { - MiserableToolbarContent(stateMixin: stateMixin, opacity: $opacity) + GraphStateToggle(graphStates: stateMixin) } } } @@ -85,7 +87,7 @@ struct MiserableToolbarContent: View { var body: some View { Group { Button { - stateMixin.modelTransform.scaling(by: 1.1) + stateMixin.modelTransform.scaling(by: 0.9) } label: { Image(systemName: "minus") } @@ -96,7 +98,7 @@ struct MiserableToolbarContent: View { .fontDesign(.monospaced) } Button { - stateMixin.modelTransform.scaling(by: 0.9) + stateMixin.modelTransform.scaling(by: 1.1) } label: { Image(systemName: "plus") } diff --git a/Sources/Grape/Views/ForceDirectedGraph+Gesture.swift b/Sources/Grape/Views/ForceDirectedGraph+Gesture.swift index bc865a1..dbfb6a1 100644 --- a/Sources/Grape/Views/ForceDirectedGraph+Gesture.swift +++ b/Sources/Grape/Views/ForceDirectedGraph+Gesture.swift @@ -98,7 +98,7 @@ import SwiftUI static var minimumScaleDelta: CGFloat { 0.001 } @inlinable - static var minimumScale: CGFloat { 1e-4 } + static var minimumScale: CGFloat { 1e-2 } @inlinable static var maximumScale: CGFloat { .infinity } diff --git a/Sources/Grape/Views/ForceDirectedGraphModel.swift b/Sources/Grape/Views/ForceDirectedGraphModel.swift index d800978..9e18046 100644 --- a/Sources/Grape/Views/ForceDirectedGraphModel.swift +++ b/Sources/Grape/Views/ForceDirectedGraphModel.swift @@ -165,7 +165,6 @@ public final class ForceDirectedGraphModel { _ graphRenderingContext: _GraphRenderingContext, _ forceField: SealedForce2D, stateMixin: ForceDirectedGraphState, -// modelTransform: Binding, emittingNewNodesWith: @escaping (NodeID) -> KineticState = { _ in .init(position: .zero) }, @@ -191,7 +190,6 @@ public final class ForceDirectedGraphModel { ) self.currentFrame = 0 self.stateMixinRef = stateMixin - // self._modelTransform = stateMixin.modelTransform } @inlinable @@ -199,7 +197,6 @@ public final class ForceDirectedGraphModel { _ graphRenderingContext: _GraphRenderingContext, _ forceField: SealedForce2D, stateMixin: ForceDirectedGraphState, -// modelTransform: Binding, emittingNewNodesWith: @escaping (NodeID) -> KineticState = { _ in .init(position: .zero) }, @@ -209,7 +206,6 @@ public final class ForceDirectedGraphModel { graphRenderingContext, forceField, stateMixin: stateMixin, -// modelTransform: modelTransform, emittingNewNodesWith: emittingNewNodesWith, ticksPerSecond: ticksPerSecond, velocityDecay: 30 / ticksPerSecond @@ -229,9 +225,11 @@ public final class ForceDirectedGraphModel { @inlinable func continuouslyTrackingRunning() { - withObservationTracking { - updateModelRunningState(isRunning: stateMixinRef.isRunning) - } onChange: { + withObservationTracking { [weak self] in + guard let self else { return } + self.updateModelRunningState(isRunning: self.stateMixinRef.isRunning) + } onChange: { [weak self] in + guard let self else { return } Task { @MainActor [weak self] in self?.continuouslyTrackingRunning() } @@ -240,11 +238,13 @@ public final class ForceDirectedGraphModel { @inlinable func continuouslyTrackingTransform() { - withObservationTracking { + withObservationTracking { [weak self] in + guard let self else { return } // FIXME: mutation cycle? - _ = stateMixinRef.modelTransform + _ = self.stateMixinRef.modelTransform // stateMixinRef.access(keyPath: \.modelTransform) - } onChange: { + } onChange: { [weak self] in + guard let self else { return } Task { @MainActor [weak self] in self?.continuouslyTrackingTransform() } @@ -299,8 +299,8 @@ extension ForceDirectedGraphModel { @inlinable // @MainActor func start(minAlpha: Double = 0.6) { - print("Into start") guard self.scheduledTimer == nil else { return } + print("Simulation started") if simulationContext.storage.kinetics.alpha < minAlpha { simulationContext.storage.kinetics.alpha = minAlpha } @@ -325,7 +325,7 @@ extension ForceDirectedGraphModel { @inlinable // @MainActor func stop() { - print("Into stop") + print("Simulation stopped") self.scheduledTimer?.invalidate() self.scheduledTimer = nil }