Skip to content

Commit

Permalink
Merge pull request #46 from li3zhen1/issue-42
Browse files Browse the repository at this point in the history
Issue 42: Dragging on background, pinch to zoom
  • Loading branch information
li3zhen1 committed Feb 21, 2024
2 parents e74a32e + ca82715 commit a308de1
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 141 deletions.
288 changes: 152 additions & 136 deletions Sources/Grape/Views/ForceDirectedGraph+Gesture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,172 +2,188 @@ 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
) {
// print(value.magnification)
let alpha = -self.model.finalTransform.invert(value.startLocation.simd)
let oldScale = self.model.modelTransform.scale
let oldTranslate = self.model.modelTransform.translate
let newScale = clamp(
Darwin.cbrt(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 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
}

@inlinable
internal func onMagnifyEnd(
_ value: MagnifyGesture.Value
) {
let alpha = -self.model.finalTransform.invert(value.startLocation.simd)
let oldScale = self.model.modelTransform.scale
let oldTranslate = self.model.modelTransform.translate
let newScale = clamp(
Darwin.cbrt(value.magnification) * oldScale,
min: Self.minimumScale,
max: Self.maximumScale
)
let newTranslate = (oldScale - newScale) * alpha + oldTranslate
let newModelTransform = ViewportTransform(
translate: newTranslate,
scale: newScale
)
// print("newModelTransform", newModelTransform)
self.model.modelTransform = newModelTransform
guard let action = self.model._onGraphMagnified else { return }
action()
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()
}

@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()
}
}
}
#endif

extension ForceDirectedGraph {
@inlinable
public func onTicked(
perform action: @escaping (KeyFrame) -> Void
perform action: @escaping (UInt) -> Void
) -> Self {
self.model._onTicked = action
return self
Expand Down
2 changes: 1 addition & 1 deletion Sources/Grape/Views/ForceDirectedGraph+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ extension ForceDirectedGraph: View {
@inlinable
var debugView: some View {
VStack(alignment: .leading, spacing: 8.0) {
Text("Elapsed Time: \(model.currentFrame.rawValue)")
Text("Elapsed Time: \(model.currentFrame)")
Divider()
Text(self.model.changeMessage)
Divider()
Expand Down
Loading

0 comments on commit a308de1

Please sign in to comment.