From 885896aab63ae0a352cbe02678e3e115549eb501 Mon Sep 17 00:00:00 2001 From: mcDandy Date: Thu, 2 Jul 2026 14:58:00 +0200 Subject: [PATCH] New version --- README.md | 10 +- more_math/BatchLoraHooksNode.py | 2 +- more_math/BatchLoraNode.py | 10 +- more_math/Parser/MathExpr.g4 | 1060 ++++++++++++------------ more_math/Parser/UnifiedMathVisitor.py | 15 +- more_math/Parser/inbuilt_symbols.py | 10 +- pyproject.toml | 2 +- web/inbuilt_symbols.js | 10 +- web/script_text_input.V1.js.m | 514 ------------ web/script_text_input.V2.js.m | 187 ----- 10 files changed, 559 insertions(+), 1261 deletions(-) delete mode 100644 web/script_text_input.V1.js.m delete mode 100644 web/script_text_input.V2.js.m diff --git a/README.md b/README.md index 0e3c106..ae34637 100644 --- a/README.md +++ b/README.md @@ -223,7 +223,7 @@ You can also get the node from comfy manager under the name of More math. - supports up to `3` coordinate inputs, - uses sampling coordinates derived from the provided coordinate tensors, - intended for spatial remapping / resampling. -- `get_value(tensor, position)`: read value at an N-D position using flat offset math. +- `get_value(tensor, position)`: read value at an N-D position using flat offset math. Superseeded by tensor[position] indexing. --- @@ -233,7 +233,7 @@ You can also get the node from comfy manager under the name of More math. - `blur(x, sigma)` / `gaussian`: Gaussian blur using separable convolution. - `edge(x, [kernel_size])`: Sobel-style edge detection. - `ezconvolution(tensor, ...)` / `ezconv`: convolution with auto layout handling. -- `convolution(tensor, ...)` / `conv`: direct convolution. +- `convolution(tensor, ...)` / `conv`: direct convolution. Expects [batch, channel, spatial...] layout. #### 3.2 Mask Morphology - `dilate(x, [kernel_size])`: dilation. @@ -356,6 +356,12 @@ You can also get the node from comfy manager under the name of More math. - `int(x)`: convert to int32 or nested int values. - `float(x)`: convert to float or nested float values. +___ + +### Generators +- `text_image(text, font, size, [max_width], [weight], [rotation_angle], [line_spacing], [italic], [underline])` - renders text to an 2D tensor + + --- ### 11) State / Stack diff --git a/more_math/BatchLoraHooksNode.py b/more_math/BatchLoraHooksNode.py index 2e15a5d..6c8512c 100644 --- a/more_math/BatchLoraHooksNode.py +++ b/more_math/BatchLoraHooksNode.py @@ -25,7 +25,7 @@ def _parse_strengths(Expression, stack): return (list(result), stack) class BatchLoraHooksNode(io.ComfyNode): - """Scale existing bypass LoRA injections on a model/clip pair.""" + """Scale existing bypass LoRA injections on a model/clip pair. Works with models/loaders/Load lora (Bypass) (For Debugging) or more_math batch lora node.""" @staticmethod def _get_bypass_hooks(injections): diff --git a/more_math/BatchLoraNode.py b/more_math/BatchLoraNode.py index 3edf48c..236034e 100644 --- a/more_math/BatchLoraNode.py +++ b/more_math/BatchLoraNode.py @@ -1,3 +1,5 @@ +from email.mime import base + from comfy_api.latest import io import folder_paths from .Stack import MrmthStack @@ -15,9 +17,9 @@ class BatchLoraNode(io.ComfyNode): """ @staticmethod - def _parse_strengths(Expression, stack,lora_list,lora_count): + def _parse_strengths(Expression, stack,lora_list,lora_count,base_model): tree = None - variables = {"lora_names": lora_list, "lora_count": lora_count} + variables = {"lora_names": lora_list, "lora_count": lora_count,"base_model": base_model} if isinstance(Expression,str): tree = parse_expr(Expression) else: @@ -70,8 +72,8 @@ class BatchLoraNode(io.ComfyNode): or os.path.normpath(lora).startswith(selected_folder + os.sep) ] - model_strengths, stack = cls._parse_strengths(model_strength, stack, lora_path, len(lora_path)) - clip_strengths, stack = cls._parse_strengths(clip_strength, stack, lora_path, len(lora_path)) + model_strengths, stack = cls._parse_strengths(model_strength, stack, lora_path, len(lora_path), add_no_lora) + clip_strengths, stack = cls._parse_strengths(clip_strength, stack, lora_path, len(lora_path), add_no_lora) print(stack) model_lora = [model] if add_no_lora else [] clip_lora = [clip] if add_no_lora else [] diff --git a/more_math/Parser/MathExpr.g4 b/more_math/Parser/MathExpr.g4 index cd22175..5000eed 100644 --- a/more_math/Parser/MathExpr.g4 +++ b/more_math/Parser/MathExpr.g4 @@ -1,6 +1,6 @@ /** * MathExpr.g4 - * + * * This grammar defines the syntax for a mathematical expression language. * It supports: * - Variable and function definitions @@ -110,722 +110,722 @@ atom: exprList: expr (COMMA expr)*; func0: - /** - timestamp() - returns the current system timestamp - */ + /** + timestamp() - returns the current system timestamp + */ TIMESTAMP LPAREN RPAREN # TimestampFunc; // Single-argument functions func1: - /** - sin(x) - applies sinus function to value or each element of value - */ + /** + sin(x) - applies sinus function to value or each element of value + */ SIN LPAREN expr RPAREN # SinFunc - /** - cos(x) - applies cosinus function to value or each element of value - */ + /** + cos(x) - applies cosinus function to value or each element of value + */ | COS LPAREN expr RPAREN # CosFunc - /** - tan(x) - applies tangents function to value or each element of value - */ + /** + tan(x) - applies tangents function to value or each element of value + */ | TAN LPAREN expr RPAREN # TanFunc - /** - asin(x) - applies arcus sinus function to value or each element of value - */ + /** + asin(x) - applies arcus sinus function to value or each element of value + */ | ASIN LPAREN expr RPAREN # AsinFunc - /** - acos(x) - applies arcus cosinus function to value or each element of value - */ + /** + acos(x) - applies arcus cosinus function to value or each element of value + */ | ACOS LPAREN expr RPAREN # AcosFunc - /** - atan(x) - applies arcus tangents function to value or each element of value - */ + /** + atan(x) - applies arcus tangents function to value or each element of value + */ | ATAN LPAREN expr RPAREN # AtanFunc - /** - sinh(x) - applies hyperbolic sinus function to value or each element of value - */ + /** + sinh(x) - applies hyperbolic sinus function to value or each element of value + */ | SINH LPAREN expr RPAREN # SinhFunc - /** - cosh(x) - applies hyperbolic cosinus function to value or each element of value - */ + /** + cosh(x) - applies hyperbolic cosinus function to value or each element of value + */ | COSH LPAREN expr RPAREN # CoshFunc - /** - tanh(x) - applies hyperbolic tangents function to value or each element of value - */ + /** + tanh(x) - applies hyperbolic tangents function to value or each element of value + */ | TANH LPAREN expr RPAREN # TanhFunc - /** - asinh(x) - applies hyperbolic arcus sinus function to value or each element of value - */ + /** + asinh(x) - applies hyperbolic arcus sinus function to value or each element of value + */ | ASINH LPAREN expr RPAREN # AsinhFunc - /** - acosh(x) - applies hyperbolic arcus cosinus function to value or each element of value - */ + /** + acosh(x) - applies hyperbolic arcus cosinus function to value or each element of value + */ | ACOSH LPAREN expr RPAREN # AcoshFunc - /** - atanh(x) - applies hyperbolic arcus tangents function to value or each element of value - */ + /** + atanh(x) - applies hyperbolic arcus tangents function to value or each element of value + */ | ATANH LPAREN expr RPAREN # AtanhFunc - /** - abs(x) - applies per element absolute value function. Same as |x| for numbers. - */ + /** + abs(x) - applies per element absolute value function. Same as |x| for numbers. + */ | ABS LPAREN expr RPAREN # AbsFunc - /** - sqrt(x) - applies sqere root per element. Negative numbers return NaN (not a number) - */ + /** + sqrt(x) - applies sqere root per element. Negative numbers return NaN (not a number) + */ | SQRT LPAREN expr RPAREN # SqrtFunc - /** - ln(x) - applies natural logarithm to value (per element). Negative numbers return NaN (not a number) - */ + /** + ln(x) - applies natural logarithm to value (per element). Negative numbers return NaN (not a number) + */ | LN LPAREN expr RPAREN # LnFunc - /** - log(x) - applies base 10 logarithm to value (per element). Negative numbers return NaN (not a number) - */ + /** + log(x) - applies base 10 logarithm to value (per element). Negative numbers return NaN (not a number) + */ | LOG LPAREN expr RPAREN # LogFunc - /** - exp(x) - applies e^x to value (per element) - */ + /** + exp(x) - applies e^x to value (per element) + */ | EXP LPAREN expr RPAREN # ExpFunc - /** - tnorm(x) - normalises tensor or list by multiplication such that sum(x^2)==1.0 for each slice. The slice is last dimension of the tensor. - */ + /** + tnorm(x) - normalises tensor or list by multiplication such that sum(x^2)==1.0 for each slice. The slice is last dimension of the tensor. + */ | TNORM LPAREN expr RPAREN # TNormFunc - /** - floor(x) - returns the largest integer less than or equal to x - */ + /** + floor(x) - returns the largest integer less than or equal to x + */ | FLOOR LPAREN expr RPAREN # FloorFunc - /** - ceil(x) - returns the smallest integer greater than or equal to x - */ + /** + ceil(x) - returns the smallest integer greater than or equal to x + */ | CEIL LPAREN expr RPAREN # CeilFunc - /** - round(x) - rounds x to the nearest integer - */ + /** + round(x) - rounds x to the nearest integer + */ | ROUND LPAREN expr RPAREN # RoundFunc - /** - gamma(x) - computes the gamma function (per element) - */ + /** + gamma(x) - computes the gamma function (per element) + */ | GAMMA LPAREN expr RPAREN # GammaFunc - /** - sigm(x) - applies the sigmoid function: 1 / (1 + exp(-x)) - */ + /** + sigm(x) - applies the sigmoid function: 1 / (1 + exp(-x)) + */ | SIGM LPAREN expr RPAREN # sigmoidFunc - /** - angle(x) - returns the angle of a complex number or vector - */ + /** + angle(x) - returns the angle of a complex number or vector + */ | ANGL LPAREN expr RPAREN # anglFunc - /** - print(x) - prints the value of x to the console while passing output unchanged - */ + /** + print(x) - prints the value of x to the console while passing output unchanged + */ | PRNT LPAREN expr RPAREN # printFunc - /** - fract(x) - returns the fractional part of x: x - floor(x) - */ + /** + fract(x) - returns the fractional part of x: x - floor(x) + */ | FRACT LPAREN expr RPAREN # FractFunc - /** - relu(x) - applies rectified linear unit function: max(0, x) - */ + /** + relu(x) - applies rectified linear unit function: max(0, x) + */ | RELU LPAREN expr RPAREN # ReluFunc - /** - softplus(x) - applies the softplus function: ln(1 + exp(x)) - */ + /** + softplus(x) - applies the softplus function: ln(1 + exp(x)) + */ | SOFTPLUS LPAREN expr RPAREN # SoftplusFunc - /** - gelu(x) - applies the Gaussian Error Linear Unit (GELU) activation function - */ + /** + gelu(x) - applies the Gaussian Error Linear Unit (GELU) activation function + */ | GELU LPAREN expr RPAREN # GeluFunc - /** - sign(x) - returns the sign of x: -1, 0, or 1 - */ + /** + sign(x) - returns the sign of x: -1, 0, or 1 + */ | SIGN LPAREN expr RPAREN # SignFunc - /** - print_shape(x) - prints the shape of the tensor x - */ + /** + print_shape(x) - prints the shape of the tensor x + */ | PRINT_SHAPE LPAREN expr RPAREN # PrintShapeFunc - /** - pinv(x) - computes the permutative inverse of a list x (list must have uniqe elements) -- TODO: CHECK -- - */ + /** + pinv(x) - computes the permutative inverse of a list x (list must have uniqe elements) -- TODO: CHECK -- + */ | PINV LPAREN expr RPAREN # PinvFunc - /** - sum(x) - computes the sum of elements of x - */ + /** + sum(x) - computes the sum of elements of x + */ | SUM LPAREN expr RPAREN # SumFunc - /** - mean(x) - computes the arithmetic mean of elements of x - */ + /** + mean(x) - computes the arithmetic mean of elements of x + */ | MEAN LPAREN expr RPAREN # MeanFunc - /** - std(x) - computes the standard deviation of elements of x - */ + /** + std(x) - computes the standard deviation of elements of x + */ | STD LPAREN expr RPAREN # StdFunc - /** - var(x) - computes the variance of elements of x - */ + /** + var(x) - computes the variance of elements of x + */ | VAR LPAREN expr RPAREN # VarFunc - /** - sort(x) - returns a sorted version of x (If input is tensor, it sorts the last dimension) - */ + /** + sort(x) - returns a sorted version of x (If input is tensor, it sorts the last dimension) + */ | SORT LPAREN expr RPAREN # SortFunc - /** - any(x) - returns true if any element of x is non-zero - */ + /** + any(x) - returns true if any element of x is non-zero + */ | ANY LPAREN expr RPAREN # AnyFunc - /** - all(x) - returns true if all elements of x are non-zero - */ + /** + all(x) - returns true if all elements of x are non-zero + */ | ALL LPAREN expr RPAREN # AllFunc - /** - edge(x, [matrix_size]) - detects edges in x. Matrix size denotes the size of edge detection matrix. - */ + /** + edge(x, [matrix_size]) - detects edges in x. Matrix size denotes the size of edge detection matrix. + */ | EDGE LPAREN expr (COMMA expr)? RPAREN # EdgeFunc - /** - median(x) - computes the median of elements of x - */ + /** + median(x) - computes the median of elements of x + */ | MEDIAN LPAREN expr RPAREN # MedianFunc - /** - mode(x) - computes the mode of elements of x - */ + /** + mode(x) - computes the mode of elements of x + */ | MODE LPAREN expr RPAREN # ModeFunc - /** - cumsum(x) - computes the cumulative sum over first dimension. - */ + /** + cumsum(x) - computes the cumulative sum over first dimension. + */ | CUMSUM LPAREN expr RPAREN # CumsumFunc - /** - count(x) - returns the number of elements in x - */ + /** + count(x) - returns the number of elements in x + */ | COUNT LPAREN expr RPAREN # CountFunc - /** - cumprod(x) - computes the cumulative product over first dimension. - */ + /** + cumprod(x) - computes the cumulative product over first dimension. + */ | CUMPROD LPAREN expr RPAREN # CumprodFunc - /** - stack_pop(slot) - removes and returns the last element of slot stack - */ + /** + stack_pop(slot) - removes and returns the last element of slot stack + */ | POP LPAREN expr RPAREN # PopFunc - /** - stack_clear(slot) - clears the contents of slot stack - */ + /** + stack_clear(slot) - clears the contents of slot stack + */ | CLEAR LPAREN expr RPAREN # ClearFunc - /** - stack_has(slot) - returns 1.0 if slot exists in state storage and is not empty else returns 0.0 - */ + /** + stack_has(slot) - returns 1.0 if slot exists in stack and is not empty else returns 0.0 + */ | HAS LPAREN expr RPAREN # HasFunc - /** - stack_get(slot) - returns the last pushed value in slot without popping it - */ + /** + stack_get(slot) - returns the last pushed value in slot without popping it + */ | GET LPAREN expr RPAREN # GetFunc - /** - argsort(x, [desc]) - returns the indices that would sort x. defaults to ascending. Uses last dimension to sort - */ + /** + argsort(x, [desc]) - returns the indices that would sort x. defaults to ascending. Uses last dimension to sort + */ | ARGSORT LPAREN expr (COMMA expr)? RPAREN # ArgsortFunc - /** - argmin(x) - returns the index of the minimum value in flattened x - */ + /** + argmin(x) - returns the index of the minimum value in flattened x + */ | ARGMIN LPAREN expr RPAREN # ArgminFunc - /** - argmax(x) - returns the index of the maximum value in flattened x - */ + /** + argmax(x) - returns the index of the maximum value in flattened x + */ | ARGMAX LPAREN expr RPAREN # ArgmaxFunc - /** - softmax(x, [axis]) - applies the softmax function to x (sum of slice == 1.0 and max value <= 1.0). axis defaults to last. - */ - | SOFTMAX LPAREN expr (COMMA expr)? RPAREN # SoftmaxFunc - /** - softmin(x, [axis]) - applies the softmin function to x (same as softmax(-x,[axis])). axis defaults to last. - */ - | SOFTMIN LPAREN expr (COMMA expr)? RPAREN # SoftminFunc - /** - erf(x) - computes the error function -- TODO: MORE DESCRIPTIVE -- - */ + /** + softmax(x, [axis]) - applies the softmax function to x (sum of slice == 1.0 and max value <= 1.0). axis defaults to last. + */ + | SOFTMAX LPAREN expr (COMMA expr)? RPAREN # SoftmaxFunc + /** + softmin(x, [axis]) - applies the softmin function to x (same as softmax(-x,[axis])). axis defaults to last. + */ + | SOFTMIN LPAREN expr (COMMA expr)? RPAREN # SoftminFunc + /** + erf(x) - computes the error function (torch.erf) + */ | ERF LPAREN expr RPAREN # ErfFunc - /** - erfinv(x) - computes the inverse error function -- TODO: MORE DESCRIPTIVE -- - */ + /** + erfinv(x) - computes the inverse error function (torch.erfinv) + */ | ERFINV LPAREN expr RPAREN # ErfinvFunc - /** - unique(x) - returns the unique elements (sorted,) --TODO: FINISH -- - */ + /** + unique(x) - returns the unique elements (sorted, flatened) + */ | UNIQUE LPAREN expr RPAREN # UniqueFunc - /** - flatten(x) - flattens tensor into a 1D tensor - */ + /** + flatten(x) - flattens tensor into a 1D tensor + */ | FLATTEN LPAREN expr RPAREN # FlattenFunc - /** - motion_mask(x) - generates a motion mask from optical flow - */ + /** + motion_mask(x) - generates a motion mask from optical flow + */ | MOTION_MASK LPAREN expr RPAREN # MotionMaskFunc - /** - flow_to_image(x) - converts optical flow x to an RGB image. Up-Down is Left-right - */ + /** + flow_to_image(x) - converts optical flow x to an RGB image. Up-Down is blue-yellow Left-right green-magenta. + */ | FLOW_TO_IMAGE LPAREN expr RPAREN # FlowToImageFunc - /** - bnot(x) - computes the bitwise NOT - */ + /** + bnot(x) - computes the bitwise NOT + */ | BNOT LPAREN expr RPAREN # BitNotFunc - /** - bitcount(x) - returns the number of set bits - */ + /** + bitcount(x) - returns the number of set bits + */ | BITCOUNT LPAREN expr RPAREN # BitCountFunc - /** - shape(x) - returns the shape of tensor as a list - */ + /** + shape(x) - returns the shape of tensor as a list + */ | SHAPE LPAREN expr RPAREN # ShapeFunc - /** - upper(x) - converts string to uppercase - */ + /** + upper(x) - converts string to uppercase + */ | UPPER LPAREN expr RPAREN # UpperFunc - /** - lower(x) - converts string to lowercase - */ + /** + lower(x) - converts string to lowercase + */ | LOWER LPAREN expr RPAREN # LowerFunc - /** - trim(x) - removes leading and trailing whitespace from string - */ + /** + trim(x) - removes leading and trailing whitespace from string + */ | TRIM LPAREN expr RPAREN # TrimFunc - /** - entropy(x) - computes shanon entropy - */ + /** + entropy(x) - computes shanon entropy + */ | ENTROPY LPAREN expr RPAREN # EntropyFunc - /** - fft(x) - computes fast Fourier transform of x - */ + /** + fft(x) - computes fast Fourier transform of x + */ | SFFT LPAREN expr RPAREN # SfftFunc - /** - dilate(x, [kernel]) - applies dilation to x. kernel is optional. - */ + /** + dilate(x, [kernel]) - applies dilation to x. kernel is optional. + */ | DILATE LPAREN expr (COMMA expr)? RPAREN # DilateFunc - /** - erode(x, [kernel]) - applies erosion to x. kernel is size of used matrix. - */ + /** + erode(x, [kernel]) - applies erosion to x. kernel is size of used matrix. + */ | ERODE LPAREN expr (COMMA expr)? RPAREN # ErodeFunc - /** - morph_open(x, [kernel]) - applies morphological opening to x. kernel is size of used matrix. - */ + /** + morph_open(x, [kernel]) - applies morphological opening to x. kernel is size of used matrix. + */ | MORPH_OPEN LPAREN expr (COMMA expr)? RPAREN # MorphOpenFunc - /** - morph_close(x, [kernel]) - applies morphological closing to x. kernel is size of used matrix. - */ + /** + morph_close(x, [kernel]) - applies morphological closing to x. kernel is size of used matrix. + */ | MORPH_CLOSE LPAREN expr (COMMA expr)? RPAREN # MorphCloseFunc - /** - int(x) - casts x to integer - */ + /** + int(x) - casts x to integer + */ | INT LPAREN expr RPAREN # IntFunc - /** - float(x) - casts x to float - */ + /** + float(x) - casts x to float + */ | FLOAT LPAREN expr RPAREN # FloatFunc - /** - flow_magnitude(x) - computes the magnitude of optical flow (or any other structure with last dimension of 2) - */ + /** + flow_magnitude(x) - computes the magnitude of optical flow (or any other structure with last dimension of 2) + */ | FLOW_MAG LPAREN expr RPAREN # FlowMagFunc - /** - flow_angle(x) - computes the angle of optical flow (basically angle() but real and imaginery is separate in 2 dimensions) - */ + /** + flow_angle(x) - computes the angle of optical flow (basically angle() but real and imaginery is separate in 2 dimensions) + */ | FLOW_ANG LPAREN expr RPAREN # FlowAngFunc; func2: - /** - pow(x, y) - computes x raised to the power of y - */ + /** + pow(x, y) - computes x raised to the power of y + */ POWE LPAREN expr COMMA expr RPAREN # PowFunc - /** - atan2(y, x) - computes the arc tangent of y/x, using the signs of both arguments to determine the quadrant - */ + /** + atan2(y, x) - computes the arc tangent of y/x, using the signs of both arguments to determine the quadrant + */ | ATAN2 LPAREN expr COMMA expr RPAREN # Atan2Func - /** - tmin(x, y) - elementwise minimum of tensor or list. min(x,y) when inputs are floats - */ + /** + tmin(x, y) - elementwise minimum of tensor or list. min(x,y) when inputs are floats + */ | TMIN LPAREN expr COMMA expr RPAREN # TMinFunc - /** - tmax(x,y) - elementwise maximum of tensor or list. max(x,y) when inputs are floats - */ + /** + tmax(x,y) - elementwise maximum of tensor or list. max(x,y) when inputs are floats + */ | TMAX LPAREN expr COMMA expr RPAREN # TMaxFunc - /** - snorm(x,[dim]) - frobenius norm of tensor. Along dimension if dimension specified. Dimension can be a list. Defaults to no dimension. - */ + /** + snorm(x,[dim]) - frobenius norm of tensor. Along dimension if dimension specified. Dimension can be a list. Defaults to no dimension. + */ | SNORM LPAREN expr (COMMA expr)? RPAREN # SNormFunc - /** - step(x, edge) - returns 0 if x < edge, else 1 - */ + /** + step(x, edge) - returns 0 if x < edge, else 1 + */ | STEP LPAREN expr COMMA expr RPAREN # StepFunc - /** - topk(x, k) - returns the k largest elements of x. For tensors it keeps them in place. - */ + /** + topk(x, k) - returns the k largest elements of x. For tensors it keeps them in place. + */ | TOPK LPAREN expr COMMA expr RPAREN # TopkFunc - /** - botk(x, k) - returns the k smallest elements of x. For tensors it keeps them in place. - */ + /** + botk(x, k) - returns the k smallest elements of x. For tensors it keeps them in place. + */ | BOTK LPAREN expr COMMA expr RPAREN # BotkFunc - /** - quartile(x, q) - computes the q-th quartile of x - */ + /** + quartile(x, q) - computes the q-th quartile of x + */ | QUARTILE LPAREN expr COMMA expr RPAREN # QuartileFunc - /** - percentile(x, p) - computes the p-th percentile of x - */ + /** + percentile(x, p) - computes the p-th percentile of x + */ | PERCENTILE LPAREN expr COMMA expr RPAREN # PercentileFunc - /** - quantile(x, q) - computes the q-th quantile of x - */ + /** + quantile(x, q) - computes the q-th quantile of x + */ | QUANTILE LPAREN expr COMMA expr RPAREN # QuantileFunc - /** - dot(x, y) - computes the dot product of x and y. Last dimension must be 3. - */ + /** + dot(x, y) - computes the dot product of x and y. Last dimension must be 3. + */ | DOT LPAREN expr COMMA expr RPAREN # DotFunc - /** - cosine_similarity(x, y) - computes the cosine similarity between x and y. - */ + /** + cosine_similarity(x, y) - computes the cosine similarity between x and y. + */ | COSSIM LPAREN expr COMMA expr RPAREN # CossimFunc - /** - flip(x, dims) - reverses the order of elements along the specified dimensions. Works also for text and list with dims=0 - */ + /** + flip(x, dims) - reverses the order of elements along the specified dimensions. Works also for text and list with dims=0 + */ | FLIP LPAREN expr COMMA expr RPAREN # FlipFunc - /** - cov(x, y) - computes the covariance matrix of x and y - */ + /** + cov(x, y) - computes the covariance matrix of x and y + */ | COV LPAREN expr COMMA expr RPAREN # CovFunc - /** - correlation(x, y) - computes the correlation between x and y - */ + /** + correlation(x, y) - computes the correlation between x and y + */ | CORR LPAREN expr COMMA expr RPAREN # CorrFunc - /** - append(x, y) - appends y to the end of x. If inputs are tensors use concatenate(x,...,dim) - */ + /** + append(x, y) - appends y to the end of x. If inputs are tensors use concatenate(x,...,dim) + */ | APPEND LPAREN expr COMMA expr RPAREN # AppendFunc - /** - permute(x, dims) - permutes the dimensions of tensor x according to dims - */ + /** + permute(x, dims) - permutes the dimensions of tensor x according to dims + */ | PERM LPAREN expr COMMA expr RPAREN # PermuteFunc - /** - gaussian(x, sigma, [reshape]) - applies a Gaussian blur to x with specified sigma. if reshape has value of 1.0, then it tries orienting the input such that channel is in the direction of filter. Otherwise it expect color dimension to be the last. - */ + /** + gaussian(x, sigma, [reshape]) - applies a Gaussian blur to x with specified sigma. if reshape has value of 1.0, then it tries orienting the input such that channel is in the direction of filter. Otherwise it expect color dimension to be the last. + */ | GAUSSIAN LPAREN expr COMMA expr (COMMA expr)? RPAREN # GaussianFunc - /** - topk_indices(x, k) - returns the indices of the k largest elements of x - */ + /** + topk_indices(x, k) - returns the indices of the k largest elements of x + */ | TOPK_IND LPAREN expr COMMA expr RPAREN # TopkIndFunc - /** - botk_indices(x, k) - returns the indices of the k smallest elements of x - */ + /** + botk_indices(x, k) - returns the indices of the k smallest elements of x + */ | BOTK_IND LPAREN expr COMMA expr RPAREN # BotkIndFunc - /** - batch_shuffle(x, indices) - shuffles and duplicates or skips the batch dimension of x according to indices - */ + /** + batch_shuffle(x, indices) - shuffles and duplicates or skips the batch dimension of x according to indices + */ | BATCH_SHUFFLE LPAREN expr COMMA expr RPAREN # BatchShuffleFunc - /** - stack_push(slot, val) - pushes val onto the end of slot stack - */ + /** + stack_push(slot, val) - pushes val onto the end of slot stack + */ | PUSH LPAREN expr COMMA expr RPAREN # PushFunc - /** - get_value(slot) - returns the value at slot from stack - */ + /** + get_value(slot) - returns the value at slot from stack + */ | GET_VALUE LPAREN expr COMMA expr RPAREN # GetValueFunc - /** - tensor(shape, [value], [dtype_template]) - creates a tensor with specified shape and optional value/type. Default value is 0 and default format FP32 - */ + /** + tensor(shape, [value], [dtype_template]) - creates a tensor with specified shape and optional value/type. Default value is 0 and default format FP32 + */ | TENSOR LPAREN indexExpr (COMMA expr (COMMA expr)? )? RPAREN # EmptyTensorFunc - /** - pad(x, padding) - pads tensor x with specified padding (dim 0 neg, dim 0 pos, dim 1 ...) - */ + /** + pad(x, padding) - pads tensor x with specified padding (dim 0 neg, dim 0 pos, dim 1 ...) + */ | PAD LPAREN expr COMMA expr RPAREN # PadFunc - /** - cross(x, y) - computes the cross product of x and y. Last dimension must be 3 - */ + /** + cross(x, y) - computes the cross product of x and y. Last dimension must be 3 + */ | CROSS LPAREN expr COMMA expr RPAREN # CrossFunc - /** - matmul(x, y) - computes the matrix multiplication of x and y - */ + /** + matmul(x, y) - computes the matrix multiplication of x and y + */ | MATMUL LPAREN expr COMMA expr RPAREN # MatmulFunc - /** - flow_apply(image, flow) - applies optical flow to an image - */ + /** + flow_apply(image, flow) - applies optical flow to an image + */ | FLOW_APPLY LPAREN expr COMMA expr RPAREN # FlowApplyFunc - /** - rife(image1, image2, [tile_size], [iterations], [multi_scale]) - computes an intermediate frame between image1 and image2 using RIFE - - tile_size: chuncks image to overlapping tile_size*tile_size parts to conserve memory Default is 1024x1024 when over 2MPx. When tiling size is between 0 - 1 it takes as a fraction of the resolution - - iterations: how many times to run RIFE model. Default 12 - - multi_scale: also use low resolution of base image for giant movements - */ + /** + rife(image1, image2, [tile_size], [iterations], [multi_scale]) - computes an intermediate frame between image1 and image2 using RIFE + - tile_size: chuncks image to overlapping tile_size*tile_size parts to conserve memory Default is 1024x1024 when over 2MPx. When tiling size is between 0 - 1 it takes as a fraction of the resolution + - iterations: how many times to run RIFE model. Default 12 + - multi_scale: also use low resolution of base image for giant movements + */ | RIFE LPAREN expr COMMA expr (COMMA expr (COMMA expr (COMMA expr)?)?)? RPAREN # RifeFunc - /** - bitwise_and(x, y) - computes the element-wise bitwise AND of x and y - */ + /** + bitwise_and(x, y) - computes the element-wise bitwise AND of x and y + */ | BAND LPAREN expr COMMA expr RPAREN # BitAndFunc - /** - bitwise_xor(x, y) - computes the element-wise bitwise XOR of x and y - */ + /** + bitwise_xor(x, y) - computes the element-wise bitwise XOR of x and y + */ | XOR LPAREN expr COMMA expr RPAREN # BitXorFunc - /** - bitwise_or(x, y) - computes the element-wise bitwise OR of x and y - */ + /** + bitwise_or(x, y) - computes the element-wise bitwise OR of x and y + */ | BOR LPAREN expr COMMA expr RPAREN # BitOrFunc - /** - split(x, [delimiter]) - splits string to list of strings based on delimiter. Default is space. - */ + /** + split(x, [delimiter]) - splits string to list of strings based on delimiter. Default is space. + */ | SPLIT LPAREN expr (COMMA expr)? RPAREN # SplitFunc - /** - join(x, [separator]) - joins strings in list using separator. Defalut separator is "". If you want to join tensors use concatinate(...) - */ + /** + join(x, [separator]) - joins strings in list using separator. Defalut separator is "". If you want to join tensors use concatinate(...) + */ | JOIN LPAREN expr (COMMA expr)? RPAREN # JoinFunc - /** - substring(s, start, [end]) - returns a substring of s from start to end. If end is not supplied, it uses end of string - */ + /** + substring(s, start, [end]) - returns a substring of s from start to end. If end is not supplied, it uses end of string + */ | SUBSTRING LPAREN expr COMMA expr (COMMA expr)? RPAREN # SubstringFunc - /** - find(s, sub) - returns the index of the first occurrence of sub in s. -1 if not found. Works only for strings. - */ + /** + find(s, sub) - returns the index of the first occurrence of sub in s. -1 if not found. Works only for strings. + */ | FIND LPAREN expr COMMA expr RPAREN # FindFunc - /** - replace(s, old, new) - replaces occurrences of old with new in s. Mostly for strings but can work with lists and tensors. - */ + /** + replace(s, old, new) - replaces occurrences of old with new in s. Mostly for strings but can work with lists and tensors. + */ | REPLACE LPAREN expr COMMA expr COMMA expr RPAREN # ReplaceFunc - /** - int_to_rgb(val) - converts an integer to an RGB color. If input is tensor it stacks the resulting R, G and B tensors using last dimension. If It is list, List of lists is returned and if value, 3 long list is returned. - */ + /** + int_to_rgb(val) - converts an integer to an RGB color. If input is tensor it stacks the resulting R, G and B tensors using last dimension. If It is list, List of lists is returned and if value, 3 long list is returned. + */ | INT_TO_RGB LPAREN expr RPAREN # Int_to_rgbFunc - /** - rgb_to_int(r, [g, b]) - converts RGB components to an integer It uses last dimension of tensor (or list) as R,G,B if one value. Othervise uses whole thing as channel. - */ + /** + rgb_to_int(r, [g, b]) - converts RGB components to an integer It uses last dimension of tensor (or list) as R,G,B if one value. Othervise uses whole thing as channel. + */ | RGB_TO_INT LPAREN expr ( COMMA expr COMMA expr)? RPAREN # Rgb_to_intFunc - /** - interpolate_linear(x, y) - performs linear interpolation on X. Assumes that dim 0 is batch and dim 1 = channel - */ + /** + interpolate_linear(x, y) - performs linear interpolation on X. Assumes that dim 0 is batch and dim 1 = channel + */ | INTERPOLATE_LINEAR LPAREN expr COMMA expr RPAREN # InterpolateLinearFunc - /** - interpolate_area(x, y) - performs area interpolation X. Assumes that dim 0 is batch and dim 1 = channel - */ + /** + interpolate_area(x, y) - performs area interpolation X. Assumes that dim 0 is batch and dim 1 = channel + */ | INTERPOLATE_AREA LPAREN expr COMMA expr RPAREN # InterpolateAreaFunc - /** - interpolate_nearest_exact(x, y) - performs nearest neighbor interpolation X. Assumes that dim 0 is batch and dim 1 = channel - */ + /** + interpolate_nearest_exact(x, y) - performs nearest neighbor interpolation X. Assumes that dim 0 is batch and dim 1 = channel + */ | INTERPOLATE_NEAREST LPAREN expr COMMA expr RPAREN # InterpolateNearestExactFunc ; func3: - /** - clamp(x, min, max) - clamps x between min and max per element - */ + /** + clamp(x, min, max) - clamps x between min and max per element + */ CLAMP LPAREN expr COMMA expr COMMA expr RPAREN # ClampFunc - /** - lerp(a, b, t) - linear interpolation between a and b by t per element. t can be float or tensor. - */ + /** + lerp(a, b, t) - linear interpolation between a and b by t per element. t can be float or tensor. + */ | LERP LPAREN expr COMMA expr COMMA expr RPAREN # LerpFunc - /** - smoothstep(x, edge0, edge1) - smooth interpolation between edge0 and edge1 - */ + /** + smoothstep(x, edge0, edge1) - smooth interpolation between edge0 and edge1 + */ | SMOOTHSTEP LPAREN expr COMMA expr COMMA expr RPAREN # SmoothstepFunc - /** - range(start, stop, step) - creates a range of values between start (inclusive) and stop (exclusive) using step. Use linspace if you want value count. - */ + /** + range(start, stop, step) - creates a range of values between start (inclusive) and stop (exclusive) using step. Use linspace if you want value count. + */ | RANGE LPAREN expr COMMA expr COMMA expr RPAREN # RangeFunc - /** - moment(x, a, k) - computes the k-th moment of x around a - */ + /** + moment(x, a, k) - computes the k-th moment of x around a + */ | MOMENT LPAREN expr COMMA expr COMMA expr RPAREN # MomentFunc - /** - cubic_ease(a, b, t) - cubic ease between a and b - */ + /** + cubic_ease(a, b, t) - cubic ease between a and b + */ | CUBIC_EASE LPAREN expr COMMA expr COMMA expr RPAREN # CubicEaseFunc - /** - elastic_ease(a, b, t) - elastic ease between a and b - */ + /** + elastic_ease(a, b, t) - elastic ease between a and b + */ | ELASTIC_EASE LPAREN expr COMMA expr COMMA expr RPAREN # ElasticEaseFunc - /** - sine_ease(a, b, t) - sine easing between a and b - */ + /** + sine_ease(a, b, t) - sine easing between a and b + */ | SINE_EASE LPAREN expr COMMA expr COMMA expr RPAREN # SineEaseFunc - /** - smootherstep(x, edge0, edge1) - smoother interpolation between edge0 and edge1 - */ + /** + smootherstep(x, edge0, edge1) - smoother interpolation between edge0 and edge1 + */ | SMOOTHERSTEP LPAREN expr COMMA expr COMMA expr RPAREN # SmootherstepFunc - /** - crop(x, start, size) - crops tensor x. Take size and begin at start. start should be the low corner. The result is at most size (if size is bigger then rest of tensor). - */ + /** + crop(x, start, size) - crops tensor x. Take size and begin at start. start should be the low corner. The result is at most size (if size is bigger then rest of tensor). + */ | CROP LPAREN expr COMMA expr COMMA expr RPAREN # CropFunc - /** - ifft(x, [axis]) - inverse fast Fourier transform - */ + /** + ifft(x, [axis]) - inverse fast Fourier transform + */ | SIFFT LPAREN expr (COMMA expr)? RPAREN # SifftFunc - /** - overlay(base, overlay, offset, [opacity]) - overlays overlay on base at offset. Opacity controls blending. It can be tensor or float. If it is tensor it should be the same shape as overlay. - */ + /** + overlay(base, overlay, offset, [opacity]) - overlays overlay on base at offset. Opacity controls blending. It can be tensor or float. If it is tensor it should be the same shape as overlay. + */ | OVERLAY LPAREN expr COMMA expr COMMA expr (COMMA expr)? RPAREN # OverlayFunc - /** - linspace(start, stop, num) - creates num linearly spaced values from start to stop (inclusive). Use range if you want to control the step between values. - */ + /** + linspace(start, stop, num) - creates num linearly spaced values from start to stop (inclusive). Use range if you want to control the step between values. + */ | LINSPACE LPAREN expr COMMA expr COMMA expr RPAREN # LinspaceFunc - /** - roll(x, shifts, [axis]) - rolls tensor x along axis - */ + /** + roll(x, shifts, [axis]) - rolls tensor x along axis + */ | ROLL LPAREN expr COMMA expr (COMMA expr)? RPAREN # RollFunc - /** - rgb_to_oklab(r, [g], [b]) - converts RGB to OKLab. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. - */ + /** + rgb_to_oklab(r, [g], [b]) - converts RGB to OKLab. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. + */ | RGB_TO_OKLAB LPAREN expr (COMMA expr COMMA expr)? RPAREN # RgbToOklabFunc - /** - rgb_to_cielab(r, [g], [b]) - converts RGB to CIELAB. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. - */ + /** + rgb_to_cielab(r, [g], [b]) - converts RGB to CIELAB. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. + */ | RGB_TO_CIELAB LPAREN expr (COMMA expr COMMA expr)? RPAREN # RgbToCielabFunc - /** - rgb_to_hsv(rgb, [degrees]) / rgb_to_hsv(r, g, b, [degrees]) - converts RGB to HSV. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. If degrees is specified and nonzero it returns hue as 0-360 instead of 0-1 - */ + /** + rgb_to_hsv(rgb, [degrees]) / rgb_to_hsv(r, g, b, [degrees]) - converts RGB to HSV. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. If degrees is specified and nonzero it returns hue as 0-360 instead of 0-1 + */ | RGB_TO_HSV LPAREN expr (COMMA expr COMMA expr)? (COMMA expr)? RPAREN # RgbToHsvFunc - /** - hsv_to_rgb(hsv, [degrees]) / hsv_to_rgb(h, s, v, [degrees]) - converts HSV to RGB. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. - */ + /** + hsv_to_rgb(hsv, [degrees]) / hsv_to_rgb(h, s, v, [degrees]) - converts HSV to RGB. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. + */ | HSV_TO_RGB LPAREN expr (COMMA expr COMMA expr)? (COMMA expr)? RPAREN # HsvToRgbFunc - /** - where(condition, x, y) - returns x if condition is true, else y. Runs per element. - */ + /** + where(condition, x, y) - returns x if condition is true, else y. Runs per element. + */ | WHERE LPAREN expr COMMA expr COMMA expr RPAREN # WhereFunc - /** - oklab_to_rgb(oklab) / oklab_to_rgb(l, a, b) - converts OKLab to RGB. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. - */ + /** + oklab_to_rgb(oklab) / oklab_to_rgb(l, a, b) - converts OKLab to RGB. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. + */ | OKLAB_TO_RGB LPAREN expr (COMMA expr COMMA expr)? RPAREN # OklabToRgbFunc - /** - cielab_to_rgb(cielab) / cielab_to_rgb(l, a, b) - converts CIELAB to RGB. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. - */ + /** + cielab_to_rgb(cielab) / cielab_to_rgb(l, a, b) - converts CIELAB to RGB. It has 2 modes. Aither 1 or 3 parameters. With 1 parameter it expects last dimension of 3 and with 3 it concatinates them after converting. + */ | CIELAB_TO_RGB LPAREN expr (COMMA expr COMMA expr)? RPAREN # CielabToRgbFunc - /** - text_image(text, font, size, [max_width], [weight], [rotation_angle], [line_spacing], [italic], [underline]) - renders text to an 2D tensor - */ + /** + text_image(text, font, size, [max_width], [weight], [rotation_angle], [line_spacing], [italic], [underline]) - renders text to an 2D tensor + */ | TEXT_IMAGE LPAREN expr COMMA expr COMMA expr (COMMA expr)? (COMMA expr)? (COMMA expr)? (COMMA expr)? (COMMA expr)? (COMMA expr)? RPAREN # TextImageFunc; func4: - /** - swap(x, dim, idx1, idx2) - swaps elements along dim between idx1 and idx2 - */ + /** + swap(x, dim, idx1, idx2) - swaps elements along dim between idx1 and idx2 + */ SWAP LPAREN expr COMMA expr COMMA expr COMMA expr RPAREN # SwapFunc - /** - nan_to_num(x, nan, pos_inf, neg_inf) - null value replacement - inspired by NVL - */ + /** + nan_to_num(x, nan, pos_inf, neg_inf) - null value replacement - inspired by NVL + */ | NVL LPAREN expr COMMA expr COMMA expr COMMA expr RPAREN # NvlFunc - /** - logspace(start, stop, num, base) - creates logarithmically spaced values - */ + /** + logspace(start, stop, num, base) - creates logarithmically spaced values + */ | LOGSPACE LPAREN expr COMMA expr COMMA expr COMMA expr RPAREN # LogspaceFunc - /** - distance(x1, y1, x2, y2) - computes Euclidean distance between (x1, y1) and (x2, y2) - */ + /** + distance(x1, y1, x2, y2) - computes Euclidean distance between (x1, y1) and (x2, y2) + */ | DIST LPAREN expr COMMA expr COMMA expr COMMA expr RPAREN # DistFunc - /** - histogram(x, bins, min, max) - computes histogram - */ + /** + histogram(x, bins, min, max) - computes histogram + */ | HISTOGRAM LPAREN expr COMMA expr COMMA expr COMMA expr RPAREN # HistogramFunc; func5: - /** - remap(v, i_min, i_max, o_min, o_max) - remaps values from input range to output range - */ + /** + remap(v, i_min, i_max, o_min, o_max) - remaps values from input range to output range + */ REMAP LPAREN expr COMMA expr COMMA expr COMMA expr COMMA expr RPAREN # RemapFunc; // N-argument functions funcN: - /** - smin(x, ...) - computes the minimum of inputs or elements. Tensors must have the same size or be broadcastable to same size. - */ + /** + smin(x, ...) - computes the minimum of inputs or elements. Tensors must have the same size or be broadcastable to same size. + */ SMIN LPAREN expr (COMMA expr)* RPAREN # SMinFunc - /** - smax(x, ...) - computes the maximum of inputs or elements. Tensors must have the same size or be broadcastable to same size. - */ + /** + smax(x, ...) - computes the maximum of inputs or elements. Tensors must have the same size or be broadcastable to same size. + */ | SMAX LPAREN expr (COMMA expr)* RPAREN # SMaxFunc - /** - map(tensor, coord1, [coord2], [coord3]) - samples/remaps input tensor using coordinates - */ + /** + map(tensor, coord1, [coord2], [coord3]) - samples/remaps input tensor using coordinates + */ | MAP LPAREN expr (COMMA expr)+ RPAREN # MapFunc - /** - ezconvolution(tensor, [kernel_sizes...], kernel) - applies convolution with kernel. Attempts to correctly convolve over size dimensions. - */ + /** + ezconvolution(tensor, [kernel_sizes...], kernel) - applies convolution with kernel. Attempts to correctly convolve over size dimensions. + */ | EZCONV LPAREN expr (COMMA expr)+ RPAREN # EzConvFunc - /** - convolution(tensor, [kernel_sizes...], kernel) - applies convolution with kernel. Expects [batch,channel, ...] - */ + /** + convolution(tensor, [kernel_sizes...], kernel) - applies convolution with kernel. Expects [batch,channel, ...] + */ | CONV LPAREN expr (COMMA expr)+ RPAREN # ConvFunc - /** - reshape(x, shape) - reshapes tensor x to the specified shape - */ + /** + reshape(x, shape) - reshapes tensor x to the specified shape + */ | RESHAPE LPAREN expr COMMA expr RPAREN # ReshapeFunc - /** - concatenate(x1, x2, ..., dim) - concatenates tensors along the specified dimension - */ + /** + concatenate(x1, x2, ..., dim) - concatenates tensors along the specified dimension + */ | CONCAT LPAREN expr (COMMA expr)+ RPAREN # ConcatFunc; funcNoise: - /** - random_normal(seed, [shape]) - generates normally distributed random noise - */ + /** + random_normal(seed, [shape]) - generates normally distributed random noise + */ NOISE LPAREN expr (COMMA expr)? RPAREN # NoiseFunc - /** - random_uniform(seed, [shape]) - generates uniformly distributed random noise - */ + /** + random_uniform(seed, [shape]) - generates uniformly distributed random noise + */ | RAND LPAREN expr (COMMA expr)? RPAREN # RandFunc - /** - random_exponential(seed, lambda, [shape]) - generates exponentially distributed random values - */ + /** + random_exponential(seed, lambda, [shape]) - generates exponentially distributed random values + */ | EXPONENTIAL LPAREN expr COMMA expr (COMMA expr)? RPAREN # ExponentialFunc - /** - random_bernoulli(seed, probability, [shape]) - generates Bernoulli distributed random values (0 or 1). Probability can be a tensor - */ + /** + random_bernoulli(seed, probability, [shape]) - generates Bernoulli distributed random values (0 or 1). Probability can be a tensor + */ | BERNOULLI LPAREN expr COMMA expr (COMMA expr)? RPAREN # BernoulliFunc - /** - random_poisson(seed, lambda, [shape]) - generates Poisson distributed random values. - */ + /** + random_poisson(seed, lambda, [shape]) - generates Poisson distributed random values. + */ | POISSON LPAREN expr COMMA expr (COMMA expr)? RPAREN # PoissonFunc - /** - random_cauchy(seed, median, sigma, [shape]) - generates Cauchy distributed random values. - */ + /** + random_cauchy(seed, median, sigma, [shape]) - generates Cauchy distributed random values. + */ | CAUCHY LPAREN expr COMMA expr COMMA expr (COMMA expr)? RPAREN # CauchyFunc - /** - random_log_normal(seed, mean, std, [shape]) - generates log-normally distributed random values. - */ + /** + random_log_normal(seed, mean, std, [shape]) - generates log-normally distributed random values. + */ | LOGNORMAL LPAREN expr COMMA expr COMMA expr (COMMA expr)? RPAREN # LogNormalFunc - /** - random_gamma(seed, shape_param, scale, [shape]) - generates Gamma distributed random values. - */ + /** + random_gamma(seed, shape_param, scale, [shape]) - generates Gamma distributed random values. + */ | GAMMADIST LPAREN expr COMMA expr COMMA expr (COMMA expr)? RPAREN # GammaDistFunc - /** - random_beta(seed, alpha, beta, [shape]) - generates Beta distributed random values - */ + /** + random_beta(seed, alpha, beta, [shape]) - generates Beta distributed random values + */ | BETADIST LPAREN expr COMMA expr COMMA expr (COMMA expr)? RPAREN # BetaDistFunc - /** - random_laplace(seed, loc, scale, [shape]) - generates Laplace distributed random values - */ + /** + random_laplace(seed, loc, scale, [shape]) - generates Laplace distributed random values + */ | LAPLACEDIST LPAREN expr COMMA expr COMMA expr (COMMA expr)? RPAREN # LaplaceDistFunc - /** - random_gumbel(seed, loc, scale, [shape]) - generates Gumbel distributed random values - */ + /** + random_gumbel(seed, loc, scale, [shape]) - generates Gumbel distributed random values + */ | GUMBELDIST LPAREN expr COMMA expr COMMA expr (COMMA expr)? RPAREN # GumbelDistFunc - /** - random_weibull(seed, scale, concentration, [shape]) - generates Weibull distributed random values - */ + /** + random_weibull(seed, scale, concentration, [shape]) - generates Weibull distributed random values + */ | WEIBULLDIST LPAREN expr COMMA expr COMMA expr (COMMA expr)? RPAREN # WeibullDistFunc - /** - random_chi2(seed, df, [shape]) - generates Chi-squared distributed random values - */ + /** + random_chi2(seed, df, [shape]) - generates Chi-squared distributed random values + */ | CHI2DIST LPAREN expr COMMA expr (COMMA expr)? RPAREN # Chi2DistFunc - /** - random_studentt(seed, df, [shape]) - generates Student-t distributed random values - */ + /** + random_studentt(seed, df, [shape]) - generates Student-t distributed random values + */ | STUDENTTDIST LPAREN expr COMMA expr (COMMA expr)? RPAREN # StudentTDistFunc - /** - perlin_noise(seed, scale, [octaves], [offset], [shape]) - generates Perlin noise - */ + /** + perlin_noise(seed, scale, [octaves], [offset], [shape]) - generates Perlin noise + */ | PERLIN LPAREN expr COMMA expr (COMMA expr)? (COMMA expr)? (COMMA expr)? RPAREN # PerlinFunc - /** - cellular_noise(seed, scale, [jitter], [offset], [shape]) - generates Cellular/Voronoi noise - */ + /** + cellular_noise(seed, scale, [jitter], [offset], [shape]) - generates Cellular/Voronoi noise + */ | CELLULAR LPAREN expr COMMA expr (COMMA expr)? (COMMA expr)? (COMMA expr)? RPAREN # CellularFunc - /** - plasma_noise(seed, scale, [octaves], [offset], [shape]) - generates Plasma/Turbulence noise - same as perlin but default octaves 4 - */ + /** + plasma_noise(seed, scale, [octaves], [offset], [shape]) - generates Plasma/Turbulence noise - same as perlin but default octaves 4 + */ | PLASMA LPAREN expr COMMA expr (COMMA expr)? (COMMA expr)? (COMMA expr)? RPAREN # PlasmaFunc - /** - ridged_noise(seed, scale, [octaves], [offset], [shape]) - generates ridged multi-fractal noise - */ + /** + ridged_noise(seed, scale, [octaves], [offset], [shape]) - generates ridged multi-fractal noise + */ | RIDGED LPAREN expr COMMA expr (COMMA expr)? (COMMA expr)? (COMMA expr)? RPAREN # RidgedFunc - /** - domain_warp_noise(seed, scale, warp_scale, warp_strength, [octaves], [warp_octaves], [offset], [shape]) - generates domain warped noise - */ + /** + domain_warp_noise(seed, scale, warp_scale, warp_strength, [octaves], [warp_octaves], [offset], [shape]) - generates domain warped noise + */ | DOMAIN_WARP LPAREN expr COMMA expr COMMA expr COMMA expr (COMMA expr)? (COMMA expr)? (COMMA expr)? (COMMA expr)? RPAREN # DomainWarpFunc; // LEXER RULES @@ -1055,8 +1055,8 @@ RBRACE: '}'; NUMBER: ([0-9]+ ('.' [0-9]*)? | '.' [0-9]+) ([eE][+-]? [0-9]+)?; CONSTANT: ('pi' | 'PI' | 'e' | 'E'); STRING: '"' (~["\\\r\n] | '\\' .)* '"' - | '\'' (~['\\\r\n] | '\\' .)* '\'' - ; + | '\'' (~['\\\r\n] | '\\' .)* '\'' + ; VARIABLE: [a-zA-Z_] [a-zA-Z_0-9]*; SL_COMMENT: '#' ~[\r\n]* -> skip; diff --git a/more_math/Parser/UnifiedMathVisitor.py b/more_math/Parser/UnifiedMathVisitor.py index 90f36ae..85f1973 100644 --- a/more_math/Parser/UnifiedMathVisitor.py +++ b/more_math/Parser/UnifiedMathVisitor.py @@ -1,8 +1,5 @@ -from re import S import time -import numpy as np import os -from numpy._core.multiarray import scalar import torch import math import inspect @@ -18,6 +15,9 @@ from ..helper_functions import generate_dim_variables from .noise_utils import NoiseUtils import struct +from PIL import Image, ImageDraw, ImageFont +import numpy as np + class ReturnSignal: __slots__ = ("value",) @@ -2051,14 +2051,6 @@ class UnifiedMathVisitor(MathExprVisitor): Renders text to a normalised float32 2D tensor [H, W] using Pillow. """ - try: - from PIL import Image, ImageDraw, ImageFont - except ImportError: - raise ImportError( - f"{ctx.start.line}:{ctx.start.column}: " - "text_image() requires the 'Pillow' package. " - "Install it with: pip install Pillow" - ) # --- Evaluate arguments --- def _to_str(v): @@ -2461,7 +2453,6 @@ class UnifiedMathVisitor(MathExprVisitor): img = img.rotate(angle, expand=True, fillcolor=0) # --- Convert to tensor on self.device --- - import numpy as np arr = np.array(img, dtype=np.float32) / 255.0 return torch.from_numpy(arr).to(device=self.device) diff --git a/more_math/Parser/inbuilt_symbols.py b/more_math/Parser/inbuilt_symbols.py index e203727..669e93c 100644 --- a/more_math/Parser/inbuilt_symbols.py +++ b/more_math/Parser/inbuilt_symbols.py @@ -74,8 +74,8 @@ INBUILT_FUNCTION_META = { 'elastic': {'min_args': 3, 'max_args': 3, 'snippet': 'elastic()', 'description': 'elastic_ease(a, b, t) - elastic ease between a and b'}, 'elastic_ease': {'min_args': 3, 'max_args': 3, 'snippet': 'elastic_ease()', 'description': 'elastic_ease(a, b, t) - elastic ease between a and b'}, 'entropy': {'min_args': 1, 'max_args': 1, 'snippet': 'entropy()', 'description': 'entropy(x) - computes shanon entropy'}, - 'erf': {'min_args': 1, 'max_args': 1, 'snippet': 'erf()', 'description': 'erf(x) - computes the error function -- TODO: MORE DESCRIPTIVE --'}, - 'erfinv': {'min_args': 1, 'max_args': 1, 'snippet': 'erfinv()', 'description': 'erfinv(x) - computes the inverse error function -- TODO: MORE DESCRIPTIVE --'}, + 'erf': {'min_args': 1, 'max_args': 1, 'snippet': 'erf()', 'description': 'erf(x) - computes the error function (torch.erf)'}, + 'erfinv': {'min_args': 1, 'max_args': 1, 'snippet': 'erfinv()', 'description': 'erfinv(x) - computes the inverse error function (torch.erfinv)'}, 'erode': {'min_args': 1, 'max_args': 2, 'snippet': 'erode()', 'description': 'erode(x, [kernel]) - applies erosion to x. kernel is size of used matrix.'}, 'exp': {'min_args': 1, 'max_args': 1, 'snippet': 'exp()', 'description': 'exp(x) - applies e^x to value (per element)'}, 'ezconv': {'min_args': 2, 'max_args': None, 'snippet': 'ezconv()', 'description': 'ezconvolution(tensor, [kernel_sizes...], kernel) - applies convolution with kernel. Attempts to correctly convolve over size dimensions.'}, @@ -91,7 +91,7 @@ INBUILT_FUNCTION_META = { 'flow_apply': {'min_args': 2, 'max_args': 2, 'snippet': 'flow_apply()', 'description': 'flow_apply(image, flow) - applies optical flow to an image'}, 'flow_mag': {'min_args': 1, 'max_args': 1, 'snippet': 'flow_mag()', 'description': 'flow_magnitude(x) - computes the magnitude of optical flow (or any other structure with last dimension of 2)'}, 'flow_magnitude': {'min_args': 1, 'max_args': 1, 'snippet': 'flow_magnitude()', 'description': 'flow_magnitude(x) - computes the magnitude of optical flow (or any other structure with last dimension of 2)'}, - 'flow_to_image': {'min_args': 1, 'max_args': 1, 'snippet': 'flow_to_image()', 'description': 'flow_to_image(x) - converts optical flow x to an RGB image. Up-Down is Left-right '}, + 'flow_to_image': {'min_args': 1, 'max_args': 1, 'snippet': 'flow_to_image()', 'description': 'flow_to_image(x) - converts optical flow x to an RGB image. Up-Down is blue-yellow Left-right green-magenta.'}, 'fract': {'min_args': 1, 'max_args': 1, 'snippet': 'fract()', 'description': 'fract(x) - returns the fractional part of x: x - floor(x)'}, 'gamma': {'min_args': 1, 'max_args': 1, 'snippet': 'gamma()', 'description': 'gamma(x) - computes the gamma function (per element)'}, 'gaussian': {'min_args': 2, 'max_args': 3, 'snippet': 'gaussian()', 'description': 'gaussian(x, sigma, [reshape]) - applies a Gaussian blur to x with specified sigma. if reshape has value of 1.0, then it tries orienting the input such that channel is in the direction of filter. Otherwise it expect color dimension to be the last.'}, @@ -215,7 +215,7 @@ INBUILT_FUNCTION_META = { 'sqrt': {'min_args': 1, 'max_args': 1, 'snippet': 'sqrt()', 'description': 'sqrt(x) - applies sqere root per element. Negative numbers return NaN (not a number)'}, 'stack_clear': {'min_args': 1, 'max_args': 1, 'snippet': 'stack_clear()', 'description': 'stack_clear(slot) - clears the contents of slot stack'}, 'stack_get': {'min_args': 1, 'max_args': 1, 'snippet': 'stack_get()', 'description': 'stack_get(slot) - returns the last pushed value in slot without popping it'}, - 'stack_has': {'min_args': 1, 'max_args': 1, 'snippet': 'stack_has()', 'description': 'stack_has(slot) - returns 1.0 if slot exists in state storage and is not empty else returns 0.0'}, + 'stack_has': {'min_args': 1, 'max_args': 1, 'snippet': 'stack_has()', 'description': 'stack_has(slot) - returns 1.0 if slot exists in stack and is not empty else returns 0.0'}, 'stack_pop': {'min_args': 1, 'max_args': 1, 'snippet': 'stack_pop()', 'description': 'stack_pop(slot) - removes and returns the last element of slot stack'}, 'stack_push': {'min_args': 2, 'max_args': 2, 'snippet': 'stack_push()', 'description': 'stack_push(slot, val) - pushes val onto the end of slot stack'}, 'std': {'min_args': 1, 'max_args': 1, 'snippet': 'std()', 'description': 'std(x) - computes the standard deviation of elements of x'}, @@ -237,7 +237,7 @@ INBUILT_FUNCTION_META = { 'topk_indices': {'min_args': 2, 'max_args': 2, 'snippet': 'topk_indices()', 'description': 'topk_indices(x, k) - returns the indices of the k largest elements of x'}, 'trim': {'min_args': 1, 'max_args': 1, 'snippet': 'trim()', 'description': 'trim(x) - removes leading and trailing whitespace from string'}, 'turbulence': {'min_args': 2, 'max_args': 5, 'snippet': 'turbulence()', 'description': 'plasma_noise(seed, scale, [octaves], [offset], [shape]) - generates Plasma/Turbulence noise - same as perlin but default octaves 4'}, - 'unique': {'min_args': 1, 'max_args': 1, 'snippet': 'unique()', 'description': 'unique(x) - returns the unique elements (sorted,) --TODO: FINISH --'}, + 'unique': {'min_args': 1, 'max_args': 1, 'snippet': 'unique()', 'description': 'unique(x) - returns the unique elements (sorted, flatened)'}, 'upper': {'min_args': 1, 'max_args': 1, 'snippet': 'upper()', 'description': 'upper(x) - converts string to uppercase'}, 'var': {'min_args': 1, 'max_args': 1, 'snippet': 'var()', 'description': 'var(x) - computes the variance of elements of x'}, 'voronoi': {'min_args': 2, 'max_args': 5, 'snippet': 'voronoi()', 'description': 'cellular_noise(seed, scale, [jitter], [offset], [shape]) - generates Cellular/Voronoi noise'}, diff --git a/pyproject.toml b/pyproject.toml index 87ad421..17e9282 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "more_math" -version = "1.0.1" +version = "1.1.0" tooltip = "Adds math nodes for numbers and types which do not need it." authors = [ {name = "Daniel Martinek", email = "danda.martinek@gmail.com"} diff --git a/web/inbuilt_symbols.js b/web/inbuilt_symbols.js index 7348110..98e49af 100644 --- a/web/inbuilt_symbols.js +++ b/web/inbuilt_symbols.js @@ -77,8 +77,8 @@ export const FUNCTION_META = { elastic: { minArgs: 3, maxArgs: 3, snippet: "elastic()", description: "elastic_ease(a, b, t) - elastic ease between a and b" }, elastic_ease: { minArgs: 3, maxArgs: 3, snippet: "elastic_ease()", description: "elastic_ease(a, b, t) - elastic ease between a and b" }, entropy: { minArgs: 1, maxArgs: 1, snippet: "entropy()", description: "entropy(x) - computes shanon entropy" }, - erf: { minArgs: 1, maxArgs: 1, snippet: "erf()", description: "erf(x) - computes the error function -- TODO: MORE DESCRIPTIVE --" }, - erfinv: { minArgs: 1, maxArgs: 1, snippet: "erfinv()", description: "erfinv(x) - computes the inverse error function -- TODO: MORE DESCRIPTIVE --" }, + erf: { minArgs: 1, maxArgs: 1, snippet: "erf()", description: "erf(x) - computes the error function (torch.erf)" }, + erfinv: { minArgs: 1, maxArgs: 1, snippet: "erfinv()", description: "erfinv(x) - computes the inverse error function (torch.erfinv)" }, erode: { minArgs: 1, maxArgs: 2, snippet: "erode()", description: "erode(x, [kernel]) - applies erosion to x. kernel is size of used matrix." }, exp: { minArgs: 1, maxArgs: 1, snippet: "exp()", description: "exp(x) - applies e^x to value (per element)" }, ezconv: { minArgs: 2, maxArgs: null, snippet: "ezconv()", description: "ezconvolution(tensor, [kernel_sizes...], kernel) - applies convolution with kernel. Attempts to correctly convolve over size dimensions." }, @@ -94,7 +94,7 @@ export const FUNCTION_META = { flow_apply: { minArgs: 2, maxArgs: 2, snippet: "flow_apply()", description: "flow_apply(image, flow) - applies optical flow to an image" }, flow_mag: { minArgs: 1, maxArgs: 1, snippet: "flow_mag()", description: "flow_magnitude(x) - computes the magnitude of optical flow (or any other structure with last dimension of 2)" }, flow_magnitude: { minArgs: 1, maxArgs: 1, snippet: "flow_magnitude()", description: "flow_magnitude(x) - computes the magnitude of optical flow (or any other structure with last dimension of 2)" }, - flow_to_image: { minArgs: 1, maxArgs: 1, snippet: "flow_to_image()", description: "flow_to_image(x) - converts optical flow x to an RGB image. Up-Down is Left-right " }, + flow_to_image: { minArgs: 1, maxArgs: 1, snippet: "flow_to_image()", description: "flow_to_image(x) - converts optical flow x to an RGB image. Up-Down is blue-yellow Left-right green-magenta." }, fract: { minArgs: 1, maxArgs: 1, snippet: "fract()", description: "fract(x) - returns the fractional part of x: x - floor(x)" }, gamma: { minArgs: 1, maxArgs: 1, snippet: "gamma()", description: "gamma(x) - computes the gamma function (per element)" }, gaussian: { minArgs: 2, maxArgs: 3, snippet: "gaussian()", description: "gaussian(x, sigma, [reshape]) - applies a Gaussian blur to x with specified sigma. if reshape has value of 1.0, then it tries orienting the input such that channel is in the direction of filter. Otherwise it expect color dimension to be the last." }, @@ -218,7 +218,7 @@ export const FUNCTION_META = { sqrt: { minArgs: 1, maxArgs: 1, snippet: "sqrt()", description: "sqrt(x) - applies sqere root per element. Negative numbers return NaN (not a number)" }, stack_clear: { minArgs: 1, maxArgs: 1, snippet: "stack_clear()", description: "stack_clear(slot) - clears the contents of slot stack" }, stack_get: { minArgs: 1, maxArgs: 1, snippet: "stack_get()", description: "stack_get(slot) - returns the last pushed value in slot without popping it" }, - stack_has: { minArgs: 1, maxArgs: 1, snippet: "stack_has()", description: "stack_has(slot) - returns 1.0 if slot exists in state storage and is not empty else returns 0.0" }, + stack_has: { minArgs: 1, maxArgs: 1, snippet: "stack_has()", description: "stack_has(slot) - returns 1.0 if slot exists in stack and is not empty else returns 0.0" }, stack_pop: { minArgs: 1, maxArgs: 1, snippet: "stack_pop()", description: "stack_pop(slot) - removes and returns the last element of slot stack" }, stack_push: { minArgs: 2, maxArgs: 2, snippet: "stack_push()", description: "stack_push(slot, val) - pushes val onto the end of slot stack" }, std: { minArgs: 1, maxArgs: 1, snippet: "std()", description: "std(x) - computes the standard deviation of elements of x" }, @@ -240,7 +240,7 @@ export const FUNCTION_META = { topk_indices: { minArgs: 2, maxArgs: 2, snippet: "topk_indices()", description: "topk_indices(x, k) - returns the indices of the k largest elements of x" }, trim: { minArgs: 1, maxArgs: 1, snippet: "trim()", description: "trim(x) - removes leading and trailing whitespace from string" }, turbulence: { minArgs: 2, maxArgs: 5, snippet: "turbulence()", description: "plasma_noise(seed, scale, [octaves], [offset], [shape]) - generates Plasma/Turbulence noise - same as perlin but default octaves 4" }, - unique: { minArgs: 1, maxArgs: 1, snippet: "unique()", description: "unique(x) - returns the unique elements (sorted,) --TODO: FINISH --" }, + unique: { minArgs: 1, maxArgs: 1, snippet: "unique()", description: "unique(x) - returns the unique elements (sorted, flatened)" }, upper: { minArgs: 1, maxArgs: 1, snippet: "upper()", description: "upper(x) - converts string to uppercase" }, var: { minArgs: 1, maxArgs: 1, snippet: "var()", description: "var(x) - computes the variance of elements of x" }, voronoi: { minArgs: 2, maxArgs: 5, snippet: "voronoi()", description: "cellular_noise(seed, scale, [jitter], [offset], [shape]) - generates Cellular/Voronoi noise" }, diff --git a/web/script_text_input.V1.js.m b/web/script_text_input.V1.js.m deleted file mode 100644 index 1213ed6..0000000 --- a/web/script_text_input.V1.js.m +++ /dev/null @@ -1,514 +0,0 @@ -import { app } from "/scripts/app.js"; - -const NODE_IDS = new Set(["mrmth_ScriptInput"]); -const NODE_NAMES = new Set(["ScriptTextInput"]); -const STYLE_ID = "mrmth-script-line-numbers"; - -const KEYWORDS = new Set(["if", "else", "while", "for", "in", "break", "continue", "return", "none", "None", "null", "NULL"]); -const CONSTANTS = new Set(["pi", "PI", "e", "E"]); -const FUNCTIONS = new Set([ - "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", - "abs", "sqrt", "ln", "log", "exp", "smin", "smax", "tmin", "tmax", "tnorm", "snorm", "floor", "ceil", - "round", "gamma", "pow", "sigm", "clamp", "fft", "ifft", "angle", "print", "print_shape", "pshp", "nvl", - "nan_to_num", "lerp", "step", "smoothstep", "fract", "relu", "softplus", "gelu", "sign", "map", "ezconvolution", - "ezconv", "convolution", "conv", "swap", "permute", "perm", "reshape", "rshp", "range", "topk", "botk", "pinv", - "sum", "mean", "std", "var", "quartile", "quartil", "percentile", "prcnt", "quantile", "dot", "moment","erf","erfinv", "any", - "all", "edge", "blur", "gaussian", "median", "mode", "cumsum", "cumprod", "topk_ind", "topk_indices", "botk_ind", - "botk_indices", "cubic_ease", "cubic", "elastic_ease", "elastic", "sine_ease", "sine", "smootherstep", "dist", - "distance", "remap", "cossim", "cosine_similarity", "count", "cnt", "length", "flatten", "append", "get_value", - "flow_apply", "batch_shuffle", "shuffle", "motion_mask", "flow_to_image", "overlay", "pad", "cross", "matmul", "rife", - "bnot", "bitwise_not", "bitcount", "popcount", "popcnt", "shape", "band", "bitwise_and", "bxor", "bitwise_xor", - "bor", "bitwise_or", "tensor", "stack_push", "stack_pop", "stack_clear", "stack_has", "stack_get", "timestamp","now", - "sort", "argsort", "argmin", "argmax", "softmax", "softmin", "unique", "flip", "cov", "corr", "correlation", "entropy", - "crop", "cat", "concatenate", "concat", "float","int", "linspace", "logspace", "roll","select", - "noise", "randn", "random_normal", "rand", "randu", "random_uniform", "randc", "random_cauchy", "rande", - "random_exponential", "randln", "random_log_normal", "randb", "random_bernoulli", "randp", "random_poisson", "randg", - "random_gamma", "randbeta", "random_beta", "randl", "random_laplace", "randgumbel", "random_gumbel", "randw", - "random_weibull", "randchi2", "random_chi2", "randt", "random_studentt", "perlin", "perlin_noise", "cellular", "voronoi", - "worley", "cellular_noise", "voronoi_noise", "plasma", "turbulence", "plasma_noise", "ridged", "ridged_noise", - "domain_warp", "domain_warp_noise", - "upper", "lower", "split", "join", "substring", "substr", "find", "trim", "replace", - "dilate", "erode", "morph_open", "morph_close", - "rgb_to_hsv", "hsv_to_rgb", - "rgb_to_oklab", "oklab_to_rgb", "rgb_to_cielab", "cielab_to_rgb", - "int_to_rgb", "rgb_to_int", - "where", "histogram", "hist", "flow_mag", "flow_magnitude", "flow_ang", "flow_angle", - "interpolate_linear", "interpolate_area", "interpolate_nearest","interpolate_nearest_exact"]); - -const BRACKET_PAIRS = { - '(': ')', - '[': ']', - '{': '}' -}; -const OPENING_BRACKETS = new Set(['(', '[', '{']); -const CLOSING_BRACKETS = new Set([')', ']', '}']); - -function escapeHtml(value) { - return value.replace(/[&<>"']/g, (ch) => { - switch (ch) { - case "&": - return "&"; - case "<": - return "<"; - case ">": - return ">"; - case '"': - return """; - case "'": - return "'"; - default: - return ch; - } - }); -} - -function tokenize(text) { - const tokens = []; - const pattern = /#.*|\/\*[\s\S]*?\*\/|"(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*'|\b\d+(?:\.\d*)?(?:[eE][+-]?\d+)?\b|\B\.\d+(?:[eE][+-]?\d+)?\b|==|!=|>=|<=|<<|>>|->|\+=|-=|\*=|\/=|%=|[+\-*/%^=<>|?:,;()\[\]{}]|\b[a-zA-Z_][a-zA-Z_0-9]*\b|\s+|./g; - let match; - const bracketStack = []; - const depthByType = { '(': 0, '[': 0, '{': 0 }; - - while ((match = pattern.exec(text)) !== null) { - const value = match[0]; - let type = "text"; - let depth = 0; - - if (value.startsWith("#") || value.startsWith("/*")) { - type = "comment"; - } else if (value.startsWith('"') || value.startsWith("'")) { - type = "string"; - } else if (OPENING_BRACKETS.has(value)) { - depth = depthByType[value]; - depthByType[value]++; - bracketStack.push(value); - type = "bracket"; - } else if (CLOSING_BRACKETS.has(value)) { - if (bracketStack.length > 0) { - const lastOpen = bracketStack[bracketStack.length - 1]; - if (BRACKET_PAIRS[lastOpen] === value) { - // Use depth of bracket type being closed, before decrement - depth = depthByType[lastOpen] - 1; - depthByType[lastOpen]--; - bracketStack.pop(); - } else { - // Mismatched closing bracket - depth = 0; - } - } else { - // Unmatched closing bracket - depth = 0; - } - type = "bracket"; - } else if (/^[+\-*/%^=<>|?:,;]|==|!=|>=|<=|<<|>>|->$/.test(value)) { - type = "operator"; - } else if (/^(?:\d+\.\d*|\d+|\.\d+)(?:[eE][+-]?\d+)?$/.test(value)) { - type = "number"; - } else if (/^[a-zA-Z_]/.test(value)) { - if (KEYWORDS.has(value)) { - type = "keyword"; - } else if (CONSTANTS.has(value)) { - type = "constant"; - } else if (FUNCTIONS.has(value)) { - type = "function"; - } else { - type = "variable"; - } - } - tokens.push({ type, value, depth }); - } - return tokens; -} - -function renderHighlight(text) { - const tokens = tokenize(text); - return tokens - .map((token) => { - if (token.type === "text") { - return escapeHtml(token.value); - } - if (token.type === "bracket") { - const depthClass = token.depth % 6; - return `${escapeHtml(token.value)}`; - } - return `${escapeHtml(token.value)}`; - }) - .join(""); -} - -function hasScriptInput(nodeData) { - const input = nodeData?.input || {}; - const required = input.required || {}; - const optional = input.optional || {}; - return Object.prototype.hasOwnProperty.call(required, "script") || Object.prototype.hasOwnProperty.call(optional, "script"); -} - -function isTargetNode(nodeData) { - if (!nodeData) { - return false; - } - if (NODE_IDS.has(nodeData.node_id)) { - return true; - } - if (NODE_IDS.has(nodeData.name)) { - return true; - } - if (NODE_NAMES.has(nodeData.name)) { - return true; - } - if (NODE_NAMES.has(nodeData.display_name)) { - return true; - } - return hasScriptInput(nodeData); -} - -function ensureStyles() { - if (document.getElementById(STYLE_ID)) { - return; - } - - const style = document.createElement("style"); - style.id = STYLE_ID; - style.textContent = ` - .mrmth-line-editor { - display: flex; - align-items: stretch; - width: 100%; - height: 100%; - min-height: 100%; - gap: 0; - overflow: hidden; - } - .mrmth-line-numbers { - padding: 6px 8px; - text-align: right; - user-select: none; - color: #9aa0a6; - background: rgba(0, 0, 0, 0.4); - border-right: 1px solid rgba(255, 255, 255, 0.1); - font-family: monospace; - font-size: 12px; - line-height: 1.4; - white-space: pre; - overflow: hidden; - box-sizing: border-box; - } - .mrmth-line-numbers-content { - white-space: pre; - } - .mrmth-editor-container { - position: relative; - flex: 1; - min-height: 30px; - overflow: hidden; - } - .mrmth-line-input { - position: relative; - z-index: 2; - flex: 1; - width: 100%; - height: 100%; - box-sizing: border-box; - font-family: monospace; - line-height: 1.4; - white-space: pre-wrap; - word-wrap: break-word; - resize: none; - background: transparent; - color: transparent; - } - .mrmth-syntax-layer { - position: absolute; - inset: 0; - margin: 0; - z-index: 1; - pointer-events: none; - white-space: pre-wrap; - word-wrap: break-word; - overflow: hidden; - mix-blend-mode: normal; - opacity: 1; - } - .mrmth-token-comment { color: #7f8c8d; } - .mrmth-token-number { color: #f39c12; } - .mrmth-token-constant { color: #f1c40f; } - .mrmth-token-keyword { color: #e74c3c; } - .mrmth-token-function { color: #8e44ad; } - .mrmth-token-variable { color: #ecf0f1; } - .mrmth-token-operator { color: #95a5a6; } - .mrmth-token-string { color: #4ea658; } - .mrmth-token-bracket { font-weight: bold; } - .mrmth-bracket-0 { color: #ffd700; } - .mrmth-bracket-1 { color: #da70d6; } - .mrmth-bracket-2 { color: #87cefa; } - .mrmth-bracket-3 { color: #98fb98; } - .mrmth-bracket-4 { color: #ff6347; } - .mrmth-bracket-5 { color: #ffa500; } - `; - document.head.appendChild(style); -} - -function measureLineHeight(inputEl) { - const probe = document.createElement("span"); - const style = getComputedStyle(inputEl); - probe.style.position = "absolute"; - probe.style.visibility = "hidden"; - probe.style.whiteSpace = "pre"; - probe.style.fontFamily = style.fontFamily; - probe.style.fontSize = style.fontSize; - probe.style.fontWeight = style.fontWeight; - probe.style.fontStyle = style.fontStyle; - probe.style.lineHeight = style.lineHeight; - probe.textContent = "A"; - document.body.appendChild(probe); - const height = probe.getBoundingClientRect().height; - document.body.removeChild(probe); - return height; -} - -function attachLineNumbers(widget) { - const inputEl = widget?.inputEl; - if (!inputEl || inputEl.dataset.mrmthLineNumbers) { - return false; - } - - const parent = inputEl.parentElement; - if (!parent) { - return false; - } - - ensureStyles(); - - const wrapper = document.createElement("div"); - wrapper.className = "mrmth-line-editor"; - - const gutter = document.createElement("div"); - gutter.className = "mrmth-line-numbers"; - - const gutterContent = document.createElement("div"); - gutterContent.className = "mrmth-line-numbers-content"; - - const editorContainer = document.createElement("div"); - editorContainer.className = "mrmth-editor-container"; - - const syntaxLayer = document.createElement("pre"); - syntaxLayer.className = "mrmth-syntax-layer"; - - parent.replaceChild(wrapper, inputEl); - wrapper.appendChild(gutter); - gutter.appendChild(gutterContent); - wrapper.appendChild(editorContainer); - editorContainer.appendChild(syntaxLayer); - editorContainer.appendChild(inputEl); - - wrapper.style.height = "100%"; - parent.style.height = "100%"; - - inputEl.classList.add("mrmth-line-input"); - inputEl.dataset.mrmthLineNumbers = "true"; - - const inputStyle = getComputedStyle(inputEl); - const lineHeightPx = measureLineHeight(inputEl); - - const applyAlpha = (color, alpha) => { - const rgbaMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/i); - if (!rgbaMatch) { - return `rgba(0, 0, 0, ${alpha})`; - } - const r = Number(rgbaMatch[1]); - const g = Number(rgbaMatch[2]); - const b = Number(rgbaMatch[3]); - return `rgba(${r}, ${g}, ${b}, ${alpha})`; - }; - - const baseBackground = inputStyle.backgroundColor || "rgba(0, 0, 0, 0.4)"; - const overlayBackground = applyAlpha(baseBackground, 0.4); - - syntaxLayer.style.height = "100%"; - syntaxLayer.style.background = "transparent"; - inputEl.style.height = "100%"; - inputEl.style.background = overlayBackground; - - gutter.style.fontFamily = inputStyle.fontFamily; - gutter.style.fontSize = inputStyle.fontSize; - gutter.style.lineHeight = inputStyle.lineHeight; - gutter.style.paddingTop = inputStyle.paddingTop; - gutter.style.paddingBottom = inputStyle.paddingBottom; - - syntaxLayer.style.fontFamily = inputStyle.fontFamily; - syntaxLayer.style.fontSize = inputStyle.fontSize; - syntaxLayer.style.lineHeight = inputStyle.lineHeight; - syntaxLayer.style.padding = `${inputStyle.paddingTop} ${inputStyle.paddingRight} ${inputStyle.paddingBottom} ${inputStyle.paddingLeft}`; - syntaxLayer.style.width = "100%"; - syntaxLayer.style.boxSizing = "border-box"; - syntaxLayer.style.overflowY = "hidden"; - - inputEl.style.background = overlayBackground; - inputEl.style.color = "transparent"; - inputEl.style.caretColor = "#ffffff"; - inputEl.style.mixBlendMode = "lighten"; - - const updateNumbers = () => { - const rawLines = inputEl.value.split("\n"); - const textLineCount = Math.max(1, rawLines.length); - - const gutterLines = []; - - // Sync syntaxLayer width to match inputEl's actual content width - const scrollbarWidth = inputEl.offsetWidth - inputEl.clientWidth; - syntaxLayer.style.width = `calc(100% - ${scrollbarWidth}px)`; - - // Create a temporary clone - const measureClone = syntaxLayer.cloneNode(false); - measureClone.style.position = "absolute"; - measureClone.style.visibility = "hidden"; - measureClone.style.width = syntaxLayer.style.width; - measureClone.style.height = "auto"; - syntaxLayer.parentElement.appendChild(measureClone); - - // Build content with simple marker at start of each line - let html = ""; - for (let i = 0; i < textLineCount; i++) { - html += ``; - html += renderHighlight(rawLines[i] || " "); - if (i < textLineCount - 1) { - html += "\n"; - } - } - measureClone.innerHTML = html; - - // Simply read offsetTop of each marker - const totalHeight = measureClone.offsetHeight; - - for (let i = 0; i < textLineCount; i++) { - const marker = measureClone.querySelector(`[data-ln="${i}"]`); - const nextMarker = i < textLineCount - 1 ? measureClone.querySelector(`[data-ln="${i + 1}"]`) : null; - - const currentY = marker ? marker.offsetTop : i * lineHeightPx; - const nextY = nextMarker ? nextMarker.offsetTop : totalHeight; - - const heightDiff = nextY - currentY; - const visualLines = Math.max(1, Math.round(heightDiff / lineHeightPx)); - - gutterLines.push(String(i + 1)); - - for (let j = 1; j < visualLines; j++) { - gutterLines.push(" "); - } - } - - measureClone.remove(); - - gutterContent.textContent = gutterLines.join("\n"); - - gutter.style.height = `${inputEl.clientHeight}px`; - gutterContent.style.transform = `translateY(${-inputEl.scrollTop}px)`; - }; - - const updateHighlight = () => { - const sourceText = inputEl.value.endsWith("\n") ? `${inputEl.value} ` : inputEl.value; - const highlighted = renderHighlight(sourceText); - syntaxLayer.innerHTML = highlighted; - syntaxLayer.scrollTop = inputEl.scrollTop; - syntaxLayer.scrollLeft = inputEl.scrollLeft; - }; - - const handleBracketAutoClose = (e) => { - const key = e.key; - if (!OPENING_BRACKETS.has(key)) { - return; - } - - const selectionStart = inputEl.selectionStart; - const selectionEnd = inputEl.selectionEnd; - - if (selectionStart !== selectionEnd) { - return; - } - - const text = inputEl.value; - const charAfter = text[selectionStart] || ''; - - // Only prevent auto-close if the MATCHING closing bracket is after cursor - const closingBracket = BRACKET_PAIRS[key]; - const isMatchingClosingBracket = charAfter === closingBracket; - - // Prevent auto-close if next char is alphanumeric or a matching closing bracket - if (/[a-zA-Z0-9_]/.test(charAfter) || isMatchingClosingBracket) { - return; - } - - e.preventDefault(); - - const before = text.substring(0, selectionStart); - const after = text.substring(selectionStart); - inputEl.value = before + key + closingBracket + after; - inputEl.selectionStart = inputEl.selectionEnd = selectionStart + 1; - - updateNumbers(); - updateHighlight(); - - const event = new Event('input', { bubbles: true }); - inputEl.dispatchEvent(event); - }; - - inputEl.addEventListener("keydown", handleBracketAutoClose); - - inputEl.addEventListener("input", () => { - updateNumbers(); - updateHighlight(); - }); - inputEl.addEventListener("scroll", () => { - gutterContent.style.transform = `translateY(${-inputEl.scrollTop}px)`; - syntaxLayer.scrollTop = inputEl.scrollTop; - syntaxLayer.scrollLeft = inputEl.scrollLeft; - }); - - if (window.ResizeObserver) { - const resizeObserver = new ResizeObserver(() => { - updateNumbers(); - updateHighlight(); - }); - resizeObserver.observe(inputEl); - } - - requestAnimationFrame(() => { - updateNumbers(); - updateHighlight(); - }); - - return true; -} - -function attachLineNumbersWithRetry(node) { - const widget = node.widgets?.find((w) => w.name === "script"); - if (attachLineNumbers(widget)) { - return; - } - - requestAnimationFrame(() => { - const retryWidget = node.widgets?.find((w) => w.name === "script"); - attachLineNumbers(retryWidget); - }); -} - -app.registerExtension({ - name: "mrmth.ScriptTextInput.LineNumbers", - async beforeRegisterNodeDef(nodeType, nodeData) { - if (!isTargetNode(nodeData)) { - return; - } - - const onNodeCreated = nodeType.prototype.onNodeCreated; - nodeType.prototype.onNodeCreated = function () { - onNodeCreated?.apply(this, arguments); - attachLineNumbersWithRetry(this); - }; - - const onConfigure = nodeType.prototype.onConfigure; - nodeType.prototype.onConfigure = function () { - onConfigure?.apply(this, arguments); - attachLineNumbersWithRetry(this); - }; - }, -}); diff --git a/web/script_text_input.V2.js.m b/web/script_text_input.V2.js.m deleted file mode 100644 index dfc507f..0000000 --- a/web/script_text_input.V2.js.m +++ /dev/null @@ -1,187 +0,0 @@ -import { app } from "/scripts/app.js"; - -const STYLE_ID = "mrmth-script-line-numbers-v2"; - -const BRACKET_PAIRS = { "(": ")", "[": "]", "{": "}" }; -const OPENING_BRACKETS = new Set(["(", "[", "{"]); - -function ensureStyles() { - if (document.getElementById(STYLE_ID)) return; - - const style = document.createElement("style"); - style.id = STYLE_ID; - style.textContent = ` - .mrmth-v2-editor { - display: flex; - width: 100%; - min-height: 160px; - border: 1px solid rgba(255,255,255,0.12); - border-radius: 8px; - overflow: hidden; - } - .mrmth-v2-gutter { - padding: 8px 10px; - min-width: 40px; - text-align: right; - user-select: none; - color: #9aa0a6; - background: rgba(0,0,0,0.35); - border-right: 1px solid rgba(255,255,255,0.12); - font-family: monospace; - font-size: 12px; - line-height: 1.45; - white-space: pre; - box-sizing: border-box; - } - .mrmth-v2-textarea { - flex: 1; - min-height: 160px !important; - border: 0 !important; - outline: none !important; - resize: vertical !important; - font-family: monospace !important; - font-size: 12px !important; - line-height: 1.45 !important; - padding: 8px 10px !important; - box-sizing: border-box !important; - } - `; - document.head.appendChild(style); -} - -function hasNearbyScriptLabel(el) { - const container = el.closest("div, section, article"); - if (!container) return false; - - // text labels around the textarea - const candidates = container.querySelectorAll("label, .label, .input-label, .property_name, span, div"); - for (const n of candidates) { - const t = (n.textContent || "").trim().toLowerCase(); - if (t === "script") return true; - } - return false; -} - -function isLikelyScriptTextarea(el) { - if (!(el instanceof HTMLTextAreaElement)) return false; - if (el.dataset.mrmthV2Script === "1") return false; - - const attrs = [ - el.name, - el.id, - el.getAttribute("data-name"), - el.getAttribute("aria-label"), - el.getAttribute("placeholder"), - ] - .filter(Boolean) - .join(" ") - .toLowerCase(); - - // accept either direct attribute match OR nearby label - return attrs.includes("script") || hasNearbyScriptLabel(el); -} - -function updateGutter(textarea, gutter) { - const lineCount = Math.max(1, textarea.value.split("\n").length); - const lines = []; - for (let i = 1; i <= lineCount; i++) lines.push(String(i)); - gutter.textContent = lines.join("\n"); - gutter.style.transform = `translateY(${-textarea.scrollTop}px)`; -} - -function addBracketAutoClose(textarea) { - textarea.addEventListener("keydown", (e) => { - const key = e.key; - if (!OPENING_BRACKETS.has(key)) return; - - const start = textarea.selectionStart; - const end = textarea.selectionEnd; - if (start !== end) return; - - const after = textarea.value[start] ?? ""; - const closing = BRACKET_PAIRS[key]; - - if (/[a-zA-Z0-9_]/.test(after) || after === closing) return; - - e.preventDefault(); - const before = textarea.value.slice(0, start); - const tail = textarea.value.slice(start); - textarea.value = `${before}${key}${closing}${tail}`; - textarea.selectionStart = textarea.selectionEnd = start + 1; - textarea.dispatchEvent(new Event("input", { bubbles: true })); - }); -} - -function hideNearbyScriptLabels(textarea) { - const root = textarea.closest("div, section, article") ?? textarea.parentElement; - if (!root) return; - - const labels = root.querySelectorAll("label, .label, .input-label, .property_name, .v-label, span, div"); - for (const el of labels) { - if (el.dataset.mrmthHiddenLabel === "1") continue; - const t = (el.textContent || "").trim().toLowerCase(); - if (t === "script") { - el.style.display = "none"; - el.dataset.mrmthHiddenLabel = "1"; - } - } -} - -function attachToTextarea(textarea) { - if (!isLikelyScriptTextarea(textarea)) return; - - const parent = textarea.parentElement; - if (!parent) return; - - ensureStyles(); - hideNearbyScriptLabels(textarea); - - const wrapper = document.createElement("div"); - wrapper.className = "mrmth-v2-editor"; - - const gutter = document.createElement("div"); - gutter.className = "mrmth-v2-gutter"; - - parent.replaceChild(wrapper, textarea); - wrapper.appendChild(gutter); - wrapper.appendChild(textarea); - - textarea.classList.add("mrmth-v2-textarea"); - textarea.dataset.mrmthV2Script = "1"; - - // disable red spell-check underline for code - textarea.spellcheck = false; - textarea.autocapitalize = "off"; - textarea.autocomplete = "off"; - textarea.autocorrect = "off"; - - const refresh = () => updateGutter(textarea, gutter); - textarea.addEventListener("input", refresh); - textarea.addEventListener("scroll", refresh); - - addBracketAutoClose(textarea); - refresh(); -} - -function scan(root = document) { - root.querySelectorAll("textarea").forEach(attachToTextarea); -} - -app.registerExtension({ - name: "mrmth.ScriptTextInput.V2", - async setup() { - scan(document); - - const observer = new MutationObserver((mutations) => { - for (const m of mutations) { - for (const node of m.addedNodes) { - if (!(node instanceof Element)) continue; - if (node.matches?.("textarea")) attachToTextarea(node); - scan(node); - } - } - }); - - observer.observe(document.body, { childList: true, subtree: true }); - }, -});