Files
CoreyCorza-ComfyUI-CRZnodes/js/Compare.js
T
2025-09-05 22:43:44 +10:00

269 lines
12 KiB
JavaScript

// ComfyUI.CRZ.Compare - Compare two values and output boolean
import { app } from "../../scripts/app.js";
import {
NODE_BACKGROUND_COLOR,
LABEL_COLOR,
INACTIVE_LABEL_COLOR,
VALUE_COLOR,
INACTIVE_VALUE_COLOR,
TOGGLE_HEIGHT,
TOGGLE_CORNER_RADIUS,
HANDLE_SIZE,
HANDLE_CORNER_RADIUS,
HANDLE_PADDING
} from "./CRZConfig.js";
// Compare node dimensions
const COMPARE_WIDTH = 100;
const COMPARE_HEIGHT = 48;
app.registerExtension({
name: "CRZCompare",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "CRZCompare") {
// Hide the title bar
nodeType.title_mode = LiteGraph.NO_TITLE;
nodeType.prototype.getTitle = function() { return ""; };
nodeType.prototype.onDrawTitle = function() { return false; };
// Set fixed size and disable resizing
nodeType.min_size = [COMPARE_WIDTH, COMPARE_HEIGHT];
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
if (onNodeCreated) {
onNodeCreated.apply(this, arguments);
}
// Set background color
this.bgcolor = NODE_BACKGROUND_COLOR;
// Set fixed size and disable resizing
this.size = [COMPARE_WIDTH, COMPARE_HEIGHT];
this.resizable = false;
// Initialize operator widget for syncing with Python
if (this.widgets && this.widgets.length > 0) {
// Find or create the operator widget
let operatorWidget = this.widgets.find(w => w.name === "operator");
if (!operatorWidget) {
operatorWidget = this.addWidget("text", "operator", "=", function(v) {
this.properties.operator = v;
});
}
operatorWidget.value = this.properties.operator || "=";
operatorWidget.hidden = true;
operatorWidget.type = "hidden";
// Hide other widgets
for (let widget of this.widgets) {
if (widget !== operatorWidget) {
widget.hidden = true;
}
}
} else {
// Manually create the operator widget
const operatorWidget = this.addWidget("text", "operator", "=", function(v) {
this.properties.operator = v;
});
operatorWidget.hidden = true;
operatorWidget.type = "hidden";
}
// Set input and output labels for clarity
this.onAdded = function() {
if (this.inputs && this.inputs.length >= 2) {
this.inputs[0].name = "a";
this.inputs[0].localized_name = "A";
this.inputs[1].name = "b";
this.inputs[1].localized_name = "B";
}
// Hide output socket label but keep the boolean indicator
if (this.outputs && this.outputs[0]) {
this.outputs[0].name = this.outputs[0].localized_name = "";
}
// Move widgets off-screen
this.widgets_start_y = -2.4e8 * LiteGraph.NODE_SLOT_HEIGHT;
};
// Ensure labels are set on configure too
const originalOnConfigure = this.onConfigure;
this.onConfigure = function(info) {
if (originalOnConfigure) {
originalOnConfigure.call(this, info);
}
if (this.inputs && this.inputs.length >= 2) {
this.inputs[0].name = "a";
this.inputs[0].localized_name = "A";
this.inputs[1].name = "b";
this.inputs[1].localized_name = "B";
}
if (this.outputs && this.outputs[0]) {
this.outputs[0].name = this.outputs[0].localized_name = "";
}
};
// Initialize comparison operator property
this.properties = this.properties || {};
this.properties.operator = this.properties.operator || "=";
// Double-click handler for configuration
this.onDblClick = function(e, pos, canvas) {
const currentOp = this.properties.operator || "=";
canvas.prompt(
"Comparison Operator (=, >, <, >=, <=)",
currentOp,
function(newOp) {
if (newOp && (newOp === "=" || newOp === ">" || newOp === "<" || newOp === ">=" || newOp === "<=")) {
this.properties.operator = newOp;
// Update the hidden widget to sync with Python
const operatorWidget = this.widgets.find(w => w.name === "operator");
if (operatorWidget) {
operatorWidget.value = newOp;
}
// Force redraw to show new operator
if (this.graph && this.graph.setDirtyCanvas) {
this.graph.setDirtyCanvas(true);
}
}
}.bind(this),
e
);
return true;
};
// Mark as CRZ node for connection hiding
this.isCRZNode = true;
}
// Override computeSize to force fixed size
nodeType.prototype.computeSize = function() {
return [COMPARE_WIDTH, COMPARE_HEIGHT];
};
// Custom drawing for the compare visualization
nodeType.prototype.onDrawForeground = function(ctx) {
if (this.flags.collapsed) return;
// Get input values for comparison preview
const inputA = this.inputs?.[0];
const inputB = this.inputs?.[1];
const output = this.outputs?.[0];
// Check if inputs are connected
const aConnected = inputA && inputA.link != null;
const bConnected = inputB && inputB.link != null;
const outputConnected = output && output.links && output.links.length > 0;
// Determine if node is active
const isActive = aConnected || bConnected || outputConnected;
// Choose colors based on active status
const labelColor = isActive ? LABEL_COLOR : INACTIVE_LABEL_COLOR;
const valueColor = isActive ? VALUE_COLOR : INACTIVE_VALUE_COLOR;
// Draw result indicator (boolean toggle-style)
const toggleWidth = 30;
const toggleHeight = TOGGLE_HEIGHT;
const toggleLeft = (this.size[0] - toggleWidth) / 2 + 8;
const toggleY = this.size[1] / 2 - 8;
// Try to get actual comparison result for preview
let previewResult = false;
if (aConnected && bConnected) {
try {
// Try to get the actual input values from connected nodes
const aLink = this.graph.links[inputA.link];
const bLink = this.graph.links[inputB.link];
if (aLink && bLink) {
const aNode = this.graph.getNodeById(aLink.origin_id);
const bNode = this.graph.getNodeById(bLink.origin_id);
// Try to get output values (this may not always work)
let aValue = null;
let bValue = null;
// For basic nodes, try to get widget values
if (aNode && aNode.widgets && aNode.widgets.length > 0) {
aValue = aNode.widgets[0].value;
}
if (bNode && bNode.widgets && bNode.widgets.length > 0) {
bValue = bNode.widgets[0].value;
}
// If we got both values, compare them based on operator
if (aValue !== null && bValue !== null) {
const operator = this.properties.operator || "=";
switch (operator) {
case ">":
previewResult = (Number(aValue) > Number(bValue));
break;
case "<":
previewResult = (Number(aValue) < Number(bValue));
break;
case ">=":
previewResult = (Number(aValue) >= Number(bValue));
break;
case "<=":
previewResult = (Number(aValue) <= Number(bValue));
break;
case "=":
default:
previewResult = (aValue == bValue);
break;
}
} else {
// Fallback: show true when both connected but can't get values
previewResult = true;
}
}
} catch (e) {
// If anything fails, show true when both connected
previewResult = true;
}
} else {
// Not fully connected, show false
previewResult = false;
}
// Draw toggle background
ctx.fillStyle = isActive ?
(previewResult ? "rgba(106, 194, 65, 0.281)" : "rgba(226, 74, 74, 0.281)") :
"#2b2b2b";
ctx.beginPath();
ctx.roundRect(toggleLeft, toggleY, toggleWidth, toggleHeight, TOGGLE_CORNER_RADIUS);
ctx.fill();
// Draw toggle handle
const handleX = previewResult ?
toggleLeft + toggleWidth - HANDLE_SIZE - HANDLE_PADDING :
toggleLeft + HANDLE_PADDING;
const handleY = toggleY + (toggleHeight - HANDLE_SIZE) / 2;
ctx.fillStyle = isActive ? "rgba(122, 122, 122, 0.562)" : "#555";
ctx.beginPath();
ctx.roundRect(handleX, handleY, HANDLE_SIZE, HANDLE_SIZE, HANDLE_CORNER_RADIUS);
ctx.fill();
// Draw the operator text
const operator = this.properties.operator || "=";
ctx.fillStyle = labelColor;
ctx.font = "12px Arial";
ctx.textAlign = "center";
// ctx.textBaseline = "middle";
// Position the operator text above the toggle
const textX = this.size[0] / 2+7;
const textY = this.size[1] / 2 +4;
ctx.fillText(operator, textX, textY);
};
}
}
});