Animate view to multiple nodes (#282)

* animateToNodes
- modify existing animation method to support passing multiple nodes, in which case the view is fit to the bounding box around them

* animateToBounds

* amend

* format

* amend
This commit is contained in:
Zoltán Dócs
2024-11-08 19:40:15 +01:00
committed by GitHub
parent 87c6d59546
commit 6ad864bc20
2 changed files with 34 additions and 19 deletions

View File

@@ -1,4 +1,4 @@
import type { CanvasColour, Dictionary, Direction, IBoundaryNodes, IContextMenuOptions, INodeSlot, INodeInputSlot, INodeOutputSlot, IOptionalSlotData, Point, Rect, Rect32, Size, IContextMenuValue, ISlotType, ConnectingLink, NullableProperties, Positionable, ReadOnlyPoint } from "./interfaces"
import type { CanvasColour, Dictionary, Direction, IBoundaryNodes, IContextMenuOptions, INodeSlot, INodeInputSlot, INodeOutputSlot, IOptionalSlotData, Point, Rect, Rect32, Size, IContextMenuValue, ISlotType, ConnectingLink, NullableProperties, Positionable, ReadOnlyPoint, ReadOnlyRect } from "./interfaces"
import type { IWidget, TWidgetValue } from "./types/widgets"
import { LGraphNode, type NodeId } from "./LGraphNode"
import type { CanvasDragEvent, CanvasMouseEvent, CanvasWheelEvent, CanvasEventDetail, CanvasPointerEvent } from "./types/events"
@@ -7838,17 +7838,20 @@ export class LGraphCanvas {
}
/**
* Centers the camera on a given node (animated version)
* @method animateToNode
**/
animateToNode(
node: LGraphNode,
* Starts an animation to fit the view around the specified selection of nodes.
* @param bounds The bounds to animate the view to, defined by a rectangle.
* @param animationParameters Various parameters for the camera movement animation.
*/
animateToBounds(
bounds: ReadOnlyRect,
{
duration = 350,
zoom = 0.75,
easing = EaseFunction.EASE_IN_OUT_QUAD
}: {
/** Duration of the animation in milliseconds. */
duration?: number,
/** Relative target zoom level. 1 means the view is fit exactly on the bounding box. */
zoom?: number,
easing?: EaseFunction
} = {}
@@ -7871,19 +7874,18 @@ export class LGraphCanvas {
let targetScale = startScale
let targetX = startX
let targetY = startY
if (zoom > 0) {
const targetScaleX = (zoom * cw) / Math.max(node.size[0], 300)
const targetScaleY = (zoom * ch) / Math.max(node.size[1], 300)
const targetScaleX = (zoom * cw) / Math.max(bounds[2], 300)
const targetScaleY = (zoom * ch) / Math.max(bounds[3], 300)
// Choose the smaller scale to ensure the node fits into the viewport
// Ensure we don't go over the max scale
targetScale = Math.min(targetScaleX, targetScaleY, this.ds.max_scale)
targetX = -node.pos[0] - node.size[0] * 0.5 + (cw * 0.5) / targetScale
targetY = -node.pos[1] - node.size[1] * 0.5 + (ch * 0.5) / targetScale
} else {
targetX = -node.pos[0] - node.size[0] * 0.5 + (cw * 0.5) / targetScale
targetY = -node.pos[1] - node.size[1] * 0.5 + (ch * 0.5) / targetScale
}
targetX = -bounds[0] - bounds[2] * 0.5 + (cw * 0.5) / targetScale
targetY = -bounds[1] - bounds[3] * 0.5 + (ch * 0.5) / targetScale
const animate = (timestamp: number) => {
const elapsed = timestamp - startTimestamp
const progress = Math.min(elapsed / duration, 1)

View File

@@ -1,4 +1,4 @@
import type { IContextMenuValue, IPinnable, Point, Positionable, Size } from "./interfaces"
import type { IContextMenuValue, IPinnable, Point, Positionable, ReadOnlyRect, Size } from "./interfaces"
import type { LGraph } from "./LGraph"
import type { ISerialisedGroup } from "./types/serialisation"
import { LiteGraph } from "./litegraph"
@@ -229,6 +229,17 @@ export class LGraphGroup implements Positionable, IPinnable {
* @param padding Value in graph units to add to all sides of the group. Default: 10
*/
resizeTo(objects: Iterable<Positionable>, padding: number = 10): void {
const boundingBox = LGraphGroup.createBounds(objects, padding);
if(boundingBox === null) return
this.pos[0] = boundingBox[0]
this.pos[1] = boundingBox[1] - this.titleHeight
this.size[0] = boundingBox[2]
this.size[1] = boundingBox[3] + this.titleHeight
}
static createBounds(objects: Iterable<Positionable>, padding: number = 10): ReadOnlyRect | null
{
const bounds = new Float32Array([Infinity, Infinity, -Infinity, -Infinity])
for (const obj of objects) {
@@ -238,12 +249,14 @@ export class LGraphGroup implements Positionable, IPinnable {
bounds[2] = Math.max(bounds[2], rect[0] + rect[2])
bounds[3] = Math.max(bounds[3], rect[1] + rect[3])
}
if (!bounds.every(x => isFinite(x))) return
if (!bounds.every(x => isFinite(x))) return null
this.pos[0] = bounds[0] - padding
this.pos[1] = bounds[1] - padding - this.titleHeight
this.size[0] = bounds[2] - bounds[0] + (2 * padding)
this.size[1] = bounds[3] - bounds[1] + (2 * padding) + this.titleHeight
return [
bounds[0] - padding,
bounds[1] - padding,
bounds[2] - bounds[0] + (2 * padding),
bounds[3] - bounds[1] + (2 * padding)
]
}
/**