mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-02 04:02:20 +00:00
Compare commits
7 Commits
fix/load-a
...
drjkl/remo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78cbfd2a7a | ||
|
|
9a3545b42b | ||
|
|
493fe667b7 | ||
|
|
7636b9eaa1 | ||
|
|
b661412c5b | ||
|
|
ba4c3525f4 | ||
|
|
58024fb4f4 |
@@ -4,7 +4,7 @@ import type { Ref } from 'vue'
|
|||||||
|
|
||||||
import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems'
|
import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems'
|
||||||
import { useVueFeatureFlags } from '@/composables/useVueFeatureFlags'
|
import { useVueFeatureFlags } from '@/composables/useVueFeatureFlags'
|
||||||
import type { ReadOnlyRect } from '@/lib/litegraph/src/interfaces'
|
import type { Rect } from '@/lib/litegraph/src/interfaces'
|
||||||
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||||
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
|
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
|
||||||
@@ -71,7 +71,7 @@ export function useSelectionToolboxPosition(
|
|||||||
visible.value = true
|
visible.value = true
|
||||||
|
|
||||||
// Get bounds for all selected items
|
// Get bounds for all selected items
|
||||||
const allBounds: ReadOnlyRect[] = []
|
const allBounds: Rect[] = []
|
||||||
for (const item of selectableItems) {
|
for (const item of selectableItems) {
|
||||||
// Skip items without valid IDs
|
// Skip items without valid IDs
|
||||||
if (item.id == null) continue
|
if (item.id == null) continue
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Point, ReadOnlyRect, Rect } from './interfaces'
|
import type { Point, Rect } from './interfaces'
|
||||||
import { EaseFunction, Rectangle } from './litegraph'
|
import { EaseFunction, Rectangle } from './litegraph'
|
||||||
|
|
||||||
export interface DragAndScaleState {
|
export interface DragAndScaleState {
|
||||||
@@ -188,10 +188,7 @@ export class DragAndScale {
|
|||||||
* Fits the view to the specified bounds.
|
* Fits the view to the specified bounds.
|
||||||
* @param bounds The bounds to fit the view to, defined by a rectangle.
|
* @param bounds The bounds to fit the view to, defined by a rectangle.
|
||||||
*/
|
*/
|
||||||
fitToBounds(
|
fitToBounds(bounds: Rect, { zoom = 0.75 }: { zoom?: number } = {}): void {
|
||||||
bounds: ReadOnlyRect,
|
|
||||||
{ zoom = 0.75 }: { zoom?: number } = {}
|
|
||||||
): void {
|
|
||||||
const cw = this.element.width / window.devicePixelRatio
|
const cw = this.element.width / window.devicePixelRatio
|
||||||
const ch = this.element.height / window.devicePixelRatio
|
const ch = this.element.height / window.devicePixelRatio
|
||||||
let targetScale = this.scale
|
let targetScale = this.scale
|
||||||
@@ -223,7 +220,7 @@ export class DragAndScale {
|
|||||||
* @param bounds The bounds to animate the view to, defined by a rectangle.
|
* @param bounds The bounds to animate the view to, defined by a rectangle.
|
||||||
*/
|
*/
|
||||||
animateToBounds(
|
animateToBounds(
|
||||||
bounds: ReadOnlyRect,
|
bounds: Readonly<Rect | Rectangle>,
|
||||||
setDirty: () => void,
|
setDirty: () => void,
|
||||||
{
|
{
|
||||||
duration = 350,
|
duration = 350,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
SUBGRAPH_INPUT_ID,
|
SUBGRAPH_INPUT_ID,
|
||||||
SUBGRAPH_OUTPUT_ID
|
SUBGRAPH_OUTPUT_ID
|
||||||
} from '@/lib/litegraph/src/constants'
|
} from '@/lib/litegraph/src/constants'
|
||||||
|
import { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'
|
||||||
import type { UUID } from '@/lib/litegraph/src/utils/uuid'
|
import type { UUID } from '@/lib/litegraph/src/utils/uuid'
|
||||||
import { createUuidv4, zeroUuid } from '@/lib/litegraph/src/utils/uuid'
|
import { createUuidv4, zeroUuid } from '@/lib/litegraph/src/utils/uuid'
|
||||||
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
||||||
@@ -1707,7 +1708,12 @@ export class LGraph
|
|||||||
...subgraphNode.subgraph.groups
|
...subgraphNode.subgraph.groups
|
||||||
].map((p: { pos: Point; size?: Size }): HasBoundingRect => {
|
].map((p: { pos: Point; size?: Size }): HasBoundingRect => {
|
||||||
return {
|
return {
|
||||||
boundingRect: [p.pos[0], p.pos[1], p.size?.[0] ?? 0, p.size?.[1] ?? 0]
|
boundingRect: new Rectangle(
|
||||||
|
p.pos[0],
|
||||||
|
p.pos[1],
|
||||||
|
p.size?.[0] ?? 0,
|
||||||
|
p.size?.[1] ?? 0
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const bounds = createBounds(positionables) ?? [0, 0, 0, 0]
|
const bounds = createBounds(positionables) ?? [0, 0, 0, 0]
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ import type {
|
|||||||
NullableProperties,
|
NullableProperties,
|
||||||
Point,
|
Point,
|
||||||
Positionable,
|
Positionable,
|
||||||
ReadOnlyPoint,
|
|
||||||
ReadOnlyRect,
|
|
||||||
Rect,
|
Rect,
|
||||||
Size
|
Size
|
||||||
} from './interfaces'
|
} from './interfaces'
|
||||||
@@ -236,11 +234,11 @@ export class LGraphCanvas
|
|||||||
implements CustomEventDispatcher<LGraphCanvasEventMap>
|
implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||||
{
|
{
|
||||||
// Optimised buffers used during rendering
|
// Optimised buffers used during rendering
|
||||||
static #temp = new Float32Array(4)
|
static #temp = [0, 0, 0, 0] satisfies Rect
|
||||||
static #temp_vec2 = new Float32Array(2)
|
static #temp_vec2 = [0, 0] satisfies Point
|
||||||
static #tmp_area = new Float32Array(4)
|
static #tmp_area = [0, 0, 0, 0] satisfies Rect
|
||||||
static #margin_area = new Float32Array(4)
|
static #margin_area = [0, 0, 0, 0] satisfies Rect
|
||||||
static #link_bounding = new Float32Array(4)
|
static #link_bounding = [0, 0, 0, 0] satisfies Rect
|
||||||
|
|
||||||
static DEFAULT_BACKGROUND_IMAGE =
|
static DEFAULT_BACKGROUND_IMAGE =
|
||||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII='
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII='
|
||||||
@@ -628,7 +626,7 @@ export class LGraphCanvas
|
|||||||
dirty_area?: Rect | null
|
dirty_area?: Rect | null
|
||||||
/** @deprecated Unused */
|
/** @deprecated Unused */
|
||||||
node_in_panel?: LGraphNode | null
|
node_in_panel?: LGraphNode | null
|
||||||
last_mouse: ReadOnlyPoint = [0, 0]
|
last_mouse: Point = [0, 0]
|
||||||
last_mouseclick: number = 0
|
last_mouseclick: number = 0
|
||||||
graph: LGraph | Subgraph | null
|
graph: LGraph | Subgraph | null
|
||||||
get _graph(): LGraph | Subgraph {
|
get _graph(): LGraph | Subgraph {
|
||||||
@@ -2634,7 +2632,7 @@ export class LGraphCanvas
|
|||||||
pointer: CanvasPointer,
|
pointer: CanvasPointer,
|
||||||
node?: LGraphNode | undefined
|
node?: LGraphNode | undefined
|
||||||
): void {
|
): void {
|
||||||
const dragRect = new Float32Array(4)
|
const dragRect: [number, number, number, number] = [0, 0, 0, 0]
|
||||||
|
|
||||||
dragRect[0] = e.canvasX
|
dragRect[0] = e.canvasX
|
||||||
dragRect[1] = e.canvasY
|
dragRect[1] = e.canvasY
|
||||||
@@ -3174,7 +3172,7 @@ export class LGraphCanvas
|
|||||||
|
|
||||||
LGraphCanvas.active_canvas = this
|
LGraphCanvas.active_canvas = this
|
||||||
this.adjustMouseEvent(e)
|
this.adjustMouseEvent(e)
|
||||||
const mouse: ReadOnlyPoint = [e.clientX, e.clientY]
|
const mouse: Point = [e.clientX, e.clientY]
|
||||||
this.mouse[0] = mouse[0]
|
this.mouse[0] = mouse[0]
|
||||||
this.mouse[1] = mouse[1]
|
this.mouse[1] = mouse[1]
|
||||||
const delta = [mouse[0] - this.last_mouse[0], mouse[1] - this.last_mouse[1]]
|
const delta = [mouse[0] - this.last_mouse[0], mouse[1] - this.last_mouse[1]]
|
||||||
@@ -4077,7 +4075,10 @@ export class LGraphCanvas
|
|||||||
this.setDirty(true)
|
this.setDirty(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#handleMultiSelect(e: CanvasPointerEvent, dragRect: Float32Array) {
|
#handleMultiSelect(
|
||||||
|
e: CanvasPointerEvent,
|
||||||
|
dragRect: [number, number, number, number]
|
||||||
|
) {
|
||||||
// Process drag
|
// Process drag
|
||||||
// Convert Point pair (pos, offset) to Rect
|
// Convert Point pair (pos, offset) to Rect
|
||||||
const { graph, selectedItems, subgraph } = this
|
const { graph, selectedItems, subgraph } = this
|
||||||
@@ -4732,32 +4733,47 @@ export class LGraphCanvas
|
|||||||
for (const renderLink of renderLinks) {
|
for (const renderLink of renderLinks) {
|
||||||
const {
|
const {
|
||||||
fromSlot,
|
fromSlot,
|
||||||
fromPos: pos,
|
fromPos: pos
|
||||||
fromDirection,
|
// fromDirection,
|
||||||
dragDirection
|
// dragDirection
|
||||||
} = renderLink
|
} = renderLink
|
||||||
const connShape = fromSlot.shape
|
const connShape = fromSlot.shape
|
||||||
const connType = fromSlot.type
|
const connType = fromSlot.type
|
||||||
|
|
||||||
const colour = resolveConnectingLinkColor(connType)
|
const color = resolveConnectingLinkColor(connType)
|
||||||
|
|
||||||
// the connection being dragged by the mouse
|
// the connection being dragged by the mouse
|
||||||
if (this.linkRenderer) {
|
if (
|
||||||
this.linkRenderer.renderDraggingLink(
|
this.linkRenderer &&
|
||||||
ctx,
|
renderLink.fromSlotIndex !== undefined &&
|
||||||
pos,
|
renderLink.node !== undefined
|
||||||
highlightPos,
|
) {
|
||||||
colour,
|
const { fromSlotIndex, node } = renderLink
|
||||||
fromDirection,
|
if (
|
||||||
dragDirection,
|
node instanceof LGraphNode &&
|
||||||
{
|
('link' in fromSlot || 'links' in fromSlot)
|
||||||
...this.buildLinkRenderContext(),
|
) {
|
||||||
linkMarkerShape: LinkMarkerShape.None
|
this.linkRenderer.renderDraggingLink(
|
||||||
}
|
ctx,
|
||||||
)
|
node,
|
||||||
|
fromSlot,
|
||||||
|
fromSlotIndex,
|
||||||
|
highlightPos,
|
||||||
|
this.buildLinkRenderContext(),
|
||||||
|
{ fromInput: 'link' in fromSlot, color }
|
||||||
|
// pos,
|
||||||
|
// colour,
|
||||||
|
// fromDirection,
|
||||||
|
// dragDirection,
|
||||||
|
// {
|
||||||
|
// ...this.buildLinkRenderContext(),
|
||||||
|
// linkMarkerShape: LinkMarkerShape.None
|
||||||
|
// }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.fillStyle = colour
|
ctx.fillStyle = color
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
if (connType === LiteGraph.EVENT || connShape === RenderShape.BOX) {
|
if (connType === LiteGraph.EVENT || connShape === RenderShape.BOX) {
|
||||||
ctx.rect(pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, 10)
|
ctx.rect(pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, 10)
|
||||||
@@ -4848,7 +4864,7 @@ export class LGraphCanvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Get the target snap / highlight point in graph space */
|
/** Get the target snap / highlight point in graph space */
|
||||||
#getHighlightPosition(): ReadOnlyPoint {
|
#getHighlightPosition(): Point {
|
||||||
return LiteGraph.snaps_for_comfy
|
return LiteGraph.snaps_for_comfy
|
||||||
? this.linkConnector.state.snapLinksPos ??
|
? this.linkConnector.state.snapLinksPos ??
|
||||||
this._highlight_pos ??
|
this._highlight_pos ??
|
||||||
@@ -4863,7 +4879,7 @@ export class LGraphCanvas
|
|||||||
*/
|
*/
|
||||||
#renderSnapHighlight(
|
#renderSnapHighlight(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
highlightPos: ReadOnlyPoint
|
highlightPos: Point
|
||||||
): void {
|
): void {
|
||||||
const linkConnectorSnap = !!this.linkConnector.state.snapLinksPos
|
const linkConnectorSnap = !!this.linkConnector.state.snapLinksPos
|
||||||
if (!this._highlight_pos && !linkConnectorSnap) return
|
if (!this._highlight_pos && !linkConnectorSnap) return
|
||||||
@@ -5205,7 +5221,8 @@ export class LGraphCanvas
|
|||||||
// clip if required (mask)
|
// clip if required (mask)
|
||||||
const shape = node._shape || RenderShape.BOX
|
const shape = node._shape || RenderShape.BOX
|
||||||
const size = LGraphCanvas.#temp_vec2
|
const size = LGraphCanvas.#temp_vec2
|
||||||
size.set(node.renderingSize)
|
size[0] = node.renderingSize[0]
|
||||||
|
size[1] = node.renderingSize[1]
|
||||||
|
|
||||||
if (node.collapsed) {
|
if (node.collapsed) {
|
||||||
ctx.font = this.inner_text_font
|
ctx.font = this.inner_text_font
|
||||||
@@ -5400,7 +5417,10 @@ export class LGraphCanvas
|
|||||||
|
|
||||||
// Normalised node dimensions
|
// Normalised node dimensions
|
||||||
const area = LGraphCanvas.#tmp_area
|
const area = LGraphCanvas.#tmp_area
|
||||||
area.set(node.boundingRect)
|
area[0] = node.boundingRect[0]
|
||||||
|
area[1] = node.boundingRect[1]
|
||||||
|
area[2] = node.boundingRect[2]
|
||||||
|
area[3] = node.boundingRect[3]
|
||||||
area[0] -= node.pos[0]
|
area[0] -= node.pos[0]
|
||||||
area[1] -= node.pos[1]
|
area[1] -= node.pos[1]
|
||||||
|
|
||||||
@@ -5502,7 +5522,10 @@ export class LGraphCanvas
|
|||||||
shape = RenderShape.ROUND
|
shape = RenderShape.ROUND
|
||||||
) {
|
) {
|
||||||
const snapGuide = LGraphCanvas.#temp
|
const snapGuide = LGraphCanvas.#temp
|
||||||
snapGuide.set(item.boundingRect)
|
snapGuide[0] = item.boundingRect[0]
|
||||||
|
snapGuide[1] = item.boundingRect[1]
|
||||||
|
snapGuide[2] = item.boundingRect[2]
|
||||||
|
snapGuide[3] = item.boundingRect[3]
|
||||||
|
|
||||||
// Not all items have pos equal to top-left of bounds
|
// Not all items have pos equal to top-left of bounds
|
||||||
const { pos } = item
|
const { pos } = item
|
||||||
@@ -5942,8 +5965,8 @@ export class LGraphCanvas
|
|||||||
*/
|
*/
|
||||||
renderLink(
|
renderLink(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
a: ReadOnlyPoint,
|
a: Point,
|
||||||
b: ReadOnlyPoint,
|
b: Point,
|
||||||
link: LLink | null,
|
link: LLink | null,
|
||||||
skip_border: boolean,
|
skip_border: boolean,
|
||||||
flow: number | null,
|
flow: number | null,
|
||||||
@@ -5960,9 +5983,9 @@ export class LGraphCanvas
|
|||||||
/** When defined, render data will be saved to this reroute instead of the {@link link}. */
|
/** When defined, render data will be saved to this reroute instead of the {@link link}. */
|
||||||
reroute?: Reroute
|
reroute?: Reroute
|
||||||
/** Offset of the bezier curve control point from {@link a point a} (output side) */
|
/** Offset of the bezier curve control point from {@link a point a} (output side) */
|
||||||
startControl?: ReadOnlyPoint
|
startControl?: Point
|
||||||
/** Offset of the bezier curve control point from {@link b point b} (input side) */
|
/** Offset of the bezier curve control point from {@link b point b} (input side) */
|
||||||
endControl?: ReadOnlyPoint
|
endControl?: Point
|
||||||
/** Number of sublines (useful to represent vec3 or rgb) @todo If implemented, refactor calculations out of the loop */
|
/** Number of sublines (useful to represent vec3 or rgb) @todo If implemented, refactor calculations out of the loop */
|
||||||
num_sublines?: number
|
num_sublines?: number
|
||||||
/** Whether this is a floating link segment */
|
/** Whether this is a floating link segment */
|
||||||
@@ -8433,7 +8456,7 @@ export class LGraphCanvas
|
|||||||
* Starts an animation to fit the view around the specified selection of nodes.
|
* 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 bounds The bounds to animate the view to, defined by a rectangle.
|
||||||
*/
|
*/
|
||||||
animateToBounds(bounds: ReadOnlyRect, options: AnimationOptions = {}) {
|
animateToBounds(bounds: Rect | Rectangle, options: AnimationOptions = {}) {
|
||||||
const setDirty = () => this.setDirty(true, true)
|
const setDirty = () => this.setDirty(true, true)
|
||||||
this.ds.animateToBounds(bounds, setDirty, options)
|
this.ds.animateToBounds(bounds, setDirty, options)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { NullGraphError } from '@/lib/litegraph/src/infrastructure/NullGraphError'
|
import { NullGraphError } from '@/lib/litegraph/src/infrastructure/NullGraphError'
|
||||||
|
import { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'
|
||||||
|
|
||||||
import type { LGraph } from './LGraph'
|
import type { LGraph } from './LGraph'
|
||||||
import { LGraphCanvas } from './LGraphCanvas'
|
import { LGraphCanvas } from './LGraphCanvas'
|
||||||
@@ -40,15 +41,15 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
|||||||
title: string
|
title: string
|
||||||
font?: string
|
font?: string
|
||||||
font_size: number = LiteGraph.DEFAULT_GROUP_FONT || 24
|
font_size: number = LiteGraph.DEFAULT_GROUP_FONT || 24
|
||||||
_bounding: Float32Array = new Float32Array([
|
_bounding: [number, number, number, number] = [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
LGraphGroup.minWidth,
|
LGraphGroup.minWidth,
|
||||||
LGraphGroup.minHeight
|
LGraphGroup.minHeight
|
||||||
])
|
]
|
||||||
|
|
||||||
_pos: Point = this._bounding.subarray(0, 2)
|
_pos: Point = [10, 10]
|
||||||
_size: Size = this._bounding.subarray(2, 4)
|
_size: Size = [LGraphGroup.minWidth, LGraphGroup.minHeight]
|
||||||
/** @deprecated See {@link _children} */
|
/** @deprecated See {@link _children} */
|
||||||
_nodes: LGraphNode[] = []
|
_nodes: LGraphNode[] = []
|
||||||
_children: Set<Positionable> = new Set()
|
_children: Set<Positionable> = new Set()
|
||||||
@@ -107,8 +108,13 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
|||||||
this._size[1] = Math.max(LGraphGroup.minHeight, v[1])
|
this._size[1] = Math.max(LGraphGroup.minHeight, v[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
get boundingRect() {
|
get boundingRect(): Rectangle {
|
||||||
return this._bounding
|
return Rectangle.from([
|
||||||
|
this._pos[0],
|
||||||
|
this._pos[1],
|
||||||
|
this._size[0],
|
||||||
|
this._size[1]
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
get nodes() {
|
get nodes() {
|
||||||
@@ -145,14 +151,17 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
|||||||
configure(o: ISerialisedGroup): void {
|
configure(o: ISerialisedGroup): void {
|
||||||
this.id = o.id
|
this.id = o.id
|
||||||
this.title = o.title
|
this.title = o.title
|
||||||
this._bounding.set(o.bounding)
|
this._pos[0] = o.bounding[0]
|
||||||
|
this._pos[1] = o.bounding[1]
|
||||||
|
this._size[0] = o.bounding[2]
|
||||||
|
this._size[1] = o.bounding[3]
|
||||||
this.color = o.color
|
this.color = o.color
|
||||||
this.flags = o.flags || this.flags
|
this.flags = o.flags || this.flags
|
||||||
if (o.font_size) this.font_size = o.font_size
|
if (o.font_size) this.font_size = o.font_size
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): ISerialisedGroup {
|
serialize(): ISerialisedGroup {
|
||||||
const b = this._bounding
|
const b = [this._pos[0], this._pos[1], this._size[0], this._size[1]]
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
title: this.title,
|
title: this.title,
|
||||||
@@ -210,7 +219,7 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (LiteGraph.highlight_selected_group && this.selected) {
|
if (LiteGraph.highlight_selected_group && this.selected) {
|
||||||
strokeShape(ctx, this._bounding, {
|
strokeShape(ctx, this.boundingRect, {
|
||||||
title_height: this.titleHeight,
|
title_height: this.titleHeight,
|
||||||
padding
|
padding
|
||||||
})
|
})
|
||||||
@@ -251,7 +260,7 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
|||||||
|
|
||||||
// Move nodes we overlap the centre point of
|
// Move nodes we overlap the centre point of
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
if (containsCentre(this._bounding, node.boundingRect)) {
|
if (containsCentre(this.boundingRect, node.boundingRect)) {
|
||||||
this._nodes.push(node)
|
this._nodes.push(node)
|
||||||
children.add(node)
|
children.add(node)
|
||||||
}
|
}
|
||||||
@@ -259,12 +268,13 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
|||||||
|
|
||||||
// Move reroutes we overlap the centre point of
|
// Move reroutes we overlap the centre point of
|
||||||
for (const reroute of reroutes.values()) {
|
for (const reroute of reroutes.values()) {
|
||||||
if (isPointInRect(reroute.pos, this._bounding)) children.add(reroute)
|
if (isPointInRect(reroute.pos, this.boundingRect)) children.add(reroute)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move groups we wholly contain
|
// Move groups we wholly contain
|
||||||
for (const group of groups) {
|
for (const group of groups) {
|
||||||
if (containsRect(this._bounding, group._bounding)) children.add(group)
|
if (containsRect(this.boundingRect, group.boundingRect))
|
||||||
|
children.add(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
groups.sort((a, b) => {
|
groups.sort((a, b) => {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import type { Reroute, RerouteId } from './Reroute'
|
|||||||
import { getNodeInputOnPos, getNodeOutputOnPos } from './canvas/measureSlots'
|
import { getNodeInputOnPos, getNodeOutputOnPos } from './canvas/measureSlots'
|
||||||
import type { IDrawBoundingOptions } from './draw'
|
import type { IDrawBoundingOptions } from './draw'
|
||||||
import { NullGraphError } from './infrastructure/NullGraphError'
|
import { NullGraphError } from './infrastructure/NullGraphError'
|
||||||
import type { ReadOnlyRectangle } from './infrastructure/Rectangle'
|
|
||||||
import { Rectangle } from './infrastructure/Rectangle'
|
import { Rectangle } from './infrastructure/Rectangle'
|
||||||
import type {
|
import type {
|
||||||
ColorOption,
|
ColorOption,
|
||||||
@@ -37,8 +36,6 @@ import type {
|
|||||||
ISlotType,
|
ISlotType,
|
||||||
Point,
|
Point,
|
||||||
Positionable,
|
Positionable,
|
||||||
ReadOnlyPoint,
|
|
||||||
ReadOnlyRect,
|
|
||||||
Rect,
|
Rect,
|
||||||
Size
|
Size
|
||||||
} from './interfaces'
|
} from './interfaces'
|
||||||
@@ -387,7 +384,7 @@ export class LGraphNode
|
|||||||
* Called once at the start of every frame. Caller may change the values in {@link out}, which will be reflected in {@link boundingRect}.
|
* Called once at the start of every frame. Caller may change the values in {@link out}, which will be reflected in {@link boundingRect}.
|
||||||
* WARNING: Making changes to boundingRect via onBounding is poorly supported, and will likely result in strange behaviour.
|
* WARNING: Making changes to boundingRect via onBounding is poorly supported, and will likely result in strange behaviour.
|
||||||
*/
|
*/
|
||||||
onBounding?(this: LGraphNode, out: Rect): void
|
onBounding?(this: LGraphNode, out: Rectangle): void
|
||||||
console?: string[]
|
console?: string[]
|
||||||
_level?: number
|
_level?: number
|
||||||
_shape?: RenderShape
|
_shape?: RenderShape
|
||||||
@@ -413,12 +410,12 @@ export class LGraphNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc {@link renderArea} */
|
/** @inheritdoc {@link renderArea} */
|
||||||
#renderArea: Float32Array = new Float32Array(4)
|
#renderArea: [number, number, number, number] = [0, 0, 0, 0]
|
||||||
/**
|
/**
|
||||||
* Rect describing the node area, including shadows and any protrusions.
|
* Rect describing the node area, including shadows and any protrusions.
|
||||||
* Determines if the node is visible. Calculated once at the start of every frame.
|
* Determines if the node is visible. Calculated once at the start of every frame.
|
||||||
*/
|
*/
|
||||||
get renderArea(): ReadOnlyRect {
|
get renderArea(): Rect {
|
||||||
return this.#renderArea
|
return this.#renderArea
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,12 +426,12 @@ export class LGraphNode
|
|||||||
*
|
*
|
||||||
* Determines the node hitbox and other rendering effects. Calculated once at the start of every frame.
|
* Determines the node hitbox and other rendering effects. Calculated once at the start of every frame.
|
||||||
*/
|
*/
|
||||||
get boundingRect(): ReadOnlyRectangle {
|
get boundingRect(): Rectangle {
|
||||||
return this.#boundingRect
|
return this.#boundingRect
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The offset from {@link pos} to the top-left of {@link boundingRect}. */
|
/** The offset from {@link pos} to the top-left of {@link boundingRect}. */
|
||||||
get boundingOffset(): ReadOnlyPoint {
|
get boundingOffset(): Point {
|
||||||
const {
|
const {
|
||||||
pos: [posX, posY],
|
pos: [posX, posY],
|
||||||
boundingRect: [bX, bY]
|
boundingRect: [bX, bY]
|
||||||
@@ -443,9 +440,9 @@ export class LGraphNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** {@link pos} and {@link size} values are backed by this {@link Rect}. */
|
/** {@link pos} and {@link size} values are backed by this {@link Rect}. */
|
||||||
_posSize: Float32Array = new Float32Array(4)
|
_posSize: [number, number, number, number] = [0, 0, 0, 0]
|
||||||
_pos: Point = this._posSize.subarray(0, 2)
|
_pos: Point = [0, 0]
|
||||||
_size: Size = this._posSize.subarray(2, 4)
|
_size: Size = [0, 0]
|
||||||
|
|
||||||
public get pos() {
|
public get pos() {
|
||||||
return this._pos
|
return this._pos
|
||||||
@@ -1653,7 +1650,7 @@ export class LGraphNode
|
|||||||
inputs ? inputs.filter((input) => !isWidgetInputSlot(input)).length : 1,
|
inputs ? inputs.filter((input) => !isWidgetInputSlot(input)).length : 1,
|
||||||
outputs ? outputs.length : 1
|
outputs ? outputs.length : 1
|
||||||
)
|
)
|
||||||
const size = out || new Float32Array([0, 0])
|
const size = out || [0, 0]
|
||||||
rows = Math.max(rows, 1)
|
rows = Math.max(rows, 1)
|
||||||
// although it should be graphcanvas.inner_text_font size
|
// although it should be graphcanvas.inner_text_font size
|
||||||
const font_size = LiteGraph.NODE_TEXT_SIZE
|
const font_size = LiteGraph.NODE_TEXT_SIZE
|
||||||
@@ -1978,7 +1975,7 @@ export class LGraphNode
|
|||||||
* @param out `x, y, width, height` are written to this array.
|
* @param out `x, y, width, height` are written to this array.
|
||||||
* @param ctx The canvas context to use for measuring text.
|
* @param ctx The canvas context to use for measuring text.
|
||||||
*/
|
*/
|
||||||
measure(out: Rect, ctx: CanvasRenderingContext2D): void {
|
measure(out: Rectangle, ctx: CanvasRenderingContext2D): void {
|
||||||
const titleMode = this.title_mode
|
const titleMode = this.title_mode
|
||||||
const renderTitle =
|
const renderTitle =
|
||||||
titleMode != TitleMode.TRANSPARENT_TITLE &&
|
titleMode != TitleMode.TRANSPARENT_TITLE &&
|
||||||
@@ -2004,13 +2001,13 @@ export class LGraphNode
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the bounding of the object, used for rendering purposes
|
* returns the bounding of the object, used for rendering purposes
|
||||||
* @param out {Float32Array[4]?} [optional] a place to store the output, to free garbage
|
* @param out {Rect?} [optional] a place to store the output, to free garbage
|
||||||
* @param includeExternal {boolean?} [optional] set to true to
|
* @param includeExternal {boolean?} [optional] set to true to
|
||||||
* include the shadow and connection points in the bounding calculation
|
* include the shadow and connection points in the bounding calculation
|
||||||
* @returns the bounding box in format of [topleft_cornerx, topleft_cornery, width, height]
|
* @returns the bounding box in format of [topleft_cornerx, topleft_cornery, width, height]
|
||||||
*/
|
*/
|
||||||
getBounding(out?: Rect, includeExternal?: boolean): Rect {
|
getBounding(out?: Rect, includeExternal?: boolean): Rect {
|
||||||
out ||= new Float32Array(4)
|
out ||= [0, 0, 0, 0]
|
||||||
|
|
||||||
const rect = includeExternal ? this.renderArea : this.boundingRect
|
const rect = includeExternal ? this.renderArea : this.boundingRect
|
||||||
out[0] = rect[0]
|
out[0] = rect[0]
|
||||||
@@ -2031,7 +2028,10 @@ export class LGraphNode
|
|||||||
this.onBounding?.(bounds)
|
this.onBounding?.(bounds)
|
||||||
|
|
||||||
const renderArea = this.#renderArea
|
const renderArea = this.#renderArea
|
||||||
renderArea.set(bounds)
|
renderArea[0] = bounds[0]
|
||||||
|
renderArea[1] = bounds[1]
|
||||||
|
renderArea[2] = bounds[2]
|
||||||
|
renderArea[3] = bounds[3]
|
||||||
// 4 offset for collapsed node connection points
|
// 4 offset for collapsed node connection points
|
||||||
renderArea[0] -= 4
|
renderArea[0] -= 4
|
||||||
renderArea[1] -= 4
|
renderArea[1] -= 4
|
||||||
@@ -3174,7 +3174,7 @@ export class LGraphNode
|
|||||||
* @returns the position
|
* @returns the position
|
||||||
*/
|
*/
|
||||||
getConnectionPos(is_input: boolean, slot_number: number, out?: Point): Point {
|
getConnectionPos(is_input: boolean, slot_number: number, out?: Point): Point {
|
||||||
out ||= new Float32Array(2)
|
out ||= [0, 0]
|
||||||
|
|
||||||
const {
|
const {
|
||||||
pos: [nodeX, nodeY],
|
pos: [nodeX, nodeY],
|
||||||
@@ -3839,7 +3839,7 @@ export class LGraphNode
|
|||||||
slot.boundingRect[3] = LiteGraph.NODE_SLOT_HEIGHT
|
slot.boundingRect[3] = LiteGraph.NODE_SLOT_HEIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
#measureSlots(): ReadOnlyRect | null {
|
#measureSlots(): Rect | null {
|
||||||
const slots: (NodeInputSlot | NodeOutputSlot)[] = []
|
const slots: (NodeInputSlot | NodeOutputSlot)[] = []
|
||||||
|
|
||||||
for (const [slotIndex, slot] of this.#concreteInputs.entries()) {
|
for (const [slotIndex, slot] of this.#concreteInputs.entries()) {
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export class LLink implements LinkSegment, Serialisable<SerialisableLLink> {
|
|||||||
data?: number | string | boolean | { toToolTip?(): string }
|
data?: number | string | boolean | { toToolTip?(): string }
|
||||||
_data?: unknown
|
_data?: unknown
|
||||||
/** Centre point of the link, calculated during render only - can be inaccurate */
|
/** Centre point of the link, calculated during render only - can be inaccurate */
|
||||||
_pos: Float32Array
|
_pos: [number, number]
|
||||||
/** @todo Clean up - never implemented in comfy. */
|
/** @todo Clean up - never implemented in comfy. */
|
||||||
_last_time?: number
|
_last_time?: number
|
||||||
/** The last canvas 2D path that was used to render this link */
|
/** The last canvas 2D path that was used to render this link */
|
||||||
@@ -171,7 +171,7 @@ export class LLink implements LinkSegment, Serialisable<SerialisableLLink> {
|
|||||||
|
|
||||||
this._data = null
|
this._data = null
|
||||||
// center
|
// center
|
||||||
this._pos = new Float32Array(2)
|
this._pos = [0, 0]
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated Use {@link LLink.create} */
|
/** @deprecated Use {@link LLink.create} */
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'
|
||||||
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
||||||
import { LayoutSource } from '@/renderer/core/layout/types'
|
import { LayoutSource } from '@/renderer/core/layout/types'
|
||||||
|
|
||||||
@@ -12,8 +13,8 @@ import type {
|
|||||||
LinkSegment,
|
LinkSegment,
|
||||||
Point,
|
Point,
|
||||||
Positionable,
|
Positionable,
|
||||||
ReadOnlyRect,
|
ReadonlyLinkNetwork,
|
||||||
ReadonlyLinkNetwork
|
Rect
|
||||||
} from './interfaces'
|
} from './interfaces'
|
||||||
import { distance, isPointInRect } from './measure'
|
import { distance, isPointInRect } from './measure'
|
||||||
import type { Serialisable, SerialisableReroute } from './types/serialisation'
|
import type { Serialisable, SerialisableReroute } from './types/serialisation'
|
||||||
@@ -49,8 +50,6 @@ export class Reroute
|
|||||||
return Reroute.radius + gap + Reroute.slotRadius
|
return Reroute.radius + gap + Reroute.slotRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
#malloc = new Float32Array(8)
|
|
||||||
|
|
||||||
/** The network this reroute belongs to. Contains all valid links and reroutes. */
|
/** The network this reroute belongs to. Contains all valid links and reroutes. */
|
||||||
#network: WeakRef<LinkNetwork>
|
#network: WeakRef<LinkNetwork>
|
||||||
|
|
||||||
@@ -73,7 +72,7 @@ export class Reroute
|
|||||||
/** This property is only defined on the last reroute of a floating reroute chain (closest to input end). */
|
/** This property is only defined on the last reroute of a floating reroute chain (closest to input end). */
|
||||||
floating?: FloatingRerouteSlot
|
floating?: FloatingRerouteSlot
|
||||||
|
|
||||||
#pos = this.#malloc.subarray(0, 2)
|
#pos: [number, number] = [0, 0]
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
get pos(): Point {
|
get pos(): Point {
|
||||||
return this.#pos
|
return this.#pos
|
||||||
@@ -89,17 +88,17 @@ export class Reroute
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
get boundingRect(): ReadOnlyRect {
|
get boundingRect(): Rectangle {
|
||||||
const { radius } = Reroute
|
const { radius } = Reroute
|
||||||
const [x, y] = this.#pos
|
const [x, y] = this.#pos
|
||||||
return [x - radius, y - radius, 2 * radius, 2 * radius]
|
return Rectangle.from([x - radius, y - radius, 2 * radius, 2 * radius])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slightly over-sized rectangle, guaranteed to contain the entire surface area for hover detection.
|
* Slightly over-sized rectangle, guaranteed to contain the entire surface area for hover detection.
|
||||||
* Eliminates most hover positions using an extremely cheap check.
|
* Eliminates most hover positions using an extremely cheap check.
|
||||||
*/
|
*/
|
||||||
get #hoverArea(): ReadOnlyRect {
|
get #hoverArea(): Rect {
|
||||||
const xOffset = 2 * Reroute.slotOffset
|
const xOffset = 2 * Reroute.slotOffset
|
||||||
const yOffset = 2 * Math.max(Reroute.radius, Reroute.slotRadius)
|
const yOffset = 2 * Math.max(Reroute.radius, Reroute.slotRadius)
|
||||||
|
|
||||||
@@ -126,14 +125,14 @@ export class Reroute
|
|||||||
sin: number = 0
|
sin: number = 0
|
||||||
|
|
||||||
/** Bezier curve control point for the "target" (input) side of the link */
|
/** Bezier curve control point for the "target" (input) side of the link */
|
||||||
controlPoint: Point = this.#malloc.subarray(4, 6)
|
controlPoint: [number, number] = [0, 0]
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
path?: Path2D
|
path?: Path2D
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
_centreAngle?: number
|
_centreAngle?: number
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
_pos: Float32Array = this.#malloc.subarray(6, 8)
|
_pos: [number, number] = [0, 0]
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
_dragging?: boolean
|
_dragging?: boolean
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ interface IDrawTextInAreaOptions {
|
|||||||
*/
|
*/
|
||||||
export function strokeShape(
|
export function strokeShape(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
area: Rect,
|
area: Rect | Rectangle,
|
||||||
{
|
{
|
||||||
shape = RenderShape.BOX,
|
shape = RenderShape.BOX,
|
||||||
round_radius,
|
round_radius,
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import { clamp } from 'es-toolkit/compat'
|
import { clamp } from 'es-toolkit/compat'
|
||||||
|
|
||||||
import type {
|
import type { Rect, Size } from '@/lib/litegraph/src/interfaces'
|
||||||
ReadOnlyRect,
|
|
||||||
ReadOnlySize,
|
|
||||||
Size
|
|
||||||
} from '@/lib/litegraph/src/interfaces'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic width and height, with min/max constraints.
|
* Basic width and height, with min/max constraints.
|
||||||
@@ -55,15 +51,15 @@ export class ConstrainedSize {
|
|||||||
this.desiredHeight = height
|
this.desiredHeight = height
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromSize(size: ReadOnlySize): ConstrainedSize {
|
static fromSize(size: Size): ConstrainedSize {
|
||||||
return new ConstrainedSize(size[0], size[1])
|
return new ConstrainedSize(size[0], size[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromRect(rect: ReadOnlyRect): ConstrainedSize {
|
static fromRect(rect: Rect): ConstrainedSize {
|
||||||
return new ConstrainedSize(rect[2], rect[3])
|
return new ConstrainedSize(rect[2], rect[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
setSize(size: ReadOnlySize): void {
|
setSize(size: Size): void {
|
||||||
this.desiredWidth = size[0]
|
this.desiredWidth = size[0]
|
||||||
this.desiredHeight = size[1]
|
this.desiredHeight = size[1]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { LGraph } from '@/lib/litegraph/src/LGraph'
|
import type { LGraph } from '@/lib/litegraph/src/LGraph'
|
||||||
import type { LLink, ResolvedConnection } from '@/lib/litegraph/src/LLink'
|
import type { LLink, ResolvedConnection } from '@/lib/litegraph/src/LLink'
|
||||||
import type { ReadOnlyRect } from '@/lib/litegraph/src/interfaces'
|
import type { Rect } from '@/lib/litegraph/src/interfaces'
|
||||||
import type { Subgraph } from '@/lib/litegraph/src/subgraph/Subgraph'
|
import type { Subgraph } from '@/lib/litegraph/src/subgraph/Subgraph'
|
||||||
import type {
|
import type {
|
||||||
ExportedSubgraph,
|
ExportedSubgraph,
|
||||||
@@ -29,7 +29,7 @@ export interface LGraphEventMap {
|
|||||||
/** The type of subgraph to create. */
|
/** The type of subgraph to create. */
|
||||||
subgraph: Subgraph
|
subgraph: Subgraph
|
||||||
/** The boundary around every item that was moved into the subgraph. */
|
/** The boundary around every item that was moved into the subgraph. */
|
||||||
bounds: ReadOnlyRect
|
bounds: Rect
|
||||||
/** The raw data that was used to create the subgraph. */
|
/** The raw data that was used to create the subgraph. */
|
||||||
exportedSubgraph: ExportedSubgraph
|
exportedSubgraph: ExportedSubgraph
|
||||||
/** The links that were used to create the subgraph. */
|
/** The links that were used to create the subgraph. */
|
||||||
|
|||||||
@@ -1,47 +1,50 @@
|
|||||||
import type {
|
import type {
|
||||||
CompassCorners,
|
CompassCorners,
|
||||||
Point,
|
Point,
|
||||||
ReadOnlyPoint,
|
Rect,
|
||||||
ReadOnlyRect,
|
|
||||||
ReadOnlySize,
|
|
||||||
ReadOnlyTypedArray,
|
|
||||||
Size
|
Size
|
||||||
} from '@/lib/litegraph/src/interfaces'
|
} from '@/lib/litegraph/src/interfaces'
|
||||||
import { isInRectangle } from '@/lib/litegraph/src/measure'
|
import { isInRectangle } from '@/lib/litegraph/src/measure'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A rectangle, represented as a float64 array of 4 numbers: [x, y, width, height].
|
* A rectangle, represented as an array of 4 numbers: [x, y, width, height].
|
||||||
*
|
*
|
||||||
* This class is a subclass of Float64Array, and so has all the methods of that class. Notably,
|
* This class extends Array and provides both array access (rect[0], rect[1], etc.)
|
||||||
* {@link Rectangle.from} can be used to convert a {@link ReadOnlyRect}. Typing of this however,
|
* and convenient property access (rect.x, rect.y, rect.width, rect.height).
|
||||||
* is broken due to the base TS lib returning Float64Array rather than `this`.
|
|
||||||
*
|
|
||||||
* Sub-array properties ({@link Float64Array.subarray}):
|
|
||||||
* - {@link pos}: The position of the top-left corner of the rectangle.
|
|
||||||
* - {@link size}: The size of the rectangle.
|
|
||||||
*/
|
*/
|
||||||
export class Rectangle extends Float64Array {
|
export class Rectangle extends Array<number> {
|
||||||
#pos: Point | undefined
|
|
||||||
#size: Size | undefined
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
x: number = 0,
|
x: number = 0,
|
||||||
y: number = 0,
|
y: number = 0,
|
||||||
width: number = 0,
|
width: number = 0,
|
||||||
height: number = 0
|
height: number = 0
|
||||||
) {
|
) {
|
||||||
super(4)
|
super()
|
||||||
|
|
||||||
this[0] = x
|
this[0] = x
|
||||||
this[1] = y
|
this[1] = y
|
||||||
this[2] = width
|
this[2] = width
|
||||||
this[3] = height
|
this[3] = height
|
||||||
|
this.length = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
static override from([x, y, width, height]: ReadOnlyRect): Rectangle {
|
static override from([x, y, width, height]: Rect): Rectangle {
|
||||||
return new Rectangle(x, y, width, height)
|
return new Rectangle(x, y, width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set all values from an array (for TypedArray compatibility) */
|
||||||
|
set(values: ArrayLike<number>): void {
|
||||||
|
this[0] = values[0] ?? 0
|
||||||
|
this[1] = values[1] ?? 0
|
||||||
|
this[2] = values[2] ?? 0
|
||||||
|
this[3] = values[3] ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create a subarray (for TypedArray compatibility) */
|
||||||
|
subarray(begin: number = 0, end?: number): number[] {
|
||||||
|
const endIndex = end ?? this.length
|
||||||
|
return this.slice(begin, endIndex)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new rectangle positioned at the given centre, with the given width/height.
|
* Creates a new rectangle positioned at the given centre, with the given width/height.
|
||||||
* @param centre The centre of the rectangle, as an `[x, y]` point
|
* @param centre The centre of the rectangle, as an `[x, y]` point
|
||||||
@@ -49,57 +52,38 @@ export class Rectangle extends Float64Array {
|
|||||||
* @param height The height of the rectangle. Default: {@link width}
|
* @param height The height of the rectangle. Default: {@link width}
|
||||||
* @returns A new rectangle whose centre is at {@link x}
|
* @returns A new rectangle whose centre is at {@link x}
|
||||||
*/
|
*/
|
||||||
static fromCentre(
|
static fromCentre([x, y]: Point, width: number, height = width): Rectangle {
|
||||||
[x, y]: ReadOnlyPoint,
|
|
||||||
width: number,
|
|
||||||
height = width
|
|
||||||
): Rectangle {
|
|
||||||
const left = x - width * 0.5
|
const left = x - width * 0.5
|
||||||
const top = y - height * 0.5
|
const top = y - height * 0.5
|
||||||
return new Rectangle(left, top, width, height)
|
return new Rectangle(left, top, width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
static ensureRect(rect: ReadOnlyRect): Rectangle {
|
static ensureRect(rect: Rect | Rectangle): Rectangle {
|
||||||
return rect instanceof Rectangle
|
return rect instanceof Rectangle
|
||||||
? rect
|
? rect
|
||||||
: new Rectangle(rect[0], rect[1], rect[2], rect[3])
|
: new Rectangle(rect[0], rect[1], rect[2], rect[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
override subarray(
|
|
||||||
begin: number = 0,
|
|
||||||
end?: number
|
|
||||||
): Float64Array<ArrayBuffer> {
|
|
||||||
const byteOffset = begin << 3
|
|
||||||
const length = end === undefined ? end : end - begin
|
|
||||||
return new Float64Array(this.buffer, byteOffset, length)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to the position of the top-left corner of this rectangle.
|
* The position of the top-left corner of this rectangle.
|
||||||
*
|
|
||||||
* Updating the values of the returned object will update this rectangle.
|
|
||||||
*/
|
*/
|
||||||
get pos(): Point {
|
get pos(): Point {
|
||||||
this.#pos ??= this.subarray(0, 2)
|
return [this[0], this[1]]
|
||||||
return this.#pos!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set pos(value: ReadOnlyPoint) {
|
set pos(value: Point) {
|
||||||
this[0] = value[0]
|
this[0] = value[0]
|
||||||
this[1] = value[1]
|
this[1] = value[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to the size of this rectangle.
|
* The size of this rectangle.
|
||||||
*
|
|
||||||
* Updating the values of the returned object will update this rectangle.
|
|
||||||
*/
|
*/
|
||||||
get size(): Size {
|
get size(): Size {
|
||||||
this.#size ??= this.subarray(2, 4)
|
return [this[2], this[3]]
|
||||||
return this.#size!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set size(value: ReadOnlySize) {
|
set size(value: Size) {
|
||||||
this[2] = value[0]
|
this[2] = value[0]
|
||||||
this[3] = value[1]
|
this[3] = value[1]
|
||||||
}
|
}
|
||||||
@@ -192,7 +176,7 @@ export class Rectangle extends Float64Array {
|
|||||||
* Updates the rectangle to the values of {@link rect}.
|
* Updates the rectangle to the values of {@link rect}.
|
||||||
* @param rect The rectangle to update to.
|
* @param rect The rectangle to update to.
|
||||||
*/
|
*/
|
||||||
updateTo(rect: ReadOnlyRect) {
|
updateTo(rect: Rect) {
|
||||||
this[0] = rect[0]
|
this[0] = rect[0]
|
||||||
this[1] = rect[1]
|
this[1] = rect[1]
|
||||||
this[2] = rect[2]
|
this[2] = rect[2]
|
||||||
@@ -215,7 +199,7 @@ export class Rectangle extends Float64Array {
|
|||||||
* @param point The point to check
|
* @param point The point to check
|
||||||
* @returns `true` if {@link point} is inside this rectangle, otherwise `false`.
|
* @returns `true` if {@link point} is inside this rectangle, otherwise `false`.
|
||||||
*/
|
*/
|
||||||
containsPoint([x, y]: ReadOnlyPoint): boolean {
|
containsPoint([x, y]: Point): boolean {
|
||||||
const [left, top, width, height] = this
|
const [left, top, width, height] = this
|
||||||
return x >= left && x < left + width && y >= top && y < top + height
|
return x >= left && x < left + width && y >= top && y < top + height
|
||||||
}
|
}
|
||||||
@@ -226,7 +210,7 @@ export class Rectangle extends Float64Array {
|
|||||||
* @param other The rectangle to check
|
* @param other The rectangle to check
|
||||||
* @returns `true` if {@link other} is inside this rectangle, otherwise `false`.
|
* @returns `true` if {@link other} is inside this rectangle, otherwise `false`.
|
||||||
*/
|
*/
|
||||||
containsRect(other: ReadOnlyRect): boolean {
|
containsRect(other: Rect | Rectangle): boolean {
|
||||||
const { right, bottom } = this
|
const { right, bottom } = this
|
||||||
const otherRight = other[0] + other[2]
|
const otherRight = other[0] + other[2]
|
||||||
const otherBottom = other[1] + other[3]
|
const otherBottom = other[1] + other[3]
|
||||||
@@ -251,7 +235,7 @@ export class Rectangle extends Float64Array {
|
|||||||
* @param rect The rectangle to check
|
* @param rect The rectangle to check
|
||||||
* @returns `true` if {@link rect} overlaps with this rectangle, otherwise `false`.
|
* @returns `true` if {@link rect} overlaps with this rectangle, otherwise `false`.
|
||||||
*/
|
*/
|
||||||
overlaps(rect: ReadOnlyRect): boolean {
|
overlaps(rect: Rect | Rectangle): boolean {
|
||||||
return (
|
return (
|
||||||
this.x < rect[0] + rect[2] &&
|
this.x < rect[0] + rect[2] &&
|
||||||
this.y < rect[1] + rect[3] &&
|
this.y < rect[1] + rect[3] &&
|
||||||
@@ -384,12 +368,12 @@ export class Rectangle extends Float64Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @returns The offset from the top-left of this rectangle to the point [{@link x}, {@link y}], as a new {@link Point}. */
|
/** @returns The offset from the top-left of this rectangle to the point [{@link x}, {@link y}], as a new {@link Point}. */
|
||||||
getOffsetTo([x, y]: ReadOnlyPoint): Point {
|
getOffsetTo([x, y]: Point): Point {
|
||||||
return [x - this[0], y - this[1]]
|
return [x - this[0], y - this[1]]
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns The offset from the point [{@link x}, {@link y}] to the top-left of this rectangle, as a new {@link Point}. */
|
/** @returns The offset from the point [{@link x}, {@link y}] to the top-left of this rectangle, as a new {@link Point}. */
|
||||||
getOffsetFrom([x, y]: ReadOnlyPoint): Point {
|
getOffsetFrom([x, y]: Point): Point {
|
||||||
return [this[0] - x, this[1] - y]
|
return [this[0] - x, this[1] - y]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,14 +454,4 @@ export class Rectangle extends Float64Array {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReadOnlyRectangle = Omit<
|
// ReadOnlyRectangle is now just Rectangle since we unified the types
|
||||||
ReadOnlyTypedArray<Rectangle>,
|
|
||||||
| 'setHeightBottomAnchored'
|
|
||||||
| 'setWidthRightAnchored'
|
|
||||||
| 'resizeTopLeft'
|
|
||||||
| 'resizeBottomLeft'
|
|
||||||
| 'resizeTopRight'
|
|
||||||
| 'resizeBottomRight'
|
|
||||||
| 'resizeBottomRight'
|
|
||||||
| 'updateTo'
|
|
||||||
>
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export interface HasBoundingRect {
|
|||||||
* @readonly
|
* @readonly
|
||||||
* @see {@link move}
|
* @see {@link move}
|
||||||
*/
|
*/
|
||||||
readonly boundingRect: ReadOnlyRect
|
readonly boundingRect: Rectangle
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An object containing a set of child objects */
|
/** An object containing a set of child objects */
|
||||||
@@ -194,7 +194,7 @@ export interface LinkSegment {
|
|||||||
/** The last canvas 2D path that was used to render this segment */
|
/** The last canvas 2D path that was used to render this segment */
|
||||||
path?: Path2D
|
path?: Path2D
|
||||||
/** Centre point of the {@link path}. Calculated during render only - can be inaccurate */
|
/** Centre point of the {@link path}. Calculated during render only - can be inaccurate */
|
||||||
readonly _pos: Float32Array
|
readonly _pos: [number, number]
|
||||||
/**
|
/**
|
||||||
* Y-forward along the {@link path} from its centre point, in radians.
|
* Y-forward along the {@link path} from its centre point, in radians.
|
||||||
* `undefined` if using circles for link centres.
|
* `undefined` if using circles for link centres.
|
||||||
@@ -226,52 +226,13 @@ export interface IFoundSlot extends IInputOrOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A point represented as `[x, y]` co-ordinates */
|
/** A point represented as `[x, y]` co-ordinates */
|
||||||
export type Point = [x: number, y: number] | Float32Array | Float64Array
|
export type Point = [x: number, y: number]
|
||||||
|
|
||||||
/** A size represented as `[width, height]` */
|
/** A size represented as `[width, height]` */
|
||||||
export type Size = [width: number, height: number] | Float32Array | Float64Array
|
export type Size = [width: number, height: number]
|
||||||
|
|
||||||
/** A very firm array */
|
|
||||||
type ArRect = [x: number, y: number, width: number, height: number]
|
|
||||||
|
|
||||||
/** A rectangle starting at top-left coordinates `[x, y, width, height]` */
|
/** A rectangle starting at top-left coordinates `[x, y, width, height]` */
|
||||||
export type Rect = ArRect | Float32Array | Float64Array
|
export type Rect = [number, number, number, number]
|
||||||
|
|
||||||
/** A point represented as `[x, y]` co-ordinates that will not be modified */
|
|
||||||
export type ReadOnlyPoint =
|
|
||||||
| readonly [x: number, y: number]
|
|
||||||
| ReadOnlyTypedArray<Float32Array>
|
|
||||||
| ReadOnlyTypedArray<Float64Array>
|
|
||||||
|
|
||||||
/** A size represented as `[width, height]` that will not be modified */
|
|
||||||
export type ReadOnlySize =
|
|
||||||
| readonly [width: number, height: number]
|
|
||||||
| ReadOnlyTypedArray<Float32Array>
|
|
||||||
| ReadOnlyTypedArray<Float64Array>
|
|
||||||
|
|
||||||
/** A rectangle starting at top-left coordinates `[x, y, width, height]` that will not be modified */
|
|
||||||
export type ReadOnlyRect =
|
|
||||||
| readonly [x: number, y: number, width: number, height: number]
|
|
||||||
| ReadOnlyTypedArray<Float32Array>
|
|
||||||
| ReadOnlyTypedArray<Float64Array>
|
|
||||||
|
|
||||||
type TypedArrays =
|
|
||||||
| Int8Array
|
|
||||||
| Uint8Array
|
|
||||||
| Uint8ClampedArray
|
|
||||||
| Int16Array
|
|
||||||
| Uint16Array
|
|
||||||
| Int32Array
|
|
||||||
| Uint32Array
|
|
||||||
| Float32Array
|
|
||||||
| Float64Array
|
|
||||||
|
|
||||||
type TypedBigIntArrays = BigInt64Array | BigUint64Array
|
|
||||||
export type ReadOnlyTypedArray<T extends TypedArrays | TypedBigIntArrays> =
|
|
||||||
Omit<
|
|
||||||
Readonly<T>,
|
|
||||||
'fill' | 'copyWithin' | 'reverse' | 'set' | 'sort' | 'subarray'
|
|
||||||
>
|
|
||||||
|
|
||||||
/** Union of property names that are of type Match */
|
/** Union of property names that are of type Match */
|
||||||
type KeysOfType<T, Match> = Exclude<
|
type KeysOfType<T, Match> = Exclude<
|
||||||
@@ -330,7 +291,7 @@ export interface INodeSlot extends HasBoundingRect {
|
|||||||
nameLocked?: boolean
|
nameLocked?: boolean
|
||||||
pos?: Point
|
pos?: Point
|
||||||
/** @remarks Automatically calculated; not included in serialisation. */
|
/** @remarks Automatically calculated; not included in serialisation. */
|
||||||
boundingRect: Rect
|
boundingRect: Rectangle
|
||||||
/**
|
/**
|
||||||
* A list of floating link IDs that are connected to this slot.
|
* A list of floating link IDs that are connected to this slot.
|
||||||
* This is calculated at runtime; it is **not** serialized.
|
* This is calculated at runtime; it is **not** serialized.
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import type {
|
import type { Rectangle } from './infrastructure/Rectangle'
|
||||||
HasBoundingRect,
|
import type { HasBoundingRect, Point, Rect } from './interfaces'
|
||||||
Point,
|
|
||||||
ReadOnlyPoint,
|
|
||||||
ReadOnlyRect,
|
|
||||||
Rect
|
|
||||||
} from './interfaces'
|
|
||||||
import { Alignment, LinkDirection, hasFlag } from './types/globalEnums'
|
import { Alignment, LinkDirection, hasFlag } from './types/globalEnums'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,7 +8,7 @@ import { Alignment, LinkDirection, hasFlag } from './types/globalEnums'
|
|||||||
* @param b Point b as `x, y`
|
* @param b Point b as `x, y`
|
||||||
* @returns Distance between point {@link a} & {@link b}
|
* @returns Distance between point {@link a} & {@link b}
|
||||||
*/
|
*/
|
||||||
export function distance(a: ReadOnlyPoint, b: ReadOnlyPoint): number {
|
export function distance(a: Point, b: Point): number {
|
||||||
return Math.sqrt(
|
return Math.sqrt(
|
||||||
(b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])
|
(b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])
|
||||||
)
|
)
|
||||||
@@ -61,10 +56,7 @@ export function isInRectangle(
|
|||||||
* @param rect The rectangle, as `x, y, width, height`
|
* @param rect The rectangle, as `x, y, width, height`
|
||||||
* @returns `true` if the point is inside the rect, otherwise `false`
|
* @returns `true` if the point is inside the rect, otherwise `false`
|
||||||
*/
|
*/
|
||||||
export function isPointInRect(
|
export function isPointInRect(point: Point, rect: Rect | Rectangle): boolean {
|
||||||
point: ReadOnlyPoint,
|
|
||||||
rect: ReadOnlyRect
|
|
||||||
): boolean {
|
|
||||||
return (
|
return (
|
||||||
point[0] >= rect[0] &&
|
point[0] >= rect[0] &&
|
||||||
point[0] < rect[0] + rect[2] &&
|
point[0] < rect[0] + rect[2] &&
|
||||||
@@ -80,7 +72,11 @@ export function isPointInRect(
|
|||||||
* @param rect The rectangle, as `x, y, width, height`
|
* @param rect The rectangle, as `x, y, width, height`
|
||||||
* @returns `true` if the point is inside the rect, otherwise `false`
|
* @returns `true` if the point is inside the rect, otherwise `false`
|
||||||
*/
|
*/
|
||||||
export function isInRect(x: number, y: number, rect: ReadOnlyRect): boolean {
|
export function isInRect(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
rect: Rect | Rectangle
|
||||||
|
): boolean {
|
||||||
return (
|
return (
|
||||||
x >= rect[0] &&
|
x >= rect[0] &&
|
||||||
x < rect[0] + rect[2] &&
|
x < rect[0] + rect[2] &&
|
||||||
@@ -121,7 +117,10 @@ export function isInsideRectangle(
|
|||||||
* @param b Rectangle B as `x, y, width, height`
|
* @param b Rectangle B as `x, y, width, height`
|
||||||
* @returns `true` if rectangles overlap, otherwise `false`
|
* @returns `true` if rectangles overlap, otherwise `false`
|
||||||
*/
|
*/
|
||||||
export function overlapBounding(a: ReadOnlyRect, b: ReadOnlyRect): boolean {
|
export function overlapBounding(
|
||||||
|
a: Rect | Rectangle,
|
||||||
|
b: Rect | Rectangle
|
||||||
|
): boolean {
|
||||||
const aRight = a[0] + a[2]
|
const aRight = a[0] + a[2]
|
||||||
const aBottom = a[1] + a[3]
|
const aBottom = a[1] + a[3]
|
||||||
const bRight = b[0] + b[2]
|
const bRight = b[0] + b[2]
|
||||||
@@ -137,7 +136,7 @@ export function overlapBounding(a: ReadOnlyRect, b: ReadOnlyRect): boolean {
|
|||||||
* @param rect The rectangle, as `x, y, width, height`
|
* @param rect The rectangle, as `x, y, width, height`
|
||||||
* @returns The centre of the rectangle, as `x, y`
|
* @returns The centre of the rectangle, as `x, y`
|
||||||
*/
|
*/
|
||||||
export function getCentre(rect: ReadOnlyRect): Point {
|
export function getCentre(rect: Rect | Rectangle): Point {
|
||||||
return [rect[0] + rect[2] * 0.5, rect[1] + rect[3] * 0.5]
|
return [rect[0] + rect[2] * 0.5, rect[1] + rect[3] * 0.5]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +146,10 @@ export function getCentre(rect: ReadOnlyRect): Point {
|
|||||||
* @param b Sub-rectangle B as `x, y, width, height`
|
* @param b Sub-rectangle B as `x, y, width, height`
|
||||||
* @returns `true` if {@link a} contains most of {@link b}, otherwise `false`
|
* @returns `true` if {@link a} contains most of {@link b}, otherwise `false`
|
||||||
*/
|
*/
|
||||||
export function containsCentre(a: ReadOnlyRect, b: ReadOnlyRect): boolean {
|
export function containsCentre(
|
||||||
|
a: Rect | Rectangle,
|
||||||
|
b: Rect | Rectangle
|
||||||
|
): boolean {
|
||||||
const centreX = b[0] + b[2] * 0.5
|
const centreX = b[0] + b[2] * 0.5
|
||||||
const centreY = b[1] + b[3] * 0.5
|
const centreY = b[1] + b[3] * 0.5
|
||||||
return isInRect(centreX, centreY, a)
|
return isInRect(centreX, centreY, a)
|
||||||
@@ -159,7 +161,10 @@ export function containsCentre(a: ReadOnlyRect, b: ReadOnlyRect): boolean {
|
|||||||
* @param b Sub-rectangle B as `x, y, width, height`
|
* @param b Sub-rectangle B as `x, y, width, height`
|
||||||
* @returns `true` if {@link a} wholly contains {@link b}, otherwise `false`
|
* @returns `true` if {@link a} wholly contains {@link b}, otherwise `false`
|
||||||
*/
|
*/
|
||||||
export function containsRect(a: ReadOnlyRect, b: ReadOnlyRect): boolean {
|
export function containsRect(
|
||||||
|
a: Rect | Rectangle,
|
||||||
|
b: Rect | Rectangle
|
||||||
|
): boolean {
|
||||||
const aRight = a[0] + a[2]
|
const aRight = a[0] + a[2]
|
||||||
const aBottom = a[1] + a[3]
|
const aBottom = a[1] + a[3]
|
||||||
const bRight = b[0] + b[2]
|
const bRight = b[0] + b[2]
|
||||||
@@ -289,8 +294,8 @@ export function rotateLink(
|
|||||||
* the right
|
* the right
|
||||||
*/
|
*/
|
||||||
export function getOrientation(
|
export function getOrientation(
|
||||||
lineStart: ReadOnlyPoint,
|
lineStart: Point,
|
||||||
lineEnd: ReadOnlyPoint,
|
lineEnd: Point,
|
||||||
x: number,
|
x: number,
|
||||||
y: number
|
y: number
|
||||||
): number {
|
): number {
|
||||||
@@ -310,10 +315,10 @@ export function getOrientation(
|
|||||||
*/
|
*/
|
||||||
export function findPointOnCurve(
|
export function findPointOnCurve(
|
||||||
out: Point,
|
out: Point,
|
||||||
a: ReadOnlyPoint,
|
a: Point,
|
||||||
b: ReadOnlyPoint,
|
b: Point,
|
||||||
controlA: ReadOnlyPoint,
|
controlA: Point,
|
||||||
controlB: ReadOnlyPoint,
|
controlB: Point,
|
||||||
t: number = 0.5
|
t: number = 0.5
|
||||||
): void {
|
): void {
|
||||||
const iT = 1 - t
|
const iT = 1 - t
|
||||||
@@ -330,8 +335,13 @@ export function findPointOnCurve(
|
|||||||
export function createBounds(
|
export function createBounds(
|
||||||
objects: Iterable<HasBoundingRect>,
|
objects: Iterable<HasBoundingRect>,
|
||||||
padding: number = 10
|
padding: number = 10
|
||||||
): ReadOnlyRect | null {
|
): Rect | null {
|
||||||
const bounds = new Float32Array([Infinity, Infinity, -Infinity, -Infinity])
|
const bounds: [number, number, number, number] = [
|
||||||
|
Infinity,
|
||||||
|
Infinity,
|
||||||
|
-Infinity,
|
||||||
|
-Infinity
|
||||||
|
]
|
||||||
|
|
||||||
for (const obj of objects) {
|
for (const obj of objects) {
|
||||||
const rect = obj.boundingRect
|
const rect = obj.boundingRect
|
||||||
@@ -379,11 +389,11 @@ export function snapPoint(pos: Point | Rect, snapTo: number): boolean {
|
|||||||
* @returns The original {@link rect}, modified in place.
|
* @returns The original {@link rect}, modified in place.
|
||||||
*/
|
*/
|
||||||
export function alignToContainer(
|
export function alignToContainer(
|
||||||
rect: Rect,
|
rect: Rect | Rectangle,
|
||||||
anchors: Alignment,
|
anchors: Alignment,
|
||||||
[containerX, containerY, containerWidth, containerHeight]: ReadOnlyRect,
|
[containerX, containerY, containerWidth, containerHeight]: Rect | Rectangle,
|
||||||
[insetX, insetY]: ReadOnlyPoint = [0, 0]
|
[insetX, insetY]: Point = [0, 0]
|
||||||
): Rect {
|
): Rect | Rectangle {
|
||||||
if (hasFlag(anchors, Alignment.Left)) {
|
if (hasFlag(anchors, Alignment.Left)) {
|
||||||
// Left
|
// Left
|
||||||
rect[0] = containerX + insetX
|
rect[0] = containerX + insetX
|
||||||
@@ -422,11 +432,11 @@ export function alignToContainer(
|
|||||||
* @returns The original {@link rect}, modified in place.
|
* @returns The original {@link rect}, modified in place.
|
||||||
*/
|
*/
|
||||||
export function alignOutsideContainer(
|
export function alignOutsideContainer(
|
||||||
rect: Rect,
|
rect: Rect | Rectangle,
|
||||||
anchors: Alignment,
|
anchors: Alignment,
|
||||||
[otherX, otherY, otherWidth, otherHeight]: ReadOnlyRect,
|
[otherX, otherY, otherWidth, otherHeight]: Rect | Rectangle,
|
||||||
[outsetX, outsetY]: ReadOnlyPoint = [0, 0]
|
[outsetX, outsetY]: Point = [0, 0]
|
||||||
): Rect {
|
): Rect | Rectangle {
|
||||||
if (hasFlag(anchors, Alignment.Left)) {
|
if (hasFlag(anchors, Alignment.Left)) {
|
||||||
// Left
|
// Left
|
||||||
rect[0] = otherX - outsetX - rect[2]
|
rect[0] = otherX - outsetX - rect[2]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type {
|
|||||||
INodeInputSlot,
|
INodeInputSlot,
|
||||||
INodeOutputSlot,
|
INodeOutputSlot,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
ReadOnlyPoint
|
Point
|
||||||
} from '@/lib/litegraph/src/interfaces'
|
} from '@/lib/litegraph/src/interfaces'
|
||||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import { type IDrawOptions, NodeSlot } from '@/lib/litegraph/src/node/NodeSlot'
|
import { type IDrawOptions, NodeSlot } from '@/lib/litegraph/src/node/NodeSlot'
|
||||||
@@ -32,7 +32,7 @@ export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
|
|||||||
this.#widget = widget ? new WeakRef(widget) : undefined
|
this.#widget = widget ? new WeakRef(widget) : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
get collapsedPos(): ReadOnlyPoint {
|
get collapsedPos(): Point {
|
||||||
return [0, LiteGraph.NODE_TITLE_HEIGHT * -0.5]
|
return [0, LiteGraph.NODE_TITLE_HEIGHT * -0.5]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type {
|
|||||||
INodeInputSlot,
|
INodeInputSlot,
|
||||||
INodeOutputSlot,
|
INodeOutputSlot,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
ReadOnlyPoint
|
Point
|
||||||
} from '@/lib/litegraph/src/interfaces'
|
} from '@/lib/litegraph/src/interfaces'
|
||||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import { type IDrawOptions, NodeSlot } from '@/lib/litegraph/src/node/NodeSlot'
|
import { type IDrawOptions, NodeSlot } from '@/lib/litegraph/src/node/NodeSlot'
|
||||||
@@ -24,7 +24,7 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
get collapsedPos(): ReadOnlyPoint {
|
get collapsedPos(): Point {
|
||||||
return [
|
return [
|
||||||
this.#node._collapsed_width ?? LiteGraph.NODE_COLLAPSED_WIDTH,
|
this.#node._collapsed_width ?? LiteGraph.NODE_COLLAPSED_WIDTH,
|
||||||
LiteGraph.NODE_TITLE_HEIGHT * -0.5
|
LiteGraph.NODE_TITLE_HEIGHT * -0.5
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import type {
|
|||||||
INodeSlot,
|
INodeSlot,
|
||||||
ISubgraphInput,
|
ISubgraphInput,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
Point,
|
Point
|
||||||
ReadOnlyPoint
|
|
||||||
} from '@/lib/litegraph/src/interfaces'
|
} from '@/lib/litegraph/src/interfaces'
|
||||||
import { LiteGraph, Rectangle } from '@/lib/litegraph/src/litegraph'
|
import { LiteGraph, Rectangle } from '@/lib/litegraph/src/litegraph'
|
||||||
import { getCentre } from '@/lib/litegraph/src/measure'
|
import { getCentre } from '@/lib/litegraph/src/measure'
|
||||||
@@ -36,7 +35,7 @@ export abstract class NodeSlot extends SlotBase implements INodeSlot {
|
|||||||
pos?: Point
|
pos?: Point
|
||||||
|
|
||||||
/** The offset from the parent node to the centre point of this slot. */
|
/** The offset from the parent node to the centre point of this slot. */
|
||||||
get #centreOffset(): ReadOnlyPoint {
|
get #centreOffset(): Point {
|
||||||
const nodePos = this.node.pos
|
const nodePos = this.node.pos
|
||||||
const { boundingRect } = this
|
const { boundingRect } = this
|
||||||
|
|
||||||
@@ -52,7 +51,7 @@ export abstract class NodeSlot extends SlotBase implements INodeSlot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The center point of this slot when the node is collapsed. */
|
/** The center point of this slot when the node is collapsed. */
|
||||||
abstract get collapsedPos(): ReadOnlyPoint
|
abstract get collapsedPos(): Point
|
||||||
|
|
||||||
#node: LGraphNode
|
#node: LGraphNode
|
||||||
get node(): LGraphNode {
|
get node(): LGraphNode {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type {
|
|||||||
INodeInputSlot,
|
INodeInputSlot,
|
||||||
INodeOutputSlot,
|
INodeOutputSlot,
|
||||||
Point,
|
Point,
|
||||||
ReadOnlyRect
|
Rect
|
||||||
} from '@/lib/litegraph/src/interfaces'
|
} from '@/lib/litegraph/src/interfaces'
|
||||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
|
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
|
||||||
@@ -213,7 +213,7 @@ export class SubgraphInput extends SubgraphSlot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** For inputs, x is the right edge of the input node. */
|
/** For inputs, x is the right edge of the input node. */
|
||||||
override arrange(rect: ReadOnlyRect): void {
|
override arrange(rect: Rect): void {
|
||||||
const [right, top, width, height] = rect
|
const [right, top, width, height] = rect
|
||||||
const { boundingRect: b, pos } = this
|
const { boundingRect: b, pos } = this
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type {
|
|||||||
INodeInputSlot,
|
INodeInputSlot,
|
||||||
INodeOutputSlot,
|
INodeOutputSlot,
|
||||||
Point,
|
Point,
|
||||||
ReadOnlyRect
|
Rect
|
||||||
} from '@/lib/litegraph/src/interfaces'
|
} from '@/lib/litegraph/src/interfaces'
|
||||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
|
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
|
||||||
@@ -119,7 +119,7 @@ export class SubgraphOutput extends SubgraphSlot {
|
|||||||
return [x + height, y + height * 0.5]
|
return [x + height, y + height * 0.5]
|
||||||
}
|
}
|
||||||
|
|
||||||
override arrange(rect: ReadOnlyRect): void {
|
override arrange(rect: Rect): void {
|
||||||
const [left, top, width, height] = rect
|
const [left, top, width, height] = rect
|
||||||
const { boundingRect: b, pos } = this
|
const { boundingRect: b, pos } = this
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import type {
|
|||||||
INodeInputSlot,
|
INodeInputSlot,
|
||||||
INodeOutputSlot,
|
INodeOutputSlot,
|
||||||
Point,
|
Point,
|
||||||
ReadOnlyRect,
|
Rect,
|
||||||
ReadOnlySize
|
Size
|
||||||
} from '@/lib/litegraph/src/interfaces'
|
} from '@/lib/litegraph/src/interfaces'
|
||||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import { SlotBase } from '@/lib/litegraph/src/node/SlotBase'
|
import { SlotBase } from '@/lib/litegraph/src/node/SlotBase'
|
||||||
@@ -45,7 +45,7 @@ export abstract class SubgraphSlot
|
|||||||
return LiteGraph.NODE_SLOT_HEIGHT
|
return LiteGraph.NODE_SLOT_HEIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly #pos: Point = new Float32Array(2)
|
readonly #pos: Point = [0, 0]
|
||||||
|
|
||||||
readonly measurement: ConstrainedSize = new ConstrainedSize(
|
readonly measurement: ConstrainedSize = new ConstrainedSize(
|
||||||
SubgraphSlot.defaultHeight,
|
SubgraphSlot.defaultHeight,
|
||||||
@@ -133,7 +133,7 @@ export abstract class SubgraphSlot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
measure(): ReadOnlySize {
|
measure(): Size {
|
||||||
const width = LGraphCanvas._measureText?.(this.displayName) ?? 0
|
const width = LGraphCanvas._measureText?.(this.displayName) ?? 0
|
||||||
|
|
||||||
const { defaultHeight } = SubgraphSlot
|
const { defaultHeight } = SubgraphSlot
|
||||||
@@ -141,7 +141,7 @@ export abstract class SubgraphSlot
|
|||||||
return this.measurement.toSize()
|
return this.measurement.toSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract arrange(rect: ReadOnlyRect): void
|
abstract arrange(rect: Rect): void
|
||||||
|
|
||||||
abstract connect(
|
abstract connect(
|
||||||
slot: INodeInputSlot | INodeOutputSlot,
|
slot: INodeInputSlot | INodeOutputSlot,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, vi } from 'vitest'
|
import { afterEach, beforeEach, describe, expect, vi } from 'vitest'
|
||||||
|
|
||||||
|
import { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'
|
||||||
import type { INodeInputSlot, Point } from '@/lib/litegraph/src/interfaces'
|
import type { INodeInputSlot, Point } from '@/lib/litegraph/src/interfaces'
|
||||||
import { LGraphNode, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
import { LGraphNode, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import { LGraph } from '@/lib/litegraph/src/litegraph'
|
import { LGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
@@ -84,8 +85,8 @@ describe('LGraphNode', () => {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
node.configure(configureData)
|
node.configure(configureData)
|
||||||
expect(node.pos).toEqual(new Float32Array([50, 60]))
|
expect(node.pos).toEqual([50, 60])
|
||||||
expect(node.size).toEqual(new Float32Array([70, 80]))
|
expect(node.size).toEqual([70, 80])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should configure inputs correctly', () => {
|
test('should configure inputs correctly', () => {
|
||||||
@@ -571,7 +572,7 @@ describe('LGraphNode', () => {
|
|||||||
name: 'test_in',
|
name: 'test_in',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
link: null,
|
link: null,
|
||||||
boundingRect: new Float32Array([0, 0, 0, 0])
|
boundingRect: new Rectangle(0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
test('should return position based on title height when collapsed', () => {
|
test('should return position based on title height when collapsed', () => {
|
||||||
@@ -594,7 +595,7 @@ describe('LGraphNode', () => {
|
|||||||
name: 'test_in_2',
|
name: 'test_in_2',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
link: null,
|
link: null,
|
||||||
boundingRect: new Float32Array([0, 0, 0, 0])
|
boundingRect: new Rectangle(0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
node.inputs = [inputSlot, inputSlot2]
|
node.inputs = [inputSlot, inputSlot2]
|
||||||
const slotIndex = 0
|
const slotIndex = 0
|
||||||
@@ -629,13 +630,13 @@ describe('LGraphNode', () => {
|
|||||||
name: 'in0',
|
name: 'in0',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
link: null,
|
link: null,
|
||||||
boundingRect: new Float32Array([0, 0, 0, 0])
|
boundingRect: new Rectangle(0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
const input1: INodeInputSlot = {
|
const input1: INodeInputSlot = {
|
||||||
name: 'in1',
|
name: 'in1',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
link: null,
|
link: null,
|
||||||
boundingRect: new Float32Array([0, 0, 0, 0]),
|
boundingRect: new Rectangle(0, 0, 0, 0),
|
||||||
pos: [5, 45]
|
pos: [5, 45]
|
||||||
}
|
}
|
||||||
node.inputs = [input0, input1]
|
node.inputs = [input0, input1]
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ exports[`LGraph configure() > LGraph matches previous snapshot (normal configure
|
|||||||
LGraph {
|
LGraph {
|
||||||
"_groups": [
|
"_groups": [
|
||||||
LGraphGroup {
|
LGraphGroup {
|
||||||
"_bounding": Float32Array [
|
"_bounding": [
|
||||||
20,
|
10,
|
||||||
20,
|
10,
|
||||||
1,
|
140,
|
||||||
3,
|
80,
|
||||||
],
|
],
|
||||||
"_children": Set {},
|
"_children": Set {},
|
||||||
"_nodes": [],
|
"_nodes": [],
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
20,
|
20,
|
||||||
20,
|
20,
|
||||||
],
|
],
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
1,
|
1,
|
||||||
3,
|
3,
|
||||||
],
|
],
|
||||||
@@ -39,19 +39,19 @@ LGraph {
|
|||||||
LGraphNode {
|
LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -98,6 +98,7 @@ LGraph {
|
|||||||
"selected": [Function],
|
"selected": [Function],
|
||||||
},
|
},
|
||||||
"title": "LGraphNode",
|
"title": "LGraphNode",
|
||||||
|
"title_buttons": [],
|
||||||
"type": "mustBeSet",
|
"type": "mustBeSet",
|
||||||
"widgets": undefined,
|
"widgets": undefined,
|
||||||
"widgets_start_y": undefined,
|
"widgets_start_y": undefined,
|
||||||
@@ -108,19 +109,19 @@ LGraph {
|
|||||||
"1": LGraphNode {
|
"1": LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -167,6 +168,7 @@ LGraph {
|
|||||||
"selected": [Function],
|
"selected": [Function],
|
||||||
},
|
},
|
||||||
"title": "LGraphNode",
|
"title": "LGraphNode",
|
||||||
|
"title_buttons": [],
|
||||||
"type": "mustBeSet",
|
"type": "mustBeSet",
|
||||||
"widgets": undefined,
|
"widgets": undefined,
|
||||||
"widgets_start_y": undefined,
|
"widgets_start_y": undefined,
|
||||||
@@ -178,19 +180,19 @@ LGraph {
|
|||||||
LGraphNode {
|
LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -237,6 +239,7 @@ LGraph {
|
|||||||
"selected": [Function],
|
"selected": [Function],
|
||||||
},
|
},
|
||||||
"title": "LGraphNode",
|
"title": "LGraphNode",
|
||||||
|
"title_buttons": [],
|
||||||
"type": "mustBeSet",
|
"type": "mustBeSet",
|
||||||
"widgets": undefined,
|
"widgets": undefined,
|
||||||
"widgets_start_y": undefined,
|
"widgets_start_y": undefined,
|
||||||
@@ -249,7 +252,16 @@ LGraph {
|
|||||||
"config": {},
|
"config": {},
|
||||||
"elapsed_time": 0.01,
|
"elapsed_time": 0.01,
|
||||||
"errors_in_execution": undefined,
|
"errors_in_execution": undefined,
|
||||||
"events": CustomEventTarget {},
|
"events": CustomEventTarget {
|
||||||
|
Symbol(listeners): {
|
||||||
|
"bubbling": Map {},
|
||||||
|
"capturing": Map {},
|
||||||
|
},
|
||||||
|
Symbol(listenerOptions): {
|
||||||
|
"bubbling": Map {},
|
||||||
|
"capturing": Map {},
|
||||||
|
},
|
||||||
|
},
|
||||||
"execution_time": undefined,
|
"execution_time": undefined,
|
||||||
"execution_timer_id": undefined,
|
"execution_timer_id": undefined,
|
||||||
"extra": {},
|
"extra": {},
|
||||||
@@ -296,7 +308,16 @@ LGraph {
|
|||||||
"config": {},
|
"config": {},
|
||||||
"elapsed_time": 0.01,
|
"elapsed_time": 0.01,
|
||||||
"errors_in_execution": undefined,
|
"errors_in_execution": undefined,
|
||||||
"events": CustomEventTarget {},
|
"events": CustomEventTarget {
|
||||||
|
Symbol(listeners): {
|
||||||
|
"bubbling": Map {},
|
||||||
|
"capturing": Map {},
|
||||||
|
},
|
||||||
|
Symbol(listenerOptions): {
|
||||||
|
"bubbling": Map {},
|
||||||
|
"capturing": Map {},
|
||||||
|
},
|
||||||
|
},
|
||||||
"execution_time": undefined,
|
"execution_time": undefined,
|
||||||
"execution_timer_id": undefined,
|
"execution_timer_id": undefined,
|
||||||
"extra": {},
|
"extra": {},
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ exports[`LGraph > supports schema v0.4 graphs > oldSchemaGraph 1`] = `
|
|||||||
LGraph {
|
LGraph {
|
||||||
"_groups": [
|
"_groups": [
|
||||||
LGraphGroup {
|
LGraphGroup {
|
||||||
"_bounding": Float32Array [
|
"_bounding": [
|
||||||
20,
|
10,
|
||||||
20,
|
10,
|
||||||
1,
|
140,
|
||||||
3,
|
80,
|
||||||
],
|
],
|
||||||
"_children": Set {},
|
"_children": Set {},
|
||||||
"_nodes": [],
|
"_nodes": [],
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
20,
|
20,
|
||||||
20,
|
20,
|
||||||
],
|
],
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
1,
|
1,
|
||||||
3,
|
3,
|
||||||
],
|
],
|
||||||
@@ -39,19 +39,19 @@ LGraph {
|
|||||||
LGraphNode {
|
LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -111,19 +111,19 @@ LGraph {
|
|||||||
"1": LGraphNode {
|
"1": LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -184,19 +184,19 @@ LGraph {
|
|||||||
LGraphNode {
|
LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -258,7 +258,16 @@ LGraph {
|
|||||||
"config": {},
|
"config": {},
|
||||||
"elapsed_time": 0.01,
|
"elapsed_time": 0.01,
|
||||||
"errors_in_execution": undefined,
|
"errors_in_execution": undefined,
|
||||||
"events": CustomEventTarget {},
|
"events": CustomEventTarget {
|
||||||
|
Symbol(listeners): {
|
||||||
|
"bubbling": Map {},
|
||||||
|
"capturing": Map {},
|
||||||
|
},
|
||||||
|
Symbol(listenerOptions): {
|
||||||
|
"bubbling": Map {},
|
||||||
|
"capturing": Map {},
|
||||||
|
},
|
||||||
|
},
|
||||||
"execution_time": undefined,
|
"execution_time": undefined,
|
||||||
"execution_timer_id": undefined,
|
"execution_timer_id": undefined,
|
||||||
"extra": {},
|
"extra": {},
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ exports[`LGraph (constructor only) > Matches previous snapshot > basicLGraph 1`]
|
|||||||
LGraph {
|
LGraph {
|
||||||
"_groups": [
|
"_groups": [
|
||||||
LGraphGroup {
|
LGraphGroup {
|
||||||
"_bounding": Float32Array [
|
"_bounding": [
|
||||||
20,
|
10,
|
||||||
20,
|
10,
|
||||||
1,
|
140,
|
||||||
3,
|
80,
|
||||||
],
|
],
|
||||||
"_children": Set {},
|
"_children": Set {},
|
||||||
"_nodes": [],
|
"_nodes": [],
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
20,
|
20,
|
||||||
20,
|
20,
|
||||||
],
|
],
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
1,
|
1,
|
||||||
3,
|
3,
|
||||||
],
|
],
|
||||||
@@ -39,19 +39,19 @@ LGraph {
|
|||||||
LGraphNode {
|
LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -109,19 +109,19 @@ LGraph {
|
|||||||
"1": LGraphNode {
|
"1": LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -180,19 +180,19 @@ LGraph {
|
|||||||
LGraphNode {
|
LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -252,7 +252,16 @@ LGraph {
|
|||||||
"config": {},
|
"config": {},
|
||||||
"elapsed_time": 0.01,
|
"elapsed_time": 0.01,
|
||||||
"errors_in_execution": undefined,
|
"errors_in_execution": undefined,
|
||||||
"events": CustomEventTarget {},
|
"events": CustomEventTarget {
|
||||||
|
Symbol(listeners): {
|
||||||
|
"bubbling": Map {},
|
||||||
|
"capturing": Map {},
|
||||||
|
},
|
||||||
|
Symbol(listenerOptions): {
|
||||||
|
"bubbling": Map {},
|
||||||
|
"capturing": Map {},
|
||||||
|
},
|
||||||
|
},
|
||||||
"execution_time": undefined,
|
"execution_time": undefined,
|
||||||
"execution_timer_id": undefined,
|
"execution_timer_id": undefined,
|
||||||
"extra": {},
|
"extra": {},
|
||||||
@@ -299,7 +308,16 @@ LGraph {
|
|||||||
"config": {},
|
"config": {},
|
||||||
"elapsed_time": 0.01,
|
"elapsed_time": 0.01,
|
||||||
"errors_in_execution": undefined,
|
"errors_in_execution": undefined,
|
||||||
"events": CustomEventTarget {},
|
"events": CustomEventTarget {
|
||||||
|
Symbol(listeners): {
|
||||||
|
"bubbling": Map {},
|
||||||
|
"capturing": Map {},
|
||||||
|
},
|
||||||
|
Symbol(listenerOptions): {
|
||||||
|
"bubbling": Map {},
|
||||||
|
"capturing": Map {},
|
||||||
|
},
|
||||||
|
},
|
||||||
"execution_time": undefined,
|
"execution_time": undefined,
|
||||||
"execution_timer_id": undefined,
|
"execution_timer_id": undefined,
|
||||||
"extra": {},
|
"extra": {},
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { test as baseTest } from 'vitest'
|
import { test as baseTest } from 'vitest'
|
||||||
|
|
||||||
|
import { Rectangle } from '../src/infrastructure/Rectangle'
|
||||||
import type { Point, Rect } from '../src/interfaces'
|
import type { Point, Rect } from '../src/interfaces'
|
||||||
import {
|
import {
|
||||||
addDirectionalOffset,
|
addDirectionalOffset,
|
||||||
@@ -131,8 +132,8 @@ test('snapPoint correctly snaps points to grid', ({ expect }) => {
|
|||||||
|
|
||||||
test('createBounds correctly creates bounding box', ({ expect }) => {
|
test('createBounds correctly creates bounding box', ({ expect }) => {
|
||||||
const objects = [
|
const objects = [
|
||||||
{ boundingRect: [0, 0, 10, 10] as Rect },
|
{ boundingRect: new Rectangle(0, 0, 10, 10) },
|
||||||
{ boundingRect: [5, 5, 10, 10] as Rect }
|
{ boundingRect: new Rectangle(5, 5, 10, 10) }
|
||||||
]
|
]
|
||||||
|
|
||||||
const defaultBounds = createBounds(objects)
|
const defaultBounds = createBounds(objects)
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import type { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'
|
import type { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'
|
||||||
import type { RenderLink } from '@/lib/litegraph/src/canvas/RenderLink'
|
import { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||||
import type { ReadOnlyPoint } from '@/lib/litegraph/src/interfaces'
|
// import type { RenderLink } from '@/lib/litegraph/src/canvas/RenderLink'
|
||||||
import { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'
|
import type { Point } from '@/lib/litegraph/src/interfaces'
|
||||||
|
// import { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'
|
||||||
import { resolveConnectingLinkColor } from '@/lib/litegraph/src/utils/linkColors'
|
import { resolveConnectingLinkColor } from '@/lib/litegraph/src/utils/linkColors'
|
||||||
import { createLinkConnectorAdapter } from '@/renderer/core/canvas/links/linkConnectorAdapter'
|
import { createLinkConnectorAdapter } from '@/renderer/core/canvas/links/linkConnectorAdapter'
|
||||||
import { useSlotLinkDragState } from '@/renderer/core/canvas/links/slotLinkDragState'
|
import { useSlotLinkDragState } from '@/renderer/core/canvas/links/slotLinkDragState'
|
||||||
import type { LinkRenderContext } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
|
import type { LinkRenderContext } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
|
||||||
import { getSlotKey } from '@/renderer/core/layout/slots/slotIdentifier'
|
|
||||||
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
|
// import { getSlotKey } from '@/renderer/core/layout/slots/slotIdentifier'
|
||||||
|
// import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
|
||||||
|
|
||||||
function buildContext(canvas: LGraphCanvas): LinkRenderContext {
|
function buildContext(canvas: LGraphCanvas): LinkRenderContext {
|
||||||
return {
|
return {
|
||||||
@@ -49,24 +51,32 @@ export function attachSlotLinkPreviewRenderer(canvas: LGraphCanvas) {
|
|||||||
const renderLinks = createLinkConnectorAdapter()?.renderLinks
|
const renderLinks = createLinkConnectorAdapter()?.renderLinks
|
||||||
if (!renderLinks || renderLinks.length === 0) return
|
if (!renderLinks || renderLinks.length === 0) return
|
||||||
|
|
||||||
const to: ReadOnlyPoint = [pointer.canvas.x, pointer.canvas.y]
|
const to: Point = [pointer.canvas.x, pointer.canvas.y]
|
||||||
ctx.save()
|
ctx.save()
|
||||||
for (const link of renderLinks) {
|
for (const link of renderLinks) {
|
||||||
const startDir = link.fromDirection ?? LinkDirection.RIGHT
|
// const startDir = link.fromDirection ?? LinkDirection.RIGHT
|
||||||
const endDir = link.dragDirection ?? LinkDirection.CENTER
|
// const endDir = link.dragDirection ?? LinkDirection.CENTER
|
||||||
const colour = resolveConnectingLinkColor(link.fromSlot.type)
|
const color = resolveConnectingLinkColor(link.fromSlot.type)
|
||||||
|
|
||||||
const fromPoint = resolveRenderLinkOrigin(link)
|
// const fromPoint = resolveRenderLinkOrigin(link)
|
||||||
|
const { node, fromSlot, fromSlotIndex } = link
|
||||||
linkRenderer.renderDraggingLink(
|
if (node instanceof LGraphNode && 'link' in fromSlot) {
|
||||||
ctx,
|
linkRenderer.renderDraggingLink(
|
||||||
fromPoint,
|
ctx,
|
||||||
to,
|
node,
|
||||||
colour,
|
fromSlot,
|
||||||
startDir,
|
fromSlotIndex,
|
||||||
endDir,
|
to,
|
||||||
context
|
context,
|
||||||
)
|
{
|
||||||
|
color
|
||||||
|
}
|
||||||
|
// colour,
|
||||||
|
// startDir,
|
||||||
|
// endDir,
|
||||||
|
// context
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
}
|
}
|
||||||
@@ -74,35 +84,35 @@ export function attachSlotLinkPreviewRenderer(canvas: LGraphCanvas) {
|
|||||||
canvas.onDrawForeground = patched
|
canvas.onDrawForeground = patched
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveRenderLinkOrigin(link: RenderLink): ReadOnlyPoint {
|
// function resolveRenderLinkOrigin(link: RenderLink): Point {
|
||||||
if (link.fromReroute) {
|
// if (link.fromReroute) {
|
||||||
const rerouteLayout = layoutStore.getRerouteLayout(link.fromReroute.id)
|
// const rerouteLayout = layoutStore.getRerouteLayout(link.fromReroute.id)
|
||||||
if (rerouteLayout) {
|
// if (rerouteLayout) {
|
||||||
return [rerouteLayout.position.x, rerouteLayout.position.y]
|
// return [rerouteLayout.position.x, rerouteLayout.position.y]
|
||||||
}
|
// }
|
||||||
|
|
||||||
const [x, y] = link.fromReroute.pos
|
// const [x, y] = link.fromReroute.pos
|
||||||
return [x, y]
|
// return [x, y]
|
||||||
}
|
// }
|
||||||
|
|
||||||
const nodeId = getRenderLinkNodeId(link)
|
// const nodeId = getRenderLinkNodeId(link)
|
||||||
if (nodeId != null) {
|
// if (nodeId != null) {
|
||||||
const isInputFrom = link.toType === 'output'
|
// const isInputFrom = link.toType === 'output'
|
||||||
const key = getSlotKey(String(nodeId), link.fromSlotIndex, isInputFrom)
|
// const key = getSlotKey(String(nodeId), link.fromSlotIndex, isInputFrom)
|
||||||
const layout = layoutStore.getSlotLayout(key)
|
// const layout = layoutStore.getSlotLayout(key)
|
||||||
if (layout) {
|
// if (layout) {
|
||||||
return [layout.position.x, layout.position.y]
|
// return [layout.position.x, layout.position.y]
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return link.fromPos
|
// return link.fromPos
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getRenderLinkNodeId(link: RenderLink): number | null {
|
// function getRenderLinkNodeId(link: RenderLink): number | null {
|
||||||
const node = link.node
|
// const node = link.node
|
||||||
if (typeof node === 'object' && node !== null && 'id' in node) {
|
// if (typeof node === 'object' && node !== null && 'id' in node) {
|
||||||
const maybeId = node.id
|
// const maybeId = node.id
|
||||||
if (typeof maybeId === 'number') return maybeId
|
// if (typeof maybeId === 'number') return maybeId
|
||||||
}
|
// }
|
||||||
return null
|
// return null
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -6,11 +6,14 @@
|
|||||||
* rendering data that can be consumed by the PathRenderer.
|
* rendering data that can be consumed by the PathRenderer.
|
||||||
* Maintains backward compatibility with existing litegraph integration.
|
* Maintains backward compatibility with existing litegraph integration.
|
||||||
*/
|
*/
|
||||||
|
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||||
import type { LLink } from '@/lib/litegraph/src/LLink'
|
import type { LLink } from '@/lib/litegraph/src/LLink'
|
||||||
import type { Reroute } from '@/lib/litegraph/src/Reroute'
|
import type { Reroute } from '@/lib/litegraph/src/Reroute'
|
||||||
import type {
|
import type {
|
||||||
CanvasColour,
|
CanvasColour,
|
||||||
ReadOnlyPoint
|
INodeInputSlot,
|
||||||
|
INodeOutputSlot,
|
||||||
|
Point as LitegraphPoint
|
||||||
} from '@/lib/litegraph/src/interfaces'
|
} from '@/lib/litegraph/src/interfaces'
|
||||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import {
|
import {
|
||||||
@@ -18,10 +21,12 @@ import {
|
|||||||
LinkMarkerShape,
|
LinkMarkerShape,
|
||||||
LinkRenderType
|
LinkRenderType
|
||||||
} from '@/lib/litegraph/src/types/globalEnums'
|
} from '@/lib/litegraph/src/types/globalEnums'
|
||||||
|
import { getSlotPosition } from '@/renderer/core/canvas/litegraph/slotCalculations'
|
||||||
import {
|
import {
|
||||||
type ArrowShape,
|
type ArrowShape,
|
||||||
CanvasPathRenderer,
|
CanvasPathRenderer,
|
||||||
type Direction,
|
type Direction,
|
||||||
|
type DragLinkData,
|
||||||
type LinkRenderData,
|
type LinkRenderData,
|
||||||
type RenderContext as PathRenderContext,
|
type RenderContext as PathRenderContext,
|
||||||
type Point,
|
type Point,
|
||||||
@@ -71,6 +76,7 @@ export class LitegraphLinkAdapter {
|
|||||||
case LinkDirection.DOWN:
|
case LinkDirection.DOWN:
|
||||||
return 'down'
|
return 'down'
|
||||||
case LinkDirection.CENTER:
|
case LinkDirection.CENTER:
|
||||||
|
case LinkDirection.NONE:
|
||||||
return 'none'
|
return 'none'
|
||||||
default:
|
default:
|
||||||
return 'right'
|
return 'right'
|
||||||
@@ -172,22 +178,22 @@ export class LitegraphLinkAdapter {
|
|||||||
* Critically: does nothing for CENTER/NONE directions (no case for them)
|
* Critically: does nothing for CENTER/NONE directions (no case for them)
|
||||||
*/
|
*/
|
||||||
private applySplineOffset(
|
private applySplineOffset(
|
||||||
point: Point,
|
point: LitegraphPoint,
|
||||||
direction: LinkDirection,
|
direction: LinkDirection,
|
||||||
distance: number
|
distance: number
|
||||||
): void {
|
): void {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case LinkDirection.LEFT:
|
case LinkDirection.LEFT:
|
||||||
point.x -= distance
|
point[0] -= distance
|
||||||
break
|
break
|
||||||
case LinkDirection.RIGHT:
|
case LinkDirection.RIGHT:
|
||||||
point.x += distance
|
point[0] += distance
|
||||||
break
|
break
|
||||||
case LinkDirection.UP:
|
case LinkDirection.UP:
|
||||||
point.y -= distance
|
point[1] -= distance
|
||||||
break
|
break
|
||||||
case LinkDirection.DOWN:
|
case LinkDirection.DOWN:
|
||||||
point.y += distance
|
point[1] += distance
|
||||||
break
|
break
|
||||||
// CENTER and NONE: no offset applied (original behavior)
|
// CENTER and NONE: no offset applied (original behavior)
|
||||||
}
|
}
|
||||||
@@ -199,8 +205,8 @@ export class LitegraphLinkAdapter {
|
|||||||
*/
|
*/
|
||||||
renderLinkDirect(
|
renderLinkDirect(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
a: ReadOnlyPoint,
|
a: LitegraphPoint,
|
||||||
b: ReadOnlyPoint,
|
b: LitegraphPoint,
|
||||||
link: LLink | null,
|
link: LLink | null,
|
||||||
skip_border: boolean,
|
skip_border: boolean,
|
||||||
flow: number | boolean | null,
|
flow: number | boolean | null,
|
||||||
@@ -210,8 +216,8 @@ export class LitegraphLinkAdapter {
|
|||||||
context: LinkRenderContext,
|
context: LinkRenderContext,
|
||||||
extras: {
|
extras: {
|
||||||
reroute?: Reroute
|
reroute?: Reroute
|
||||||
startControl?: ReadOnlyPoint
|
startControl?: LitegraphPoint
|
||||||
endControl?: ReadOnlyPoint
|
endControl?: LitegraphPoint
|
||||||
num_sublines?: number
|
num_sublines?: number
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
} = {}
|
} = {}
|
||||||
@@ -272,13 +278,19 @@ export class LitegraphLinkAdapter {
|
|||||||
y: a[1] + (extras.startControl![1] || 0)
|
y: a[1] + (extras.startControl![1] || 0)
|
||||||
}
|
}
|
||||||
const end = { x: b[0], y: b[1] }
|
const end = { x: b[0], y: b[1] }
|
||||||
this.applySplineOffset(end, endDir, dist * factor)
|
const endArray: LitegraphPoint = [end.x, end.y]
|
||||||
|
this.applySplineOffset(endArray, endDir, dist * factor)
|
||||||
|
end.x = endArray[0]
|
||||||
|
end.y = endArray[1]
|
||||||
cps.push(start, end)
|
cps.push(start, end)
|
||||||
linkData.controlPoints = cps
|
linkData.controlPoints = cps
|
||||||
} else if (!hasStartCtrl && hasEndCtrl) {
|
} else if (!hasStartCtrl && hasEndCtrl) {
|
||||||
// End provided, derive start via direction offset (CENTER => no offset)
|
// End provided, derive start via direction offset (CENTER => no offset)
|
||||||
const start = { x: a[0], y: a[1] }
|
const start = { x: a[0], y: a[1] }
|
||||||
this.applySplineOffset(start, startDir, dist * factor)
|
const startArray: LitegraphPoint = [start.x, start.y]
|
||||||
|
this.applySplineOffset(startArray, startDir, dist * factor)
|
||||||
|
start.x = startArray[0]
|
||||||
|
start.y = startArray[1]
|
||||||
const end = {
|
const end = {
|
||||||
x: b[0] + (extras.endControl![0] || 0),
|
x: b[0] + (extras.endControl![0] || 0),
|
||||||
y: b[1] + (extras.endControl![1] || 0)
|
y: b[1] + (extras.endControl![1] || 0)
|
||||||
@@ -289,8 +301,14 @@ export class LitegraphLinkAdapter {
|
|||||||
// Neither provided: derive both from directions (CENTER => no offset)
|
// Neither provided: derive both from directions (CENTER => no offset)
|
||||||
const start = { x: a[0], y: a[1] }
|
const start = { x: a[0], y: a[1] }
|
||||||
const end = { x: b[0], y: b[1] }
|
const end = { x: b[0], y: b[1] }
|
||||||
this.applySplineOffset(start, startDir, dist * factor)
|
const startArray: LitegraphPoint = [start.x, start.y]
|
||||||
this.applySplineOffset(end, endDir, dist * factor)
|
const endArray: LitegraphPoint = [end.x, end.y]
|
||||||
|
this.applySplineOffset(startArray, startDir, dist * factor)
|
||||||
|
this.applySplineOffset(endArray, endDir, dist * factor)
|
||||||
|
start.x = startArray[0]
|
||||||
|
start.y = startArray[1]
|
||||||
|
end.x = endArray[0]
|
||||||
|
end.y = endArray[1]
|
||||||
cps.push(start, end)
|
cps.push(start, end)
|
||||||
linkData.controlPoints = cps
|
linkData.controlPoints = cps
|
||||||
}
|
}
|
||||||
@@ -315,7 +333,7 @@ export class LitegraphLinkAdapter {
|
|||||||
// Copy calculated center position back to litegraph object
|
// Copy calculated center position back to litegraph object
|
||||||
// This is needed for hit detection and menu interaction
|
// This is needed for hit detection and menu interaction
|
||||||
if (linkData.centerPos) {
|
if (linkData.centerPos) {
|
||||||
linkSegment._pos = linkSegment._pos || new Float32Array(2)
|
linkSegment._pos = linkSegment._pos || [0, 0]
|
||||||
linkSegment._pos[0] = linkData.centerPos.x
|
linkSegment._pos[0] = linkData.centerPos.x
|
||||||
linkSegment._pos[1] = linkData.centerPos.y
|
linkSegment._pos[1] = linkData.centerPos.y
|
||||||
|
|
||||||
@@ -329,8 +347,8 @@ export class LitegraphLinkAdapter {
|
|||||||
if (this.enableLayoutStoreWrites && link && link.id !== -1) {
|
if (this.enableLayoutStoreWrites && link && link.id !== -1) {
|
||||||
// Calculate bounds and center only when writing
|
// Calculate bounds and center only when writing
|
||||||
const bounds = this.calculateLinkBounds(
|
const bounds = this.calculateLinkBounds(
|
||||||
[linkData.startPoint.x, linkData.startPoint.y] as ReadOnlyPoint,
|
[linkData.startPoint.x, linkData.startPoint.y] as LitegraphPoint,
|
||||||
[linkData.endPoint.x, linkData.endPoint.y] as ReadOnlyPoint,
|
[linkData.endPoint.x, linkData.endPoint.y] as LitegraphPoint,
|
||||||
linkData
|
linkData
|
||||||
)
|
)
|
||||||
const centerPos = linkData.centerPos || {
|
const centerPos = linkData.centerPos || {
|
||||||
@@ -363,33 +381,56 @@ export class LitegraphLinkAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a link being dragged from a slot to mouse position
|
||||||
|
* Used during link creation/reconnection
|
||||||
|
*/
|
||||||
renderDraggingLink(
|
renderDraggingLink(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
from: ReadOnlyPoint,
|
fromNode: LGraphNode | null,
|
||||||
to: ReadOnlyPoint,
|
fromSlot: INodeOutputSlot | INodeInputSlot,
|
||||||
colour: CanvasColour,
|
fromSlotIndex: number,
|
||||||
startDir: LinkDirection,
|
toPosition: LitegraphPoint,
|
||||||
endDir: LinkDirection,
|
context: LinkRenderContext,
|
||||||
context: LinkRenderContext
|
options: {
|
||||||
|
fromInput?: boolean
|
||||||
|
color?: CanvasColour
|
||||||
|
disabled?: boolean
|
||||||
|
} = {}
|
||||||
): void {
|
): void {
|
||||||
this.renderLinkDirect(
|
if (!fromNode) return
|
||||||
ctx,
|
|
||||||
from,
|
// Get slot position using layout tree if available
|
||||||
to,
|
const slotPos = getSlotPosition(
|
||||||
null,
|
fromNode,
|
||||||
false,
|
fromSlotIndex,
|
||||||
null,
|
options.fromInput || false
|
||||||
colour,
|
|
||||||
startDir,
|
|
||||||
endDir,
|
|
||||||
{
|
|
||||||
...context,
|
|
||||||
linkMarkerShape: LinkMarkerShape.None
|
|
||||||
},
|
|
||||||
{
|
|
||||||
disabled: false
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
if (!slotPos) return
|
||||||
|
|
||||||
|
// Get slot direction
|
||||||
|
const slotDir =
|
||||||
|
fromSlot.dir ||
|
||||||
|
(options.fromInput ? LinkDirection.LEFT : LinkDirection.RIGHT)
|
||||||
|
// Create drag data
|
||||||
|
const dragData: DragLinkData = {
|
||||||
|
fixedPoint: { x: slotPos[0], y: slotPos[1] },
|
||||||
|
fixedDirection: this.convertDirection(slotDir),
|
||||||
|
dragPoint: { x: toPosition[0], y: toPosition[1] },
|
||||||
|
color: options.color ? String(options.color) : undefined,
|
||||||
|
type: fromSlot.type !== undefined ? String(fromSlot.type) : undefined,
|
||||||
|
disabled: options.disabled || false,
|
||||||
|
fromInput: options.fromInput || false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert context
|
||||||
|
const pathContext = this.convertToPathRenderContext(context)
|
||||||
|
|
||||||
|
// Hide center marker when dragging links
|
||||||
|
pathContext.style.showCenterMarker = false
|
||||||
|
|
||||||
|
// Render using pure renderer
|
||||||
|
this.pathRenderer.drawDraggingLink(ctx, dragData, pathContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -397,8 +438,8 @@ export class LitegraphLinkAdapter {
|
|||||||
* Includes padding for line width and control points
|
* Includes padding for line width and control points
|
||||||
*/
|
*/
|
||||||
private calculateLinkBounds(
|
private calculateLinkBounds(
|
||||||
startPos: ReadOnlyPoint,
|
startPos: LitegraphPoint,
|
||||||
endPos: ReadOnlyPoint,
|
endPos: LitegraphPoint,
|
||||||
linkData: LinkRenderData
|
linkData: LinkRenderData
|
||||||
): Bounds {
|
): Bounds {
|
||||||
let minX = Math.min(startPos[0], endPos[0])
|
let minX = Math.min(startPos[0], endPos[0])
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export interface RenderContext {
|
|||||||
highlightedIds?: Set<string>
|
highlightedIds?: Set<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DragLinkData {
|
export interface DragLinkData {
|
||||||
/** Fixed end - the slot being dragged from */
|
/** Fixed end - the slot being dragged from */
|
||||||
fixedPoint: Point
|
fixedPoint: Point
|
||||||
fixedDirection: Direction
|
fixedDirection: Direction
|
||||||
@@ -605,6 +605,7 @@ export class CanvasPathRenderer {
|
|||||||
type: dragData.type,
|
type: dragData.type,
|
||||||
disabled: dragData.disabled
|
disabled: dragData.disabled
|
||||||
}
|
}
|
||||||
|
console.log({ linkData })
|
||||||
|
|
||||||
// Use standard link drawing
|
// Use standard link drawing
|
||||||
return this.drawLink(ctx, linkData, context)
|
return this.drawLink(ctx, linkData, context)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { computed, ref, toValue } from 'vue'
|
|||||||
import type { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'
|
import type { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'
|
||||||
import { LLink } from '@/lib/litegraph/src/LLink'
|
import { LLink } from '@/lib/litegraph/src/LLink'
|
||||||
import { Reroute } from '@/lib/litegraph/src/Reroute'
|
import { Reroute } from '@/lib/litegraph/src/Reroute'
|
||||||
import type { ReadOnlyPoint } from '@/lib/litegraph/src/interfaces'
|
import type { Point as LitegraphPoint } from '@/lib/litegraph/src/interfaces'
|
||||||
import { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'
|
import { LinkDirection } from '@/lib/litegraph/src/types/globalEnums'
|
||||||
import { LitegraphLinkAdapter } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
|
import { LitegraphLinkAdapter } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
|
||||||
import type { LinkRenderContext } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
|
import type { LinkRenderContext } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
|
||||||
@@ -113,7 +113,7 @@ export function useLinkLayoutSync() {
|
|||||||
|
|
||||||
// Special handling for floating input chain
|
// Special handling for floating input chain
|
||||||
const isFloatingInputChain = !sourceNode && targetNode
|
const isFloatingInputChain = !sourceNode && targetNode
|
||||||
const startControl: ReadOnlyPoint = isFloatingInputChain
|
const startControl: LitegraphPoint = isFloatingInputChain
|
||||||
? [0, 0]
|
? [0, 0]
|
||||||
: [dist * reroute.cos, dist * reroute.sin]
|
: [dist * reroute.cos, dist * reroute.sin]
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ export function useLinkLayoutSync() {
|
|||||||
(endPos[1] - lastReroute.pos[1]) ** 2
|
(endPos[1] - lastReroute.pos[1]) ** 2
|
||||||
)
|
)
|
||||||
const finalDist = Math.min(Reroute.maxSplineOffset, finalDistance * 0.25)
|
const finalDist = Math.min(Reroute.maxSplineOffset, finalDistance * 0.25)
|
||||||
const finalStartControl: ReadOnlyPoint = [
|
const finalStartControl: LitegraphPoint = [
|
||||||
finalDist * lastReroute.cos,
|
finalDist * lastReroute.cos,
|
||||||
finalDist * lastReroute.sin
|
finalDist * lastReroute.sin
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import type {
|
|||||||
INodeInputSlot,
|
INodeInputSlot,
|
||||||
INodeOutputSlot
|
INodeOutputSlot
|
||||||
} from '@/lib/litegraph/src/interfaces'
|
} from '@/lib/litegraph/src/interfaces'
|
||||||
import { RenderShape } from '@/lib/litegraph/src/litegraph'
|
import { Rectangle, RenderShape } from '@/lib/litegraph/src/litegraph'
|
||||||
import NodeContent from '@/renderer/extensions/vueNodes/components/NodeContent.vue'
|
import NodeContent from '@/renderer/extensions/vueNodes/components/NodeContent.vue'
|
||||||
import NodeHeader from '@/renderer/extensions/vueNodes/components/NodeHeader.vue'
|
import NodeHeader from '@/renderer/extensions/vueNodes/components/NodeHeader.vue'
|
||||||
import NodeSlots from '@/renderer/extensions/vueNodes/components/NodeSlots.vue'
|
import NodeSlots from '@/renderer/extensions/vueNodes/components/NodeSlots.vue'
|
||||||
@@ -85,7 +85,7 @@ const nodeData = computed<VueNodeData>(() => {
|
|||||||
name,
|
name,
|
||||||
type: input.type,
|
type: input.type,
|
||||||
shape: input.isOptional ? RenderShape.HollowCircle : undefined,
|
shape: input.isOptional ? RenderShape.HollowCircle : undefined,
|
||||||
boundingRect: [0, 0, 0, 0],
|
boundingRect: new Rectangle(0, 0, 0, 0),
|
||||||
link: null
|
link: null
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -94,13 +94,13 @@ const nodeData = computed<VueNodeData>(() => {
|
|||||||
return {
|
return {
|
||||||
name: output,
|
name: output,
|
||||||
type: output,
|
type: output,
|
||||||
boundingRect: [0, 0, 0, 0],
|
boundingRect: new Rectangle(0, 0, 0, 0),
|
||||||
links: []
|
links: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...output,
|
...output,
|
||||||
boundingRect: [0, 0, 0, 0],
|
boundingRect: new Rectangle(0, 0, 0, 0),
|
||||||
links: []
|
links: []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { createI18n } from 'vue-i18n'
|
|||||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||||
import type { INodeOutputSlot } from '@/lib/litegraph/src/interfaces'
|
import type { INodeOutputSlot } from '@/lib/litegraph/src/interfaces'
|
||||||
import type { INodeInputSlot } from '@/lib/litegraph/src/interfaces'
|
import type { INodeInputSlot } from '@/lib/litegraph/src/interfaces'
|
||||||
|
import { Rectangle } from '@/lib/litegraph/src/litegraph'
|
||||||
import enMessages from '@/locales/en/main.json' with { type: 'json' }
|
import enMessages from '@/locales/en/main.json' with { type: 'json' }
|
||||||
|
|
||||||
import NodeSlots from './NodeSlots.vue'
|
import NodeSlots from './NodeSlots.vue'
|
||||||
@@ -29,7 +30,7 @@ const makeNodeData = (overrides: Partial<VueNodeData> = {}): VueNodeData => ({
|
|||||||
interface StubSlotData {
|
interface StubSlotData {
|
||||||
name?: string
|
name?: string
|
||||||
type?: string
|
type?: string
|
||||||
boundingRect?: [number, number, number, number]
|
boundingRect?: Rectangle
|
||||||
}
|
}
|
||||||
|
|
||||||
const InputSlotStub = defineComponent({
|
const InputSlotStub = defineComponent({
|
||||||
@@ -96,13 +97,13 @@ describe('NodeSlots.vue', () => {
|
|||||||
const inputObjNoWidget = {
|
const inputObjNoWidget = {
|
||||||
name: 'objNoWidget',
|
name: 'objNoWidget',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
boundingRect: new Float32Array([0, 0, 0, 0]),
|
boundingRect: new Rectangle(0, 0, 0, 0),
|
||||||
link: null
|
link: null
|
||||||
}
|
}
|
||||||
const inputObjWithWidget = {
|
const inputObjWithWidget = {
|
||||||
name: 'objWithWidget',
|
name: 'objWithWidget',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
boundingRect: new Float32Array([0, 0, 0, 0]),
|
boundingRect: new Rectangle(0, 0, 0, 0),
|
||||||
widget: { name: 'objWithWidget' },
|
widget: { name: 'objWithWidget' },
|
||||||
link: null
|
link: null
|
||||||
}
|
}
|
||||||
@@ -150,13 +151,13 @@ describe('NodeSlots.vue', () => {
|
|||||||
const outputObj = {
|
const outputObj = {
|
||||||
name: 'outA',
|
name: 'outA',
|
||||||
type: 'any',
|
type: 'any',
|
||||||
boundingRect: new Float32Array([0, 0, 0, 0]),
|
boundingRect: new Rectangle(0, 0, 0, 0),
|
||||||
links: []
|
links: []
|
||||||
}
|
}
|
||||||
const outputObjB = {
|
const outputObjB = {
|
||||||
name: 'outB',
|
name: 'outB',
|
||||||
type: 'any',
|
type: 'any',
|
||||||
boundingRect: new Float32Array([0, 0, 0, 0]),
|
boundingRect: new Rectangle(0, 0, 0, 0),
|
||||||
links: []
|
links: []
|
||||||
}
|
}
|
||||||
const outputs: INodeOutputSlot[] = [outputObj, outputObjB]
|
const outputs: INodeOutputSlot[] = [outputObj, outputObjB]
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import { computed, onErrorCaptured, ref } from 'vue'
|
|||||||
|
|
||||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||||
import type { INodeSlot } from '@/lib/litegraph/src/litegraph'
|
import { type INodeSlot, Rectangle } from '@/lib/litegraph/src/litegraph'
|
||||||
import { isSlotObject } from '@/utils/typeGuardUtil'
|
import { isSlotObject } from '@/utils/typeGuardUtil'
|
||||||
|
|
||||||
import InputSlot from './InputSlot.vue'
|
import InputSlot from './InputSlot.vue'
|
||||||
@@ -60,28 +60,30 @@ const filteredInputs = computed(() => {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
.map((input) =>
|
.map(
|
||||||
isSlotObject(input)
|
(input): INodeSlot =>
|
||||||
? input
|
isSlotObject(input)
|
||||||
: ({
|
? input
|
||||||
name: typeof input === 'string' ? input : '',
|
: {
|
||||||
type: 'any',
|
name: typeof input === 'string' ? input : '',
|
||||||
boundingRect: [0, 0, 0, 0] as [number, number, number, number]
|
type: 'any',
|
||||||
} as INodeSlot)
|
boundingRect: new Rectangle(0, 0, 0, 0)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Outputs don't have widgets, so we don't need to filter them
|
// Outputs don't have widgets, so we don't need to filter them
|
||||||
const filteredOutputs = computed(() => {
|
const filteredOutputs = computed(() => {
|
||||||
const outputs = nodeData?.outputs || []
|
const outputs = nodeData?.outputs || []
|
||||||
return outputs.map((output) =>
|
return outputs.map(
|
||||||
isSlotObject(output)
|
(output): INodeSlot =>
|
||||||
? output
|
isSlotObject(output)
|
||||||
: ({
|
? output
|
||||||
name: typeof output === 'string' ? output : '',
|
: {
|
||||||
type: 'any',
|
name: typeof output === 'string' ? output : '',
|
||||||
boundingRect: [0, 0, 0, 0] as [number, number, number, number]
|
type: 'any',
|
||||||
} as INodeSlot)
|
boundingRect: new Rectangle(0, 0, 0, 0)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
:slot-data="{
|
:slot-data="{
|
||||||
name: widget.name,
|
name: widget.name,
|
||||||
type: widget.type,
|
type: widget.type,
|
||||||
boundingRect: [0, 0, 0, 0]
|
boundingRect
|
||||||
}"
|
}"
|
||||||
:node-id="nodeData?.id != null ? String(nodeData.id) : ''"
|
:node-id="nodeData?.id != null ? String(nodeData.id) : ''"
|
||||||
:index="getWidgetInputIndex(widget)"
|
:index="getWidgetInputIndex(widget)"
|
||||||
@@ -60,6 +60,7 @@ import type {
|
|||||||
VueNodeData
|
VueNodeData
|
||||||
} from '@/composables/graph/useGraphNodeManager'
|
} from '@/composables/graph/useGraphNodeManager'
|
||||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||||
|
import { Rectangle } from '@/lib/litegraph/src/litegraph'
|
||||||
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
||||||
import { useNodeTooltips } from '@/renderer/extensions/vueNodes/composables/useNodeTooltips'
|
import { useNodeTooltips } from '@/renderer/extensions/vueNodes/composables/useNodeTooltips'
|
||||||
// Import widget components directly
|
// Import widget components directly
|
||||||
@@ -164,6 +165,8 @@ const processedWidgets = computed((): ProcessedWidget[] => {
|
|||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const boundingRect = new Rectangle(0, 0, 0, 0)
|
||||||
|
|
||||||
// TODO: Refactor to avoid O(n) lookup - consider storing input index on widget creation
|
// TODO: Refactor to avoid O(n) lookup - consider storing input index on widget creation
|
||||||
// or restructuring data model to unify widgets and inputs
|
// or restructuring data model to unify widgets and inputs
|
||||||
// Map a widget to its corresponding input slot index
|
// Map a widget to its corresponding input slot index
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ReadOnlyRect } from '@/lib/litegraph/src/interfaces'
|
import type { Rect } from '@/lib/litegraph/src/interfaces'
|
||||||
import type { Bounds } from '@/renderer/core/layout/types'
|
import type { Bounds } from '@/renderer/core/layout/types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,9 +33,7 @@ export const lcm = (a: number, b: number): number => {
|
|||||||
* @param rectangles - Array of rectangle tuples in [x, y, width, height] format
|
* @param rectangles - Array of rectangle tuples in [x, y, width, height] format
|
||||||
* @returns Bounds object with union rectangle, or null if no rectangles provided
|
* @returns Bounds object with union rectangle, or null if no rectangles provided
|
||||||
*/
|
*/
|
||||||
export function computeUnionBounds(
|
export function computeUnionBounds(rectangles: readonly Rect[]): Bounds | null {
|
||||||
rectangles: readonly ReadOnlyRect[]
|
|
||||||
): Bounds | null {
|
|
||||||
const n = rectangles.length
|
const n = rectangles.length
|
||||||
if (n === 0) {
|
if (n === 0) {
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, vi } from 'vitest'
|
import { afterEach, beforeEach, describe, expect, vi } from 'vitest'
|
||||||
|
|
||||||
import type { INodeInputSlot, Point } from '@/lib/litegraph/src/litegraph'
|
import type { INodeInputSlot, Point } from '@/lib/litegraph/src/litegraph'
|
||||||
|
import { Rectangle } from '@/lib/litegraph/src/litegraph'
|
||||||
import { LGraphNode, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
import { LGraphNode, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import { LGraph } from '@/lib/litegraph/src/litegraph'
|
import { LGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import { NodeInputSlot } from '@/lib/litegraph/src/litegraph'
|
import { NodeInputSlot } from '@/lib/litegraph/src/litegraph'
|
||||||
@@ -84,8 +85,8 @@ describe('LGraphNode', () => {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
node.configure(configureData)
|
node.configure(configureData)
|
||||||
expect(node.pos).toEqual(new Float32Array([50, 60]))
|
expect(node.pos).toEqual([50, 60])
|
||||||
expect(node.size).toEqual(new Float32Array([70, 80]))
|
expect(node.size).toEqual([70, 80])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should configure inputs correctly', () => {
|
test('should configure inputs correctly', () => {
|
||||||
@@ -571,7 +572,7 @@ describe('LGraphNode', () => {
|
|||||||
name: 'test_in',
|
name: 'test_in',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
link: null,
|
link: null,
|
||||||
boundingRect: new Float32Array([0, 0, 0, 0])
|
boundingRect: new Rectangle(0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
test('should return position based on title height when collapsed', () => {
|
test('should return position based on title height when collapsed', () => {
|
||||||
@@ -594,7 +595,7 @@ describe('LGraphNode', () => {
|
|||||||
name: 'test_in_2',
|
name: 'test_in_2',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
link: null,
|
link: null,
|
||||||
boundingRect: new Float32Array([0, 0, 0, 0])
|
boundingRect: new Rectangle(0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
node.inputs = [inputSlot, inputSlot2]
|
node.inputs = [inputSlot, inputSlot2]
|
||||||
const slotIndex = 0
|
const slotIndex = 0
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ exports[`LGraph > supports schema v0.4 graphs > oldSchemaGraph 1`] = `
|
|||||||
LGraph {
|
LGraph {
|
||||||
"_groups": [
|
"_groups": [
|
||||||
LGraphGroup {
|
LGraphGroup {
|
||||||
"_bounding": Float32Array [
|
"_bounding": [
|
||||||
20,
|
10,
|
||||||
20,
|
10,
|
||||||
1,
|
140,
|
||||||
3,
|
80,
|
||||||
],
|
],
|
||||||
"_children": Set {},
|
"_children": Set {},
|
||||||
"_nodes": [],
|
"_nodes": [],
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
20,
|
20,
|
||||||
20,
|
20,
|
||||||
],
|
],
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
1,
|
1,
|
||||||
3,
|
3,
|
||||||
],
|
],
|
||||||
@@ -39,19 +39,19 @@ LGraph {
|
|||||||
LGraphNode {
|
LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -111,19 +111,19 @@ LGraph {
|
|||||||
"1": LGraphNode {
|
"1": LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
@@ -184,19 +184,19 @@ LGraph {
|
|||||||
LGraphNode {
|
LGraphNode {
|
||||||
"_collapsed_width": undefined,
|
"_collapsed_width": undefined,
|
||||||
"_level": undefined,
|
"_level": undefined,
|
||||||
"_pos": Float32Array [
|
"_pos": [
|
||||||
10,
|
10,
|
||||||
10,
|
10,
|
||||||
],
|
],
|
||||||
"_posSize": Float32Array [
|
"_posSize": [
|
||||||
10,
|
0,
|
||||||
10,
|
0,
|
||||||
140,
|
0,
|
||||||
60,
|
0,
|
||||||
],
|
],
|
||||||
"_relative_id": undefined,
|
"_relative_id": undefined,
|
||||||
"_shape": undefined,
|
"_shape": undefined,
|
||||||
"_size": Float32Array [
|
"_size": [
|
||||||
140,
|
140,
|
||||||
60,
|
60,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// TODO: Fix these tests after migration
|
// TODO: Fix these tests after migration
|
||||||
import { test as baseTest } from 'vitest'
|
import { test as baseTest } from 'vitest'
|
||||||
|
|
||||||
|
import { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'
|
||||||
import type { Point, Rect } from '@/lib/litegraph/src/interfaces'
|
import type { Point, Rect } from '@/lib/litegraph/src/interfaces'
|
||||||
import {
|
import {
|
||||||
addDirectionalOffset,
|
addDirectionalOffset,
|
||||||
@@ -132,8 +133,8 @@ test('snapPoint correctly snaps points to grid', ({ expect }) => {
|
|||||||
|
|
||||||
test('createBounds correctly creates bounding box', ({ expect }) => {
|
test('createBounds correctly creates bounding box', ({ expect }) => {
|
||||||
const objects = [
|
const objects = [
|
||||||
{ boundingRect: [0, 0, 10, 10] as Rect },
|
{ boundingRect: new Rectangle(0, 0, 10, 10) },
|
||||||
{ boundingRect: [5, 5, 10, 10] as Rect }
|
{ boundingRect: new Rectangle(5, 5, 10, 10) }
|
||||||
]
|
]
|
||||||
|
|
||||||
const defaultBounds = createBounds(objects)
|
const defaultBounds = createBounds(objects)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
import type { ReadOnlyRect } from '@/lib/litegraph/src/interfaces'
|
import type { Rect } from '@/lib/litegraph/src/interfaces'
|
||||||
import { computeUnionBounds, gcd, lcm } from '@/utils/mathUtil'
|
import { computeUnionBounds, gcd, lcm } from '@/utils/mathUtil'
|
||||||
|
|
||||||
describe('mathUtil', () => {
|
describe('mathUtil', () => {
|
||||||
@@ -27,9 +27,9 @@ describe('mathUtil', () => {
|
|||||||
expect(computeUnionBounds([])).toBe(null)
|
expect(computeUnionBounds([])).toBe(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Tests for tuple format (ReadOnlyRect)
|
// Tests for tuple format (Rect)
|
||||||
it('should work with ReadOnlyRect tuple format', () => {
|
it('should work with Rect tuple format', () => {
|
||||||
const tuples: ReadOnlyRect[] = [
|
const tuples: Rect[] = [
|
||||||
[10, 20, 30, 40] as const, // bounds: 10,20 to 40,60
|
[10, 20, 30, 40] as const, // bounds: 10,20 to 40,60
|
||||||
[50, 10, 20, 30] as const // bounds: 50,10 to 70,40
|
[50, 10, 20, 30] as const // bounds: 50,10 to 70,40
|
||||||
]
|
]
|
||||||
@@ -44,8 +44,8 @@ describe('mathUtil', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle single ReadOnlyRect tuple', () => {
|
it('should handle single Rect tuple', () => {
|
||||||
const tuple: ReadOnlyRect = [10, 20, 30, 40] as const
|
const tuple: Rect = [10, 20, 30, 40] as const
|
||||||
const result = computeUnionBounds([tuple])
|
const result = computeUnionBounds([tuple])
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
@@ -57,7 +57,7 @@ describe('mathUtil', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle tuple format with negative dimensions', () => {
|
it('should handle tuple format with negative dimensions', () => {
|
||||||
const tuples: ReadOnlyRect[] = [
|
const tuples: Rect[] = [
|
||||||
[100, 50, -20, -10] as const, // x+width=80, y+height=40
|
[100, 50, -20, -10] as const, // x+width=80, y+height=40
|
||||||
[90, 45, 15, 20] as const // x+width=105, y+height=65
|
[90, 45, 15, 20] as const // x+width=105, y+height=65
|
||||||
]
|
]
|
||||||
@@ -74,7 +74,7 @@ describe('mathUtil', () => {
|
|||||||
|
|
||||||
it('should maintain optimal performance with SoA tuples', () => {
|
it('should maintain optimal performance with SoA tuples', () => {
|
||||||
// Test that array access is as expected for typical selection sizes
|
// Test that array access is as expected for typical selection sizes
|
||||||
const tuples: ReadOnlyRect[] = Array.from(
|
const tuples: Rect[] = Array.from(
|
||||||
{ length: 10 },
|
{ length: 10 },
|
||||||
(_, i) =>
|
(_, i) =>
|
||||||
[
|
[
|
||||||
|
|||||||
Reference in New Issue
Block a user