Skip to content

Commit 6bc5743

Browse files
committed
Fix drag freezing
1 parent 37233b7 commit 6bc5743

File tree

8 files changed

+62
-27
lines changed

8 files changed

+62
-27
lines changed

Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/Lattice.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Grape
1111

1212
struct Lattice: View {
1313

14-
let width = 20
14+
let width = 30
1515
let edge: [(Int, Int)]
1616

1717
@State var graphStates = ForceDirectedGraphState(
@@ -33,7 +33,6 @@ struct Lattice: View {
3333
self.edge = edge
3434
}
3535

36-
@inlinable
3736
var body: some View {
3837
ForceDirectedGraph(states: graphStates) {
3938

@@ -51,11 +50,15 @@ struct Lattice: View {
5150

5251
} force: {
5352
.link(
54-
originalLength: .constant(0.8),
53+
originalLength: 0.8,
5554
stiffness: .weightedByDegree { _, _ in 1.0 }
5655
)
5756
.manyBody(strength: -0.8)
5857
}
58+
.graphOverlay(content: { proxy in
59+
Rectangle().fill(.clear).contentShape(Rectangle())
60+
.withGraphDragGesture(proxy)
61+
})
5962
.toolbar {
6063
GraphStateToggle(graphStates: graphStates)
6164
}

Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/MermaidVisualization.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ struct MermaidVisualization: View {
100100

101101
} force: {
102102
.manyBody()
103-
.link(originalLength: .constant(70))
103+
.link(originalLength: 70.0)
104104
.center()
105105
} emittingNewNodesWithStates: { id in
106106
KineticState(position: getInitialPosition(id: id, r: 100))
@@ -111,7 +111,6 @@ struct MermaidVisualization: View {
111111
.onTapGesture { value in
112112
if let nodeID = proxy.locateNode(at: .init(x: value.x, y: value.y)) {
113113
guard let nodeID = nodeID as? String else { return }
114-
print(nodeID)
115114
model.tappedNode = nodeID
116115
}
117116
}

Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/Miserables.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ struct MiserableGraph: View {
6969
.manyBody(strength: -20)
7070
.center()
7171
.link(
72-
originalLength: .constant(35.0),
72+
originalLength: 35.0,
7373
stiffness: .weightedByDegree { _, _ in 1.0}
7474
)
7575
}

Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/MyRing.swift

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,43 +14,44 @@ struct MyRing: View {
1414

1515
@State var graphStates = ForceDirectedGraphState()
1616

17+
@State var draggingNodeID: Int? = nil
18+
19+
static let storkeStyle = StrokeStyle(lineWidth: 1.5, lineCap: .round, lineJoin: .round)
20+
1721
var body: some View {
1822

1923
ForceDirectedGraph(states: graphStates) {
2024
Series(0..<20) { i in
25+
2126
NodeMark(id: 3 * i + 0)
2227
.symbolSize(radius: 6.0)
2328
.foregroundStyle(.green)
24-
.stroke(.clear)
29+
.stroke(3*i+0 == draggingNodeID ? .black : .clear, Self.storkeStyle)
30+
2531
NodeMark(id: 3 * i + 1)
2632
.symbol(.pentagon)
2733
.symbolSize(radius:10)
2834
.foregroundStyle(.blue)
29-
.stroke(.clear)
35+
.stroke(3*i+1 == draggingNodeID ? .black : .clear, Self.storkeStyle)
36+
3037
NodeMark(id: 3 * i + 2)
3138
.symbol(.circle)
3239
.symbolSize(radius:6.0)
3340
.foregroundStyle(.yellow)
34-
.stroke(.clear)
41+
.stroke(3*i+2 == draggingNodeID ? .black : .clear, Self.storkeStyle)
3542

3643
LinkMark(from: 3 * i + 0, to: 3 * i + 1)
3744
LinkMark(from: 3 * i + 1, to: 3 * i + 2)
38-
3945
LinkMark(from: 3 * i + 0, to: 3 * ((i + 1) % 20) + 0)
4046
LinkMark(from: 3 * i + 1, to: 3 * ((i + 1) % 20) + 1)
4147
LinkMark(from: 3 * i + 2, to: 3 * ((i + 1) % 20) + 2)
42-
43-
4448
}
45-
.stroke(
46-
.black,
47-
StrokeStyle(lineWidth: 1.5, lineCap: .round, lineJoin: .round)
48-
)
49+
.stroke(.black, Self.storkeStyle)
4950

5051
} force: {
5152
.manyBody(strength: -15)
5253
.link(
53-
originalLength: .constant(20.0),
54+
originalLength: 20.0,
5455
stiffness: .weightedByDegree { _, _ in 3.0}
5556
)
5657
.center()
@@ -69,10 +70,16 @@ struct MyRing: View {
6970
func describe(_ state: GraphDragState?) {
7071
switch state {
7172
case .node(let anyHashable):
72-
print("Dragging \(anyHashable as! Int)")
73+
let id = anyHashable as! Int
74+
if draggingNodeID != id {
75+
draggingNodeID = id
76+
print("Dragging \(id)")
77+
}
7378
case .background(let start):
79+
draggingNodeID = nil
7480
print("Dragging \(start)")
7581
case nil:
82+
draggingNodeID = nil
7683
print("Drag ended")
7784
}
7885
}

Sources/Grape/Descriptors/ForceDescriptor.swift

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ public enum NodeAttribute<NodeID: Hashable, Attribute> {
66
case constant(Attribute)
77
}
88

9+
extension NodeAttribute: ExpressibleByFloatLiteral where Attribute == Double {
10+
@inlinable
11+
public init(floatLiteral value: Double) {
12+
self = .constant(value)
13+
}
14+
}
15+
916
extension NodeAttribute {
1017
@inlinable
1118
func makeCompactRepresentation(nodeIDs: [NodeID]) -> ForceSimulation.AttributeDescriptor<Attribute> {
@@ -270,14 +277,26 @@ public struct ManyBodyForce<NodeID: Hashable>: _ForceDescriptor {
270277

271278
public struct LinkForce<NodeID: Hashable>: _ForceDescriptor {
272279

273-
public enum Stiffness {
280+
public enum Stiffness: ExpressibleByFloatLiteral {
274281
case constant(Double)
275282
case weightedByDegree((EdgeID<NodeID>, LinkLookup<NodeID>) -> Double)
283+
284+
@inlinable
285+
public init(floatLiteral value: Double) {
286+
self = .weightedByDegree({ _, _ in
287+
value
288+
})
289+
}
276290
}
277291

278-
public enum LinkLength {
292+
public enum LinkLength: ExpressibleByFloatLiteral {
279293
case constant(Double)
280294
case varied((EdgeID<NodeID>, LinkLookup<NodeID>) -> Double)
295+
296+
@inlinable
297+
public init(floatLiteral value: Double) {
298+
self = .constant(value)
299+
}
281300
}
282301
public var stiffness: Stiffness
283302
public var originalLength: LinkLength

Sources/Grape/Gestures/GraphDragGesture.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ struct GraphDragModifier: ViewModifier {
8181
if dragState == nil {
8282
if let nodeID = graphProxy.locateNode(at: value.startLocation) {
8383
dragState = .node(nodeID)
84+
8485
graphProxy.setNodeFixation(nodeID: nodeID, fixation: value.startLocation)
8586
} else {
8687
dragState = .background(start: value.location.simd)

Sources/Grape/Modifiers/GraphProxy.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ extension GraphProxy: _AnyGraphProxyProtocol {
2626
}
2727

2828
@inlinable
29-
public func setNodeFixation(nodeID: some Hashable, fixation: CGPoint?) {
30-
storage?.setNodeFixation(nodeID: nodeID, fixation: fixation)
29+
public func setNodeFixation(nodeID: some Hashable, fixation: CGPoint?, minimumAlpha: Double = 0.5) {
30+
storage?.setNodeFixation(nodeID: nodeID, fixation: fixation, minimumAlpha: minimumAlpha)
3131
}
3232

3333
@inlinable

Sources/Grape/Views/ForceDirectedGraphModel.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public protocol _AnyGraphProxyProtocol {
99
func locateNode(at locationInViewportCoordinate: CGPoint) -> AnyHashable?
1010

1111
@inlinable
12-
func setNodeFixation(nodeID: some Hashable, fixation: CGPoint?)
12+
func setNodeFixation(nodeID: some Hashable, fixation: CGPoint?, minimumAlpha: Double)
1313

1414
@inlinable
1515
var kineticAlpha: Double { get nonmutating set }
@@ -38,10 +38,16 @@ extension ForceDirectedGraphModel: _AnyGraphProxyProtocol {
3838
}
3939

4040
@inlinable
41-
public func setNodeFixation(nodeID: some Hashable, fixation: CGPoint?) {
41+
public func setNodeFixation(nodeID: some Hashable, fixation: CGPoint?, minimumAlpha: Double) {
4242
guard let nodeID = nodeID as? NodeID else {
4343
return
4444
}
45+
46+
simulationContext.storage.kinetics.alpha = max(
47+
simulationContext.storage.kinetics.alpha,
48+
minimumAlpha
49+
)
50+
4551
let newLocationInSimulation: SIMD2<Double>? =
4652
if let fixation {
4753
finalTransform.invert(fixation.simd)
@@ -58,8 +64,8 @@ extension ForceDirectedGraphModel: _AnyGraphProxyProtocol {
5864
get {
5965
simulationContext.storage.kinetics.alpha
6066
}
61-
set {
62-
simulationContext.storage.kinetics.alpha = newValue
67+
_modify {
68+
yield &simulationContext.storage.kinetics.alpha
6369
}
6470
}
6571
}
@@ -720,7 +726,7 @@ extension ForceDirectedGraphModel {
720726
count: self.simulationContext.storage.kinetics.position.count
721727
)
722728
}
723-
debugPrint("[REVIVED]")
729+
debugPrint("Graph state revived. Note this might cause expensive rerendering when combined with `richLabel` with unstable id.")
724730
}
725731

726732
}

0 commit comments

Comments
 (0)