Snap everything to grid (#315)

* Implement snap to grid

- Moves positioning logic to LGraph
- Simplifies code
- Adds Pointer API to alt-clone node
- Removes always_round_positions, replaced by always snap to grid (default size is 1 when always snapping)

Fix refator error

* Fix group items snapped without group

* Allow snapping of all items

- Add snapToGrid to Positionable
- Impl. on all types
- Deprecated: alignToGrid is now a wrapper

* Fix test import alias, update expectations

* Prevent desync of before / after change events

Adds ability to perform late binding of finally() during drag start.

* nit - Refactor

* Fix unwanted snap on node/group add

* nit - Doc

* Add shift key state tracking for snap to grid

Private impl., no state API as yet.

* Add snap guides rendering

Nodes, reroutes

* Optimisation - reroute rendering

 Fixes exponential redraw

* Add snap guidelines for groups
This commit is contained in:
filtered
2024-11-19 02:12:20 +11:00
committed by GitHub
parent 3e50941ce3
commit c0e8b33716
13 changed files with 291 additions and 83 deletions

View File

@@ -1,4 +1,4 @@
import type { Dictionary, IContextMenuValue, LinkNetwork, ISlotType, MethodNames, Point, LinkSegment } from "./interfaces"
import type { Dictionary, IContextMenuValue, LinkNetwork, ISlotType, MethodNames, Point, LinkSegment, Positionable } from "./interfaces"
import type { ISerialisedGraph, Serialisable, SerialisableGraph, SerialisableReroute } from "./types/serialisation"
import { Reroute, RerouteId } from "./Reroute"
import { LGraphEventMode } from "./types/globalEnums"
@@ -9,6 +9,7 @@ import { type NodeId, LGraphNode } from "./LGraphNode"
import { type LinkId, LLink } from "./LLink"
import { MapProxyHandler } from "./MapProxyHandler"
import { isSortaInsideOctagon } from "./measure"
import { getAllNestedItems } from "./utils/collections"
interface IGraphInput {
name: string
@@ -25,6 +26,26 @@ export interface LGraphState {
type ParamsArray<T extends Record<any, any>, K extends MethodNames<T>> = Parameters<T[K]>[1] extends undefined ? Parameters<T[K]> | Parameters<T[K]>[0] : Parameters<T[K]>
/** Configuration used by {@link LGraph} `config`. */
export interface LGraphConfig {
/** @deprecated Legacy config - unused */
align_to_grid?: any
/**
* When set to a positive number, when nodes are moved their positions will
* be rounded to the nearest multiple of this value. Half up.
* Default: `undefined`
* @todo Not implemented - see {@link LiteGraph.CANVAS_GRID_SIZE}
*/
snapToGrid?: number
/**
* If `true`, items always snap to the grid - modifier keys are ignored.
* When {@link snapToGrid} is falsy, a value of `1` is used.
* Default: `false`
*/
alwaysSnapToGrid?: boolean
links_ontop?: any
}
/**
* LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.
* supported callbacks:
@@ -81,7 +102,7 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
filter?: string
_subgraph_node?: LGraphNode
/** Must contain serialisable values, e.g. primitive types */
config: { align_to_grid?: any; links_ontop?: any }
config: LGraphConfig
vars: Dictionary<unknown>
nodes_executing: boolean[]
nodes_actioning: (string | boolean)[]
@@ -710,6 +731,12 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
if (!node) return
const { state } = this
// Ensure created items are snapped
if (this.config.alwaysSnapToGrid) {
const snapTo = this.getSnapToGridSize()
if (snapTo) node.snapToGrid(snapTo)
}
// LEGACY: This was changed from constructor === LGraphGroup
//groups
if (node instanceof LGraphGroup) {
@@ -961,6 +988,36 @@ export class LGraph implements LinkNetwork, Serialisable<SerialisableGraph> {
}
}
/**
* Snaps the provided items to a grid.
*
* Item positions are reounded to the nearest multiple of {@link LiteGraph.CANVAS_GRID_SIZE}.
*
* When {@link config}.{@link LGraphConfig.alwaysSnapToGrid alwaysSnapToGrid} is enabled
* and the grid size is falsy, a default of 1 is used.
* @param items The items to snap to the grid
* @todo Currently only snaps nodes.
*/
snapToGrid(items: Set<Positionable>): void {
const snapTo = this.getSnapToGridSize()
if (!snapTo) return
getAllNestedItems(items).forEach(item => {
if (!item.pinned) item.snapToGrid(snapTo)
})
}
/**
* Finds the size of the grid that items should be snapped to when moved.
* @returns The size of the grid that items should be snapped to
*/
getSnapToGridSize(): number {
// Default to 1 when always snapping
return this.config.alwaysSnapToGrid
? LiteGraph.CANVAS_GRID_SIZE || 1
: LiteGraph.CANVAS_GRID_SIZE
}
/**
* Checks that the node type matches the node type registered, used when replacing a nodetype by a newer version during execution
* this replaces the ones using the old version with the new version