Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new magnet axes #8506

Merged
merged 5 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/gui2/src/components/GraphEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const graphBindingsHandler = graphBindings.handler({
for (const node of nodeSelection.selected) {
graphStore.deleteNode(node)
}
nodeSelection.selected.clear()
})
},
zoomToSelected() {
Expand All @@ -158,7 +159,7 @@ const graphBindingsHandler = graphBindings.handler({
let right = -Infinity
let bottom = -Infinity
const nodesToCenter =
nodeSelection.selected.size === 0 ? graphStore.nodeRects.keys() : nodeSelection.selected
nodeSelection.selected.size === 0 ? graphStore.currentNodeIds : nodeSelection.selected
for (const id of nodesToCenter) {
const rect = graphStore.nodeRects.get(id)
if (!rect) continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ test.each`
}
const grid = new SnapGrid(computed(() => rects))
const xSnapped = new Rect(new Vec2(snappedRectPosition, 0.0), new Vec2(10.0, 10.0))
expect(grid.snap(xSnapped, 15.0)[0]).toBeCloseTo(expectedSnap)
expect(grid.snap(xSnapped, 16.0).x).toBeCloseTo(expectedSnap)
const ySnapped = new Rect(new Vec2(0.0, snappedRectPosition), new Vec2(10.0, 10.0))
expect(grid.snap(ySnapped, 15.0)[1]).toBeCloseTo(expectedSnap)
expect(grid.snap(ySnapped, 16.0).y).toBeCloseTo(expectedSnap)
},
)
48 changes: 30 additions & 18 deletions app/gui2/src/components/GraphEditor/dragging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ import { useApproach } from '@/util/animation'
import { partitionPoint } from '@/util/array'
import type { Opt } from '@/util/opt'
import { Rect } from '@/util/rect'
import theme from '@/util/theme.json'
import { Vec2 } from '@/util/vec2'
import { iteratorFilter } from 'lib0/iterator'
import { abs } from 'lib0/math'
import type { ExprId } from 'shared/yjsModel'
import { computed, markRaw, ref, watchEffect, type ComputedRef, type WatchStopHandle } from 'vue'

const DRAG_SNAP_THRESHOLD = 15
const DRAG_SNAP_THRESHOLD = 16
const VERTICAL_GAP = theme.node.vertical_gap

interface PartialVec2 {
x: number | null
y: number | null
}

export class SnapGrid {
leftAxes: ComputedRef<number[]>
Expand All @@ -26,29 +32,35 @@ export class SnapGrid {
this.rightAxes = computed(() =>
Array.from(rects.value, (rect) => rect.right).sort((a, b) => a - b),
)
this.topAxes = computed(() => Array.from(rects.value, (rect) => rect.top).sort((a, b) => a - b))
this.topAxes = computed(() =>
Array.from(rects.value, (rect) => rect.top)
.concat(Array.from(rects.value, (rect) => rect.bottom + VERTICAL_GAP))
.sort((a, b) => a - b),
)
this.bottomAxes = computed(() =>
Array.from(rects.value, (rect) => rect.bottom).sort((a, b) => a - b),
Array.from(rects.value, (rect) => rect.bottom)
.concat(Array.from(rects.value, (rect) => rect.top - VERTICAL_GAP))
.sort((a, b) => a - b),
)
}

snappedMany(rects: Rect[], threshold: number): Vec2 {
const minSnap = rects.reduce<[x: number | null, y: number | null]>(
const minSnap = rects.reduce<PartialVec2>(
(minSnap, rect) => {
const [xSnap, ySnap] = this.snap(rect, threshold)
return [SnapGrid.minSnap(minSnap[0], xSnap), SnapGrid.minSnap(minSnap[1], ySnap)]
const snap = this.snap(rect, threshold)
return { x: SnapGrid.minSnap(minSnap.x, snap.x), y: SnapGrid.minSnap(minSnap.y, snap.y) }
},
[null, null],
{ x: null, y: null },
)
return new Vec2(minSnap[0] ?? 0.0, minSnap[1] ?? 0.0)
return new Vec2(minSnap.x ?? 0.0, minSnap.y ?? 0.0)
}

snap(rect: Rect, threshold: number): [x: number | null, y: number | null] {
snap(rect: Rect, threshold: number): PartialVec2 {
const leftSnap = SnapGrid.boundSnap(rect.left, this.leftAxes.value, threshold)
const rightSnap = SnapGrid.boundSnap(rect.right, this.rightAxes.value, threshold)
const topSnap = SnapGrid.boundSnap(rect.top, this.topAxes.value, threshold)
const bottomSnap = SnapGrid.boundSnap(rect.bottom, this.bottomAxes.value, threshold)
return [SnapGrid.minSnap(leftSnap, rightSnap), SnapGrid.minSnap(topSnap, bottomSnap)]
return { x: SnapGrid.minSnap(leftSnap, rightSnap), y: SnapGrid.minSnap(topSnap, bottomSnap) }
}

private static boundSnap(value: number, axes: number[], threshold: number): number | null {
Expand All @@ -58,11 +70,11 @@ export class SnapGrid {
const notLowerNearest = axes[firstNotLower]
const snapToHigher = notLowerNearest != null ? notLowerNearest - value : null
const snap = SnapGrid.minSnap(snapToLower, snapToHigher)
return snap != null && abs(snap) <= threshold ? snap : null
return snap != null && Math.abs(snap) <= threshold ? snap : null
}

private static minSnap(a: Opt<number>, b: Opt<number>): number | null {
if (a != null && b != null) return abs(a) < abs(b) ? a : b
if (a != null && b != null) return Math.abs(a) < Math.abs(b) ? a : b
else return a ?? b ?? null
}
}
Expand Down Expand Up @@ -135,10 +147,10 @@ export function useDragging() {
const newSnappedOffsetTarget = snappedOffsetTarget.value
// Skip animation if target offset does not change significantly, to avoid shivering
// when node is snapped.
if (abs(newSnappedOffsetTarget.x - oldSnappedOffset.x) < 2.0) {
if (Math.abs(newSnappedOffsetTarget.x - oldSnappedOffset.x) < 2.0) {
snapX.skip()
}
if (abs(newSnappedOffsetTarget.y - oldSnappedOffset.y) < 2.0) {
if (Math.abs(newSnappedOffsetTarget.y - oldSnappedOffset.y) < 2.0) {
snapY.skip()
}
}
Expand All @@ -151,10 +163,10 @@ export function useDragging() {
createSnapGrid() {
const nonDraggedRects = computed(() => {
const nonDraggedNodes = iteratorFilter(
graphStore.nodeRects.entries(),
([id]) => !this.draggedNodes.has(id),
graphStore.currentNodeIds.values(),
(id) => !this.draggedNodes.has(id),
)
return Array.from(nonDraggedNodes, ([, rect]) => rect)
return Array.from(nonDraggedNodes, (id) => graphStore.nodeRects.get(id)!)
})
return new SnapGrid(nonDraggedRects)
}
Expand Down
7 changes: 4 additions & 3 deletions app/gui2/src/stores/graph/graphDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,10 @@ export class GraphDb {
}

const functionAst = functionAst_.astExtended
if (!functionAst) return
if (!functionAst.isTree(RawAst.Tree.Type.Function)) return
this.bindings.readFunctionAst(functionAst)
if (functionAst?.isTree(RawAst.Tree.Type.Function)) {
this.bindings.readFunctionAst(functionAst)
}
return currentNodeIds
}

assignUpdatedMetadata(node: Node, meta: NodeMetadata) {
Expand Down
6 changes: 5 additions & 1 deletion app/gui2/src/stores/graph/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const useGraphStore = defineStore('graph', () => {
const editedNodeInfo = ref<NodeEditInfo>()
const imports = ref<{ import: Import; span: ContentRange }[]>([])
const methodAst = ref<Ast.Function>()
const currentNodeIds = ref(new Set<ExprId>())

const unconnectedEdge = ref<UnconnectedEdge>()

Expand Down Expand Up @@ -109,7 +110,7 @@ export const useGraphStore = defineStore('graph', () => {

methodAst.value = getExecutedMethodAst(newRoot, proj.executionContext.getStackTop(), db)
if (methodAst.value) {
db.readFunctionAst(methodAst.value, (id) => meta.get(id))
currentNodeIds.value = db.readFunctionAst(methodAst.value, (id) => meta.get(id))
}
})
}
Expand Down Expand Up @@ -215,6 +216,8 @@ export const useGraphStore = defineStore('graph', () => {
const node = db.nodeIdToNode.get(id)
if (!node) return
proj.module?.deleteExpression(node.outerExprId)
nodeRects.delete(id)
node.pattern?.visitRecursive((ast) => exprRects.delete(ast.astId))
}

function setNodeContent(id: ExprId, content: string) {
Expand Down Expand Up @@ -331,6 +334,7 @@ export const useGraphStore = defineStore('graph', () => {
editedNodeInfo,
unconnectedEdge,
edges,
currentNodeIds,
nodeRects,
vizRects,
exprRects,
Expand Down
6 changes: 5 additions & 1 deletion app/gui2/src/util/navigator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ export function useNavigator(viewportNode: Ref<Element | undefined>) {
viewportNode.value.clientWidth / rect.width,
),
)
targetCenter.value = new Vec2(rect.left + rect.width / 2, rect.top + rect.height / 2)
const centerX =
!Number.isFinite(rect.left) && !Number.isFinite(rect.width) ? 0 : rect.left + rect.width / 2
const centerY =
!Number.isFinite(rect.top) && !Number.isFinite(rect.height) ? 0 : rect.top + rect.height / 2
targetCenter.value = new Vec2(centerX, centerY)
}

let zoomPivot = Vec2.Zero
Expand Down
Loading