Skip to content

Commit

Permalink
can now organize specifically your selection
Browse files Browse the repository at this point in the history
  • Loading branch information
EliCDavis committed Sep 23, 2024
1 parent b2492c4 commit c6f5ff4
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ export class NodeFlowGraph {
}

connectedInputsNodeReferences(nodeIndex: number): Array<FlowNode> {
return this.#mainNodeSubsystem.connectedInputsNodeReferences(nodeIndex);
return this.#mainNodeSubsystem.connectedInputsNodeReferencesByIndex(nodeIndex);
}

connectedOutputsNodeReferences(nodeIndex: number): Array<FlowNode> {
Expand Down
125 changes: 95 additions & 30 deletions src/nodes/subsystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ export class NodeSubsystem {
this.#nodeFactory.addPublisher(identifier, publisher);
}


clickStart(mousePosition: Vector2, ctrlKey: boolean): boolean {
let hoveringSomething = false;
if (this.#nodeHovering > -1) {
Expand Down Expand Up @@ -266,8 +265,11 @@ export class NodeSubsystem {
*
* @param nodeIndex index of the node to examine inputs to
*/
connectedInputsNodeReferences(nodeIndex: number): Array<FlowNode> {
const node = this.#nodes[nodeIndex]
connectedInputsNodeReferencesByIndex(nodeIndex: number): Array<FlowNode> {
return this.connectedInputsNodeReferences(this.#nodes[nodeIndex]);
}

connectedInputsNodeReferences(node: FlowNode): Array<FlowNode> {
const connections = new Array<FlowNode>();
for (let i = 0; i < this.#connections.length; i++) {
const connection = this.#connections[i];
Expand Down Expand Up @@ -363,49 +365,99 @@ export class NodeSubsystem {
Organize(ctx, this);
}

#nodesSelected(): Array<number> {
const selected = new Array<number>();
for (let i = 0; i < this.#nodes.length; i++) {
if (this.#nodes[i].selected()) {
selected.push(i);
}
}
return selected;
}

#organizeSelected(ctx: CanvasRenderingContext2D): void {
Organize(ctx, this, this.#nodesSelected());
}

openContextMenu(ctx: CanvasRenderingContext2D, position: Vector2): ContextMenuConfig | null {

let config: ContextMenuConfig = {
const organizeNodesSubMenu: ContextMenuConfig = {
name: "Organize",
group: nodeFlowGroup,
items: [
{
name: "Organize All Nodes",
name: "All Nodes",
group: nodeFlowGroup,
callback: () => {
this.organize(ctx);
}
}
],
]
}

let config: ContextMenuConfig = {
items: [],
subMenus: [
organizeNodesSubMenu,
this.#nodeFactory.openMenu(this, position)
]
}

if (this.#nodesSelected().length > 0) {
organizeNodesSubMenu.items?.push({
name: "Selected Nodes",
group: nodeFlowGroup,
callback: () => {
this.#organizeSelected(ctx)
}
})
}

if (this.#nodeHovering > -1) {
const nodeToReview = this.#nodeHovering;

config.items?.push(
{
name: "Delete Node",
group: nodeFlowGroup,
callback: () => {
this.#removeNodeByIndex(nodeToReview);
}
},
{
name: "Select Connected Nodes",
group: nodeFlowGroup,
callback: () => {
this.#selectConnectedNodes(nodeToReview);
}
},
{
name: "Clear Node Connections",
group: nodeFlowGroup,
callback: () => {
this.#removeNodeConnections(nodeToReview);
const nodeToReviewNode = this.#nodes[nodeToReview];

config.subMenus?.push({
group: nodeFlowGroup,
name: "Select",
items: [
{
name: "Direct Connected Nodes",
group: nodeFlowGroup,
callback: () => {
this.#selectConnectedNodes(nodeToReview);
}
},
{
name: "Input Nodes + Descendents",
group: nodeFlowGroup,
callback: () => {
this.#selectInputNodesAndDescendents(nodeToReviewNode);
}
},
]
})

config.subMenus?.push({
group: nodeFlowGroup,
name: "Delete",
items: [
{
name: "Node",
group: nodeFlowGroup,
callback: () => {
this.#removeNodeByIndex(nodeToReview);
}
},
{
name: "Connections",
group: nodeFlowGroup,
callback: () => {
this.#removeNodeConnections(nodeToReview);
}
}
}
);
]
})

if (this.#nodes[nodeToReview].locked()) {
config.items?.push({
Expand All @@ -429,18 +481,31 @@ export class NodeSubsystem {
return config;
}

#selectInputNodesAndDescendents(node: FlowNode): void {
if (node === undefined) {
return;
}
this.#selectNode(node, false);

const inputs = this.connectedInputsNodeReferences(node);
for (let i = 0; i < inputs.length; i++) {
this.#selectInputNodesAndDescendents(inputs[i]);
}
}

#selectConnectedNodes(nodeIndex: number): void {
const node = this.#nodes[nodeIndex];
if (node === undefined) {
return;
}
this.#selectNode(node, false);

const outputs = this.connectedOutputsNodeReferences(nodeIndex);
for (let i = 0; i < outputs.length; i++) {
this.#selectNode(outputs[i], false);
}

const inputs = this.connectedInputsNodeReferences(nodeIndex);
const inputs = this.connectedInputsNodeReferencesByIndex(nodeIndex);
for (let i = 0; i < inputs.length; i++) {
this.#selectNode(inputs[i], false);
}
Expand Down
66 changes: 48 additions & 18 deletions src/organize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,54 @@ import { Box } from "./types/box";

// TODO: Cyclical dependencies will end our life

function MarkInputs(graph: NodeSubsystem, positions: Array<number>, nodeLUT: Map<FlowNode, number>, node: number, depth: number) {
const inputs = graph.connectedInputsNodeReferences(node);
function MarkInputs(graph: NodeSubsystem, positions: Array<number>, nodeLUT: Map<FlowNode, number>, node: number, depth: number, shouldSort: Map<FlowNode, boolean>) {
const inputs = graph.connectedInputsNodeReferencesByIndex(node);
for (let i = 0; i < inputs.length; i++) {
const nodeIndex = nodeLUT.get(inputs[i]) as number;

if (!shouldSort.has(inputs[i])) {
continue;
}

positions[nodeIndex] = depth;
MarkInputs(graph, positions, nodeLUT, nodeIndex, depth - 1);
MarkInputs(graph, positions, nodeLUT, nodeIndex, depth - 1, shouldSort);
}
}

function MarkOutputs(graph: NodeSubsystem, positions: Array<number>, nodeLUT: Map<FlowNode, number>, node: number, depth: number) {
const outputs = graph.connectedInputsNodeReferences(node);
function MarkOutputs(graph: NodeSubsystem, positions: Array<number>, nodeLUT: Map<FlowNode, number>, node: number, depth: number, shouldSort: Map<FlowNode, boolean>) {
const outputs = graph.connectedInputsNodeReferencesByIndex(node);
for (let i = 0; i < outputs.length; i++) {
const nodeIndex = nodeLUT.get(outputs[i]) as number;

if (!shouldSort.has(outputs[i])) {
continue;
}

positions[nodeIndex] = depth;
MarkOutputs(graph, positions, nodeLUT, nodeIndex, depth + 1);
MarkOutputs(graph, positions, nodeLUT, nodeIndex, depth + 1, shouldSort);
}
}

export function Organize(ctx: CanvasRenderingContext2D, graph: NodeSubsystem): void {
export function Organize(ctx: CanvasRenderingContext2D, graph: NodeSubsystem, nodesToSort?: Array<number>): void {
const nodes = graph.getNodes();
const nodeLUT = new Map<FlowNode, number>();
const bounds = new Array<Box>(nodes.length);
const relativePosition = new Array<Array<number>>(nodes.length);
const claimed = new Array<boolean>(nodes.length);
const shouldSort = new Map<FlowNode, boolean>(); // TODO: we don't really need a Map, is their a Set data structure?

if (nodesToSort) {
if (nodesToSort.length < 2) {
return;
}
for (let i = 0; i < nodesToSort.length; i++) {
shouldSort.set(nodes[nodesToSort[i]], true);
}
} else {
for (let i = 0; i < nodes.length; i++) {
shouldSort.set(nodes[i], true);
}
}

// Initialize everything
for (let i = 0; i < nodes.length; i++) {
Expand All @@ -40,8 +64,8 @@ export function Organize(ctx: CanvasRenderingContext2D, graph: NodeSubsystem): v

for (let i = 0; i < nodes.length; i++) {
relativePosition[i][i] = 0;
MarkInputs(graph, relativePosition[i], nodeLUT, i, -1);
MarkOutputs(graph, relativePosition[i], nodeLUT, i, 1);
MarkInputs(graph, relativePosition[i], nodeLUT, i, -1, shouldSort);
MarkOutputs(graph, relativePosition[i], nodeLUT, i, 1, shouldSort);
}

interface entry {
Expand All @@ -51,9 +75,14 @@ export function Organize(ctx: CanvasRenderingContext2D, graph: NodeSubsystem): v
max: number
}

let entries = new Array<entry>(nodes.length);
let entries = new Array<entry>(shouldSort.size);

let nodeIndex = 0;
for (let i = 0; i < nodes.length; i++) {
if (!shouldSort.has(nodes[i])) {
continue;
}

let min = 0;
let max = 0;
for (let x = 0; x < nodes.length; x++) {
Expand All @@ -65,12 +94,13 @@ export function Organize(ctx: CanvasRenderingContext2D, graph: NodeSubsystem): v
min = Math.min(min, val);
max = Math.max(max, val);
}
entries[i] = {
entries[nodeIndex] = {
length: max - min,
node: i,
min: min,
max: max
}
nodeIndex++
}


Expand All @@ -83,7 +113,7 @@ export function Organize(ctx: CanvasRenderingContext2D, graph: NodeSubsystem): v

const columns = Array<Column>(entries[0].length + 1);
for (let i = 0; i < columns.length; i++) {
columns[i] = {
columns[i] = {
Nodes: new Array<FlowNode>(),
Width: 0
};
Expand All @@ -106,11 +136,11 @@ export function Organize(ctx: CanvasRenderingContext2D, graph: NodeSubsystem): v
if (claimed[p] === true) {
continue;
}

const nodeBounds = bounds[p];
const column = columns[position - entry.min];
const column = columns[position - entry.min];
column.Nodes.push(nodes[p])
column.Width = Math.max(column.Width, nodeBounds.Size.x)
column.Width = Math.max(column.Width, nodeBounds.Size.x)

claimed[p] = true;
}
Expand All @@ -134,13 +164,13 @@ export function Organize(ctx: CanvasRenderingContext2D, graph: NodeSubsystem): v
for (let n = 0; n < column.Nodes.length; n++) {
const node = column.Nodes[n];
const nodeBounds = bounds[nodeLUT.get(node) as number];

let pos = {
x: widthOffset + allColumnsWidths + (columns.length * widthSpacing),
y: heightOffset
}
heightOffset += nodeBounds.Size.y + heightSpacing

heightOffset += nodeBounds.Size.y + heightSpacing
node.setPosition(pos);
}
}
Expand Down

0 comments on commit c6f5ff4

Please sign in to comment.