from __future__ import annotations import numpy as np from backend.node_registry import register_node from backend.data_types import DataField @register_node(display_name="Field Arithmetic") class FieldArithmetic: @classmethod def INPUT_TYPES(cls): return { "required": { "field_a": ("DATA_FIELD",), "field_b": ("DATA_FIELD",), "operation": (["add", "subtract", "multiply", "divide", "min", "max", "hypot"],), } } OUTPUTS = ( ('DATA_FIELD', 'result'), ) FUNCTION = "process" DESCRIPTION = ( "Apply a point-wise arithmetic operation to two DATA_FIELDs of the same resolution. " "add/subtract/multiply/divide/min/max perform element-wise operations; " "hypot computes sqrt(a² + b²) per pixel. " "Equivalent to gwy_data_field_sum_fields / subtract_fields / multiply_fields / " "divide_fields / min_of_fields / max_of_fields / hypot_of_fields in arithmetic.c." ) def process(self, field_a: DataField, field_b: DataField, operation: str) -> tuple: if field_a.data.shape != field_b.data.shape: raise ValueError( f"Fields must have the same resolution: " f"{field_a.data.shape} vs {field_b.data.shape}" ) a = field_a.data b = field_b.data if operation == "add": result = a + b elif operation == "subtract": result = a - b elif operation == "multiply": result = a * b elif operation == "divide": result = a / b elif operation == "min": result = np.minimum(a, b) elif operation == "max": result = np.maximum(a, b) elif operation == "hypot": result = np.hypot(a, b) else: raise ValueError(f"Unknown operation: {operation!r}") return (field_a.replace(data=result),)