Skip to content

Commit

Permalink
Extend core snapToGrid to LiteGraph Groups. (#3393)
Browse files Browse the repository at this point in the history
Extends the core Comfy.SnapToGrid behavior for nodes to apply to LiteGraph's LGraphGroup with the same behavior. Also, pulls out redundant rounding code into util function.
  • Loading branch information
rgthree committed May 27, 2024
1 parent 16a493a commit f6a2039
Showing 1 changed file with 89 additions and 7 deletions.
96 changes: 89 additions & 7 deletions web/extensions/core/snapToGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import { app } from "../../scripts/app.js";

// Shift + drag/resize to snap to grid

/** Rounds a Vector2 in-place to the current CANVAS_GRID_SIZE. */
function roundVectorToGrid(vec) {
vec[0] = LiteGraph.CANVAS_GRID_SIZE * Math.round(vec[0] / LiteGraph.CANVAS_GRID_SIZE);
vec[1] = LiteGraph.CANVAS_GRID_SIZE * Math.round(vec[1] / LiteGraph.CANVAS_GRID_SIZE);
return vec;
}

app.registerExtension({
name: "Comfy.SnapToGrid",
init() {
Expand Down Expand Up @@ -43,10 +50,7 @@ app.registerExtension({
const onResize = node.onResize;
node.onResize = function () {
if (app.shiftDown) {
const w = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.size[0] / LiteGraph.CANVAS_GRID_SIZE);
const h = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.size[1] / LiteGraph.CANVAS_GRID_SIZE);
node.size[0] = w;
node.size[1] = h;
roundVectorToGrid(node.size);
}
return onResize?.apply(this, arguments);
};
Expand All @@ -57,9 +61,7 @@ app.registerExtension({
const origDrawNode = LGraphCanvas.prototype.drawNode;
LGraphCanvas.prototype.drawNode = function (node, ctx) {
if (app.shiftDown && this.node_dragged && node.id in this.selected_nodes) {
const x = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.pos[0] / LiteGraph.CANVAS_GRID_SIZE);
const y = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.pos[1] / LiteGraph.CANVAS_GRID_SIZE);

const [x, y] = roundVectorToGrid([...node.pos]);
const shiftX = x - node.pos[0];
let shiftY = y - node.pos[1];

Expand All @@ -85,5 +87,85 @@ app.registerExtension({

return origDrawNode.apply(this, arguments);
};



/**
* The currently moving, selected group only. Set after the `selected_group` has actually started
* moving.
*/
let selectedAndMovingGroup = null;

/**
* Handles moving a group; tracking when a group has been moved (to show the ghost in `drawGroups`
* below) as well as handle the last move call from LiteGraph's `processMouseUp`.
*/
const groupMove = LGraphGroup.prototype.move;
LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes) {
const v = groupMove.apply(this, arguments);
// When we've started moving, set `selectedAndMovingGroup` as LiteGraph sets `selected_group`
// too eagerly and we don't want to behave like we're moving until we get a delta.
if (!selectedAndMovingGroup && app.canvas.selected_group === this && (deltax || deltay)) {
selectedAndMovingGroup = this;
}

// LiteGraph will call group.move both on mouse-move as well as mouse-up though we only want
// to snap on a mouse-up which we can determine by checking if `app.canvas.last_mouse_dragging`
// has been set to `false`. Essentially, this check here is the equivilant to calling an
// `LGraphGroup.prototype.onNodeMoved` if it had existed.
if (app.canvas.last_mouse_dragging === false && app.shiftDown) {
// After moving a group (while app.shiftDown), snap all the child nodes and, finally,
// align the group itself.
this.recomputeInsideNodes();
for (const node of this._nodes) {
node.alignToGrid();
}
LGraphNode.prototype.alignToGrid.apply(this);
}
return v;
};

/**
* Handles drawing a group when, snapping the size when one is actively being resized tracking and/or
* drawing a ghost box when one is actively being moved. This mimics the node snapping behavior for
* both.
*/
const drawGroups = LGraphCanvas.prototype.drawGroups;
LGraphCanvas.prototype.drawGroups = function (canvas, ctx) {
if (this.selected_group && app.shiftDown) {
if (this.selected_group_resizing) {
roundVectorToGrid(this.selected_group.size);
} else if (selectedAndMovingGroup) {
const [x, y] = roundVectorToGrid([...selectedAndMovingGroup.pos]);
const f = ctx.fillStyle;
const s = ctx.strokeStyle;
ctx.fillStyle = "rgba(100, 100, 100, 0.33)";
ctx.strokeStyle = "rgba(100, 100, 100, 0.66)";
ctx.rect(x, y, ...selectedAndMovingGroup.size);
ctx.fill();
ctx.stroke();
ctx.fillStyle = f;
ctx.strokeStyle = s;
}
} else if (!this.selected_group) {
selectedAndMovingGroup = null;
}
return drawGroups.apply(this, arguments);
};


/** Handles adding a group in a snapping-enabled state. */
const onGroupAdd = LGraphCanvas.onGroupAdd;
LGraphCanvas.onGroupAdd = function() {
const v = onGroupAdd.apply(app.canvas, arguments);
if (app.shiftDown) {
const lastGroup = app.graph._groups[app.graph._groups.length - 1];
if (lastGroup) {
roundVectorToGrid(lastGroup.pos);
roundVectorToGrid(lastGroup.size);
}
}
return v;
};
},
});

0 comments on commit f6a2039

Please sign in to comment.