From d8badc53205faf2eeccedaa3f56adce77ffae320 Mon Sep 17 00:00:00 2001 From: "Alexander G. Morano" Date: Sun, 4 May 2025 03:32:02 -0400 Subject: [PATCH] op_unary fixed factorial added fill array mode for op unary --- core/calc.py | 74 ++++++++++++++++++++-------------------- core/vars.py | 2 -- web/nodes/akashic.js | 2 +- web/nodes/array.js | 2 +- web/nodes/calc_binary.js | 2 +- web/nodes/delay.js | 2 +- web/nodes/flatten.js | 3 +- web/nodes/graph.js | 2 +- web/nodes/lerp.js | 2 +- web/nodes/op_unary.js | 48 ++++++++++++++++++++++++++ web/nodes/queue.js | 2 +- web/nodes/route.js | 2 +- web/nodes/stack.js | 2 +- web/nodes/stringer.js | 18 ++++++++++ web/nodes/tick.js | 2 +- web/nodes/value.js | 2 +- 16 files changed, 115 insertions(+), 52 deletions(-) create mode 100644 web/nodes/op_unary.js create mode 100644 web/nodes/stringer.js diff --git a/core/calc.py b/core/calc.py index cf68989..efd7b97 100644 --- a/core/calc.py +++ b/core/calc.py @@ -160,7 +160,7 @@ OP_UNARY = { EnumUnaryOperation.TAN: lambda x: math.tan(x), EnumUnaryOperation.NEGATE: lambda x: -x, EnumUnaryOperation.RECIPROCAL: lambda x: 1 / x if x != 0 else 0, - EnumUnaryOperation.FACTORIAL: lambda x: math.factorial(math.abs(int(x))), + EnumUnaryOperation.FACTORIAL: lambda x: math.factorial(abs(int(x))), EnumUnaryOperation.EXP: lambda x: math.exp(x), EnumUnaryOperation.NOT: lambda x: not x, EnumUnaryOperation.BIT_NOT: lambda x: ~int(x), @@ -216,6 +216,7 @@ class BitSplitNode(CozyBaseNode): CATEGORY = JOV_CATEGORY RETURN_TYPES = (COZY_TYPE_ANY, "BOOLEAN",) RETURN_NAMES = ("BIT", "BOOL",) + OUTPUT_IS_LIST = (True, True,) OUTPUT_TOOLTIPS = ( "Bits as Numerical output (0 or 1)", "Bits as Boolean output (True or False)" @@ -272,6 +273,7 @@ class ComparisonNode(CozyBaseNode): CATEGORY = JOV_CATEGORY RETURN_TYPES = (COZY_TYPE_ANY, COZY_TYPE_ANY,) RETURN_NAMES = ("OUT", "VAL",) + OUTPUT_IS_LIST = (True, True,) OUTPUT_TOOLTIPS = ( "Outputs the input at PASS or FAIL depending the evaluation", "The comparison result value" @@ -405,6 +407,7 @@ class LerpNode(CozyBaseNode): CATEGORY = JOV_CATEGORY RETURN_TYPES = (COZY_TYPE_ANY,) RETURN_NAMES = ("🦄",) + OUTPUT_IS_LIST = (True,) OUTPUT_TOOLTIPS = ( f"Output can vary depending on the type chosen in the {"TYPE"} parameter" ) @@ -510,6 +513,7 @@ class OPUnaryNode(CozyBaseNode): CATEGORY = JOV_CATEGORY RETURN_TYPES = (COZY_TYPE_ANY,) RETURN_NAMES = ("❔",) + OUTPUT_IS_LIST = (True,) OUTPUT_TOOLTIPS = ( "Output type will match the input type" ) @@ -521,12 +525,19 @@ Perform single function operations like absolute value, mean, median, mode, magn @classmethod def INPUT_TYPES(cls) -> InputType: d = super().INPUT_TYPES() + typ = EnumConvertType._member_names_[:6] d = deep_merge(d, { "optional": { "A": (COZY_TYPE_NUMERICAL, { "default": None}), "FUNCTION": (EnumUnaryOperation._member_names_, { - "default": EnumUnaryOperation.ABS.name}) + "default": EnumUnaryOperation.ABS.name}), + "TYPE": (typ, { + "default": EnumConvertType.FLOAT.name, + "tooltip":"Take the input and convert it into the selected type"}), + "FILL": ("BOOLEAN", { + "default": False, + "tooltip":"If the value should fill the output type (VEC*)"}), } }) return d @@ -535,24 +546,16 @@ Perform single function operations like absolute value, mean, median, mode, magn results = [] A = parse_param(kw, "A", EnumConvertType.ANY, 0) op = parse_param(kw, "FUNCTION", EnumUnaryOperation, EnumUnaryOperation.ABS.name) - params = list(zip_longest_fill(A, op)) + out = parse_param(kw, "TYPE", EnumConvertType, EnumConvertType.FLOAT.name) + fill = parse_param(kw, "FILL", EnumConvertType.BOOLEAN, False) + params = list(zip_longest_fill(A, op, out, fill)) pbar = ProgressBar(len(params)) - for idx, (A, op) in enumerate(params): - typ = EnumConvertType.ANY - if isinstance(A, (str, )): - typ = EnumConvertType.STRING - elif isinstance(A, (bool, )): + for idx, (A, op, out, fill) in enumerate(params): + typ = EnumConvertType.FLOAT + if isinstance(A, (bool, )): typ = EnumConvertType.BOOLEAN - elif isinstance(A, (int, )): - typ = EnumConvertType.INT - elif isinstance(A, (float, )): - typ = EnumConvertType.FLOAT elif isinstance(A, (list, set, tuple,)): typ = EnumConvertType(len(A) * 10) - elif isinstance(A, (dict,)): - typ = EnumConvertType.DICT - elif isinstance(A, (TensorType,)): - typ = EnumConvertType.IMAGE val = parse_value(A, typ, 0) if not isinstance(val, (list, tuple, )): @@ -593,17 +596,11 @@ Perform single function operations like absolute value, mean, median, mode, magn ret.append(v) val = ret - convert = int if isinstance(A, (bool, int, np.uint8, np.uint16, np.uint32, np.uint64)) else float - ret = [] - for v in val: - try: - ret.append(convert(v)) - except OverflowError: - ret.append(0) - except Exception as e: - logger.error(f"{e} :: {op}") - ret.append(0) - val = parse_value(val, typ, 0) + if fill: + while len(val) < 4: + val.append(val[-1]) + + val = parse_value(val, out, 0) results.append(val) pbar.update_absolute(idx) return (results,) @@ -613,6 +610,7 @@ class OPBinaryNode(CozyBaseNode): CATEGORY = JOV_CATEGORY RETURN_TYPES = (COZY_TYPE_ANY,) RETURN_NAMES = ("❔",) + OUTPUT_IS_LIST = (True,) OUTPUT_TOOLTIPS = ( "Output type will match the input type" ) @@ -768,6 +766,7 @@ class StringerNode(CozyBaseNode): CATEGORY = JOV_CATEGORY RETURN_TYPES = ("STRING", "INT",) RETURN_NAMES = ("STRING", "COUNT",) + OUTPUT_IS_LIST = (True, False,) SORT = 44 DESCRIPTION = """ Manipulate strings through filtering @@ -788,7 +787,7 @@ Manipulate strings through filtering "REPLACE": ("STRING", { "default":"", "dynamicPrompt":False}), "RANGE": ("VEC3", { - "default":(0, -1, 1), + "default":(0, -1, 1), "int": True, "tooltip":"Start, End and Step. Values will clip to the actual list size(s)."}), } }) @@ -796,23 +795,25 @@ Manipulate strings through filtering def run(self, **kw) -> tuple[TensorType, ...]: # turn any all inputs into the - data_list = parse_dynamic(kw, "❔", EnumConvertType.ANY, "") + data_list = parse_dynamic(kw, "STRING", EnumConvertType.ANY, "") if data_list is None: logger.warn("no data for list") - return ([],) - # flat list of ALL the dynamic inputs... - #data_list = flatten(data_list) - # single operation mode -- like array node + return ([], 0) + op = parse_param(kw, "FUNCTION", EnumConvertString, EnumConvertString.SPLIT.name)[0] key = parse_param(kw, "KEY", EnumConvertType.STRING, "")[0] replace = parse_param(kw, "REPLACE", EnumConvertType.STRING, "")[0] stenst = parse_param(kw, "RANGE", EnumConvertType.VEC3INT, (0, -1, 1))[0] results = [] + print(data_list) match op: case EnumConvertString.SPLIT: results = data_list if key != "": - results = [r.split(key) for r in data_list] + results = [] + for d in data_list: + d = [key if len(r) == 0 else r for r in d.split(key)] + results.extend(d) case EnumConvertString.JOIN: results = [key.join(data_list)] case EnumConvertString.FIND: @@ -830,15 +831,14 @@ Manipulate strings through filtering results.append(x[start:end:step]) else: results.append(x) - if len(results) == 0: - results = [""] - return (results, [len(r) for r in results],) if len(results) > 1 else (results[0], len(results[0]),) + return (results, len(results),) class SwizzleNode(CozyBaseNode): NAME = "SWIZZLE (JOV) 😵" CATEGORY = JOV_CATEGORY RETURN_TYPES = (COZY_TYPE_ANY,) RETURN_NAMES = ("🦄",) + OUTPUT_IS_LIST = (True,) SORT = 40 DESCRIPTION = """ Swap components between two vectors based on specified swizzle patterns and values. It provides flexibility in rearranging vector elements dynamically. diff --git a/core/vars.py b/core/vars.py index 52935e6..fc29d31 100644 --- a/core/vars.py +++ b/core/vars.py @@ -33,9 +33,7 @@ Supplies raw or default values for various data types, supporting vector input w @classmethod def INPUT_TYPES(cls) -> InputType: d = super().INPUT_TYPES() - typ = EnumConvertType._member_names_[:6] - d = deep_merge(d, { "optional": { "A": (COZY_TYPE_ANY, { diff --git a/web/nodes/akashic.js b/web/nodes/akashic.js index e1280ab..93cd9b2 100644 --- a/web/nodes/akashic.js +++ b/web/nodes/akashic.js @@ -2,7 +2,7 @@ import { app } from "../../../scripts/app.js" import { ComfyWidgets } from '../../../scripts/widgets.js'; -import { nodeAddDynamic } from '../util.js' +import { nodeAddDynamic } from "../util.js" const _prefix = '📥' const _id = "AKASHIC (JOV) 📓" diff --git a/web/nodes/array.js b/web/nodes/array.js index f7eeef2..8aa7f7e 100644 --- a/web/nodes/array.js +++ b/web/nodes/array.js @@ -5,7 +5,7 @@ */ import { app } from "../../../scripts/app.js" -import { nodeAddDynamic } from '../util.js' +import { nodeAddDynamic } from "../util.js" const _id = "ARRAY (JOV) 📚" const _prefix = '❔' diff --git a/web/nodes/calc_binary.js b/web/nodes/calc_binary.js index 224cb39..7e1ed9e 100644 --- a/web/nodes/calc_binary.js +++ b/web/nodes/calc_binary.js @@ -1,7 +1,7 @@ /**/ import { app } from "../../../scripts/app.js" -import { widgetHookAB } from '../util.js' +import { widgetHookAB } from "../util.js" const _id = "OP BINARY (JOV) 🌟" diff --git a/web/nodes/delay.js b/web/nodes/delay.js index d7810b6..5915740 100644 --- a/web/nodes/delay.js +++ b/web/nodes/delay.js @@ -2,7 +2,7 @@ import { api } from "../../../scripts/api.js"; import { app } from "../../../scripts/app.js"; -import { apiJovimetrix } from '../util.js' +import { apiJovimetrix } from "../util.js" import { bubbles } from '../fun.js' const _id = "DELAY (JOV) ✋🏽" diff --git a/web/nodes/flatten.js b/web/nodes/flatten.js index f996f97..949fe3d 100644 --- a/web/nodes/flatten.js +++ b/web/nodes/flatten.js @@ -1,7 +1,7 @@ /**/ import { app } from "../../../scripts/app.js" -import { nodeAddDynamic } from '../util.js' +import { nodeAddDynamic } from "../util.js" const _id = "FLATTEN (JOV) ⬇️" const _prefix = 'IMAGE' @@ -14,6 +14,5 @@ app.registerExtension({ } nodeAddDynamic(nodeType, _prefix); - } }) diff --git a/web/nodes/graph.js b/web/nodes/graph.js index 242d025..2aaa9b1 100644 --- a/web/nodes/graph.js +++ b/web/nodes/graph.js @@ -1,7 +1,7 @@ /**/ import { app } from "../../../scripts/app.js" -import { apiJovimetrix, nodeAddDynamic } from '../util.js' +import { apiJovimetrix, nodeAddDynamic } from "../util.js" const _id = "GRAPH (JOV) 📈" const _prefix = '❔' diff --git a/web/nodes/lerp.js b/web/nodes/lerp.js index 3267e5d..40594bf 100644 --- a/web/nodes/lerp.js +++ b/web/nodes/lerp.js @@ -1,7 +1,7 @@ /**/ import { app } from "../../../scripts/app.js" -import { widgetHookControl, widgetHookAB } from '../util.js' +import { widgetHookControl, widgetHookAB } from "../util.js" const _id = "LERP (JOV) 🔰" diff --git a/web/nodes/op_unary.js b/web/nodes/op_unary.js new file mode 100644 index 0000000..d3f1761 --- /dev/null +++ b/web/nodes/op_unary.js @@ -0,0 +1,48 @@ +/**/ + +import { app } from "../../../scripts/app.js" +import { TypeSlotEvent, TypeSlot, nodeFitHeight } from "../util.js" + +const _id = "OP UNARY (JOV) 🎲" + +app.registerExtension({ + name: 'jovimetrix.node.' + _id, + async beforeRegisterNodeDef(nodeType, nodeData) { + + if (nodeData.name !== _id) { + return; + } + + const onConnectionsChange = nodeType.prototype.onConnectionsChange + nodeType.prototype.onConnectionsChange = function (slotType, slot_idx, event, link_info, node_slot) { + const me = onConnectionsChange?.apply(this, arguments); + let connected = false; + const output = this.outputs[0]; + if (slotType == TypeSlot.Input && link_info && event == TypeSlotEvent.Connect) { + const fromNode = this.graph._nodes.find( + (otherNode) => otherNode.id == link_info.origin_id + ) + + if (fromNode) { + const parent_link = fromNode.outputs[link_info.origin_slot]; + if (parent_link) { + output.type = parent_link.type; + //output.name = parent_link.name; + //output.localized_name = parent_link.localized_name; + connected = true; + } + } + } + if (connected == false) { + output.type = "*"; + //output.name = "❔"; + //output.localized_name = "❔"; + } + nodeFitHeight(this); + return me; + } + + return nodeType; + } +}) + diff --git a/web/nodes/queue.js b/web/nodes/queue.js index 839fd2e..22e9c53 100644 --- a/web/nodes/queue.js +++ b/web/nodes/queue.js @@ -3,7 +3,7 @@ import { api } from "../../../scripts/api.js"; import { app } from "../../../scripts/app.js"; import { ComfyWidgets } from '../../../scripts/widgets.js'; -import { apiJovimetrix, TypeSlotEvent, TypeSlot } from '../util.js' +import { apiJovimetrix, TypeSlotEvent, TypeSlot } from "../util.js" import { flashBackgroundColor } from '../fun.js' const _id1 = "QUEUE (JOV) 🗃"; diff --git a/web/nodes/route.js b/web/nodes/route.js index eb7db15..682a47c 100644 --- a/web/nodes/route.js +++ b/web/nodes/route.js @@ -4,7 +4,7 @@ import { app } from "../../../scripts/app.js" import { TypeSlot, TypeSlotEvent, nodeFitHeight, nodeVirtualLinkRoot, nodeInputsClear, nodeOutputsClear -} from '../util.js' +} from "../util.js" const _id = "ROUTE (JOV) 🚌"; const _prefix = '🔮'; diff --git a/web/nodes/stack.js b/web/nodes/stack.js index 35dca60..45b897d 100644 --- a/web/nodes/stack.js +++ b/web/nodes/stack.js @@ -1,7 +1,7 @@ /**/ import { app } from "../../../scripts/app.js" -import { nodeAddDynamic} from '../util.js' +import { nodeAddDynamic} from "../util.js" const _id = "STACK (JOV) ➕" const _prefix = 'IMAGE' diff --git a/web/nodes/stringer.js b/web/nodes/stringer.js new file mode 100644 index 0000000..e4978d2 --- /dev/null +++ b/web/nodes/stringer.js @@ -0,0 +1,18 @@ +/**/ + +import { app } from "../../../scripts/app.js" +import { nodeAddDynamic } from "../util.js" + +const _id = "STRINGER (JOV) 🪀" +const _prefix = 'STRING' + +app.registerExtension({ + name: 'jovimetrix.node.' + _id, + async beforeRegisterNodeDef(nodeType, nodeData) { + if (nodeData.name !== _id) { + return; + } + + nodeAddDynamic(nodeType, _prefix); + } +}) \ No newline at end of file diff --git a/web/nodes/tick.js b/web/nodes/tick.js index c50aafc..5302a31 100644 --- a/web/nodes/tick.js +++ b/web/nodes/tick.js @@ -2,7 +2,7 @@ import { api } from "../../../scripts/api.js"; import { app } from "../../../scripts/app.js" -import { apiJovimetrix } from '../util.js' +import { apiJovimetrix } from "../util.js" const _id = "TICK (JOV) ⏱"; const EVENT_JOVI_TICK = "jovi-tick"; diff --git a/web/nodes/value.js b/web/nodes/value.js index f16fdda..68042c5 100644 --- a/web/nodes/value.js +++ b/web/nodes/value.js @@ -1,7 +1,7 @@ /**/ import { app } from "../../../scripts/app.js" -import { widgetHookAB, nodeFitHeight} from '../util.js' +import { widgetHookAB, nodeFitHeight} from "../util.js" const _id = "VALUE (JOV) 🧬"