Skip to content

Commit

Permalink
Fix magnify gesture
Browse files Browse the repository at this point in the history
  • Loading branch information
li3zhen1 committed Feb 21, 2024
1 parent ae3080e commit ca82715
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 151 deletions.
298 changes: 149 additions & 149 deletions Sources/Grape/Views/ForceDirectedGraph+Gesture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,182 +2,182 @@ import ForceSimulation
import SwiftUI

#if !os(tvOS)
extension ForceDirectedGraph {
@inlinable
static var minimumAlphaAfterDrag: CGFloat { 0.5 }
@inlinable
internal func onDragChange(
_ value: SwiftUI.DragGesture.Value
) {
if !model.isDragStartStateRecorded {
if let nodeID = model.findNode(at: value.startLocation) {
model.draggingNodeID = nodeID
} else {
model.backgroundDragStart = value.location.simd
extension ForceDirectedGraph {
@inlinable
static var minimumAlphaAfterDrag: CGFloat { 0.5 }
@inlinable
internal func onDragChange(
_ value: SwiftUI.DragGesture.Value
) {
if !model.isDragStartStateRecorded {
if let nodeID = model.findNode(at: value.startLocation) {
model.draggingNodeID = nodeID
} else {
model.backgroundDragStart = value.location.simd
}
assert(model.isDragStartStateRecorded == true)
}
assert(model.isDragStartStateRecorded == true)
}

guard let nodeID = model.draggingNodeID else {
if let dragStart = model.backgroundDragStart {
let delta = value.location.simd - dragStart
model.modelTransform.translate += delta
model.backgroundDragStart = value.location.simd

guard let nodeID = model.draggingNodeID else {
if let dragStart = model.backgroundDragStart {
let delta = value.location.simd - dragStart
model.modelTransform.translate += delta
model.backgroundDragStart = value.location.simd
}
return
}
return
}

if model.simulationContext.storage.kinetics.alpha < Self.minimumAlphaAfterDrag {
model.simulationContext.storage.kinetics.alpha = Self.minimumAlphaAfterDrag
}
if model.simulationContext.storage.kinetics.alpha < Self.minimumAlphaAfterDrag {
model.simulationContext.storage.kinetics.alpha = Self.minimumAlphaAfterDrag
}

let newLocationInSimulation = model.finalTransform.invert(value.location.simd)
let newLocationInSimulation = model.finalTransform.invert(value.location.simd)

if let nodeIndex = model.simulationContext.nodeIndexLookup[nodeID] {
model.simulationContext.storage.kinetics.fixation[
nodeIndex
] = newLocationInSimulation
}
if let nodeIndex = model.simulationContext.nodeIndexLookup[nodeID] {
model.simulationContext.storage.kinetics.fixation[
nodeIndex
] = newLocationInSimulation
}

guard let action = model._onNodeDragChanged else { return }
action(nodeID, value.location)
guard let action = model._onNodeDragChanged else { return }
action(nodeID, value.location)

}
}

@inlinable
internal func onDragEnd(
_ value: SwiftUI.DragGesture.Value
) {

guard let nodeID = model.draggingNodeID else {
if let dragStart = model.backgroundDragStart {
let delta = value.location.simd - dragStart
model.modelTransform.translate += delta
model.backgroundDragStart = nil
@inlinable
internal func onDragEnd(
_ value: SwiftUI.DragGesture.Value
) {

guard let nodeID = model.draggingNodeID else {
if let dragStart = model.backgroundDragStart {
let delta = value.location.simd - dragStart
model.modelTransform.translate += delta
model.backgroundDragStart = nil
}
return
}
if model.simulationContext.storage.kinetics.alpha < Self.minimumAlphaAfterDrag {
model.simulationContext.storage.kinetics.alpha = Self.minimumAlphaAfterDrag
}
return
}
if model.simulationContext.storage.kinetics.alpha < Self.minimumAlphaAfterDrag {
model.simulationContext.storage.kinetics.alpha = Self.minimumAlphaAfterDrag
}

model.draggingNodeID = nil

guard let nodeIndex = model.simulationContext.nodeIndexLookup[nodeID] else { return }
if model._onNodeDragEnded == nil {
model.simulationContext.storage.kinetics.fixation[
nodeIndex
] = nil
} else if let action = model._onNodeDragEnded, action(nodeID, value.location) {
model.simulationContext.storage.kinetics.fixation[
nodeIndex
] = nil
model.draggingNodeID = nil

guard let nodeIndex = model.simulationContext.nodeIndexLookup[nodeID] else { return }
if model._onNodeDragEnded == nil {
model.simulationContext.storage.kinetics.fixation[
nodeIndex
] = nil
} else if let action = model._onNodeDragEnded, action(nodeID, value.location) {
model.simulationContext.storage.kinetics.fixation[
nodeIndex
] = nil
}
}
}

@inlinable
static var minimumDragDistance: CGFloat { 3.0 }
}
@inlinable
static var minimumDragDistance: CGFloat { 3.0 }
}

extension ForceDirectedGraph {
@inlinable
internal func onTapGesture(
_ location: CGPoint
) {
guard let action = self.model._onNodeTapped else { return }
let nodeID = self.model.findNode(at: location)
action(nodeID)
extension ForceDirectedGraph {
@inlinable
internal func onTapGesture(
_ location: CGPoint
) {
guard let action = self.model._onNodeTapped else { return }
let nodeID = self.model.findNode(at: location)
action(nodeID)
}
}
}
#endif

#if os(iOS) || os(macOS)
extension ForceDirectedGraph {
extension ForceDirectedGraph {

@inlinable
static var minimumScaleDelta: CGFloat { 0.001 }
@inlinable
static var minimumScaleDelta: CGFloat { 0.001 }

@inlinable
static var minimumScale: CGFloat { 0.25 }
@inlinable
static var minimumScale: CGFloat { 0.25 }

@inlinable
static var maximumScale: CGFloat { 4.0 }
@inlinable
static var maximumScale: CGFloat { 4.0 }

@inlinable
static var magnificationDecay: CGFloat { 0.1 }
@inlinable
static var magnificationDecay: CGFloat { 0.1 }

@inlinable
internal func clamp(
_ value: CGFloat,
min: CGFloat,
max: CGFloat
) -> CGFloat {
Swift.min(Swift.max(value, min), max)
}
@inlinable
internal func clamp(
_ value: CGFloat,
min: CGFloat,
max: CGFloat
) -> CGFloat {
Swift.min(Swift.max(value, min), max)
}

@inlinable
internal func onMagnifyChange(
_ value: MagnifyGesture.Value
) {
var oldScale: Double
if let _scale = self.model.lastScaleRecord {
oldScale = _scale
} else {
self.model.lastScaleRecord = self.model.modelTransform.scale
oldScale = self.model.modelTransform.scale
@inlinable
internal func onMagnifyChange(
_ value: MagnifyGesture.Value
) {
var startTransform: ViewportTransform
if let t = self.model.lastTransformRecord {
startTransform = t
} else {
self.model.lastTransformRecord = self.model.modelTransform
startTransform = self.model.modelTransform
}

let alpha = (startTransform.translate(by: self.model.obsoleteState.cgSize.simd / 2))
.invert(value.startLocation.simd)

let newScale = clamp(
value.magnification * startTransform.scale,
min: Self.minimumScale,
max: Self.maximumScale)

let newTranslate = (startTransform.scale - newScale) * alpha + startTransform.translate

let newModelTransform = ViewportTransform(
translate: newTranslate,
scale: newScale
)
self.model.modelTransform = newModelTransform

guard let action = self.model._onGraphMagnified else { return }
action()
}

let alpha = -self.model.finalTransform.invert(value.startLocation.simd)

let oldTranslate = self.model.modelTransform.translate
let newScale = clamp(
value.magnification * oldScale,
min: Self.minimumScale,
max: Self.maximumScale)

let newTranslate = (oldScale - newScale) * alpha + oldTranslate

let newModelTransform = ViewportTransform(
translate: newTranslate,
scale: newScale
)
self.model.modelTransform = newModelTransform

guard let action = self.model._onGraphMagnified else { return }
action()
}

@inlinable
internal func onMagnifyEnd(
_ value: MagnifyGesture.Value
) {
var oldScale: Double
if let _scale = self.model.lastScaleRecord {
oldScale = _scale
} else {
self.model.lastScaleRecord = self.model.modelTransform.scale
oldScale = self.model.modelTransform.scale
@inlinable
internal func onMagnifyEnd(
_ value: MagnifyGesture.Value
) {
var startTransform: ViewportTransform
if let t = self.model.lastTransformRecord {
startTransform = t
} else {
self.model.lastTransformRecord = self.model.modelTransform
startTransform = self.model.modelTransform
}

let alpha = (startTransform.translate(by: self.model.obsoleteState.cgSize.simd / 2))
.invert(value.startLocation.simd)

let newScale = clamp(
value.magnification * startTransform.scale,
min: Self.minimumScale,
max: Self.maximumScale)

let newTranslate = (startTransform.scale - newScale) * alpha + startTransform.translate
let newModelTransform = ViewportTransform(
translate: newTranslate,
scale: newScale
)
self.model.lastTransformRecord = nil
self.model.modelTransform = newModelTransform
guard let action = self.model._onGraphMagnified else { return }
action()
}

let alpha = -self.model.finalTransform.invert(value.startLocation.simd)

let oldTranslate = self.model.modelTransform.translate
let newScale = clamp(
value.magnification * oldScale,
min: Self.minimumScale,
max: Self.maximumScale
)
let newTranslate = (oldScale - newScale) * alpha + oldTranslate
let newModelTransform = ViewportTransform(
translate: newTranslate,
scale: newScale
)
self.model.lastScaleRecord = nil
self.model.modelTransform = newModelTransform
guard let action = self.model._onGraphMagnified else { return }
action()
}
}
#endif

extension ForceDirectedGraph {
Expand Down
17 changes: 15 additions & 2 deletions Sources/Grape/Views/ForceDirectedGraphModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import SwiftUI
// @Observable
public final class ForceDirectedGraphModel<Content: GraphContent> {

@usableFromInline
internal struct ObsoleteState {
@usableFromInline
var cgSize: CGSize
}

public typealias NodeID = Content.NodeID

@usableFromInline
Expand Down Expand Up @@ -55,9 +61,9 @@ public final class ForceDirectedGraphModel<Content: GraphContent> {
return draggingNodeID != nil || backgroundDragStart != nil
}

// records the scale right before a magnification gesture starts
// records the transform right before a magnification gesture starts
@usableFromInline
var lastScaleRecord: Double? = nil
var lastTransformRecord: ViewportTransform? = nil


@usableFromInline
Expand Down Expand Up @@ -142,6 +148,11 @@ public final class ForceDirectedGraphModel<Content: GraphContent> {
@usableFromInline
var _onGraphMagnified: (() -> Void)? = nil


// // records the transform right before a magnification gesture starts
@usableFromInline
var obsoleteState = ObsoleteState(cgSize: .zero)

@inlinable
init(
_ graphRenderingContext: _GraphRenderingContext<NodeID>,
Expand Down Expand Up @@ -171,6 +182,7 @@ public final class ForceDirectedGraphModel<Content: GraphContent> {
count: self.simulationContext.storage.kinetics.position.count
)
self.currentFrame = 0
// self.lastViewportSize = .zero
self._modelTransformExtenalBinding = modelTransform
self.modelTransform = modelTransform.wrappedValue
}
Expand Down Expand Up @@ -265,6 +277,7 @@ extension ForceDirectedGraphModel {
) {
// should not invoke `access`, but actually does now ?
// print("Rendering frame \(_$currentFrame.rawValue)")
obsoleteState.cgSize = size

let transform = modelTransform.translate(by: size.simd / 2)
// debugPrint(transform.scale)
Expand Down

0 comments on commit ca82715

Please sign in to comment.