Files
Ewald 831b072943 Add professional compositing engines, HDR fixes, and native VIDEO support
Core (nodes/core/): alpha, masks, batch consolidation, blend, transform
matrix, bbox, parameters, validation engines shared across nodes, each with
unit tests.

Fix an unconditional RGB clamp to [0,1] that silently destroyed HDR/negative
values in Merge, Transform, Color Correct, Comp, CornerPin, Distort,
Spherize, Crop Stitch, and the shared masked-compositing helpers. Alpha
stays clamped everywhere; RGB no longer does.

Fix Grain applying `amount` twice in overlay/soft_light modes (quadratic
instead of linear falloff).

Add STMap mode to Distort (absolute UV remap vs relative displacement).
Add bbox output/input wiring between Crop and Crop Stitch. Add edge_grow
and edge_color_match to Crop Stitch. Add despill to Keyer. Add shuffle mode
to Channel (R/G/B/A routed from any source). Add size/luma_response to
Grain. Add grid mode to Constant. Add frame/layer accumulation limits to
Preview and Draw, plus a font-path containment fix and supersampling in
Text.

Make Frame Range and Append recognize a real ComfyUI VIDEO input (not only
this pack's internal media wrapper), preserving true fps/audio instead of
silently dropping them, and add a native VIDEO output alongside the
existing IMAGE output on both.

Add node_replacements.py (empty registry, wired into the extension
lifecycle) for future schema migrations.

Add a CI matrix pinning the verified requires-comfyui floor, and
.comfyignore for Registry packaging.
2026-09-27 11:55:49 +02:00

76 lines
2.1 KiB
Python

from __future__ import annotations
import math
import torch
from nodes.core.transform import build_2d_transform, mat_rotate, transform_points
DEVICE = torch.device("cpu")
DTYPE = torch.float32
def _transform(**kwargs) -> torch.Tensor:
return build_2d_transform(device=DEVICE, dtype=DTYPE, **kwargs)
def test_identity_leaves_points_unchanged():
matrix = _transform()
points = torch.tensor([[0.0, 0.0], [10.0, 5.0]])
out = transform_points(matrix, points)
assert torch.allclose(out, points, atol=1e-6)
def test_pure_translation():
matrix = _transform(tx=5.0, ty=-3.0)
points = torch.tensor([[0.0, 0.0], [1.0, 1.0]])
out = transform_points(matrix, points)
assert torch.allclose(out, torch.tensor([[5.0, -3.0], [6.0, -2.0]]), atol=1e-6)
def test_non_uniform_scale():
matrix = _transform(sx=2.0, sy=0.5)
points = torch.tensor([[10.0, 10.0]])
out = transform_points(matrix, points)
assert torch.allclose(out, torch.tensor([[20.0, 5.0]]), atol=1e-6)
def test_90_degree_rotation_about_origin():
matrix = mat_rotate(90.0, device=DEVICE, dtype=DTYPE)
points = torch.tensor([[1.0, 0.0]])
out = transform_points(matrix, points)
assert torch.allclose(out, torch.tensor([[0.0, 1.0]]), atol=1e-5)
def test_rotation_about_pivot_keeps_pivot_fixed():
matrix = _transform(rotation=45.0, pivot_x=50.0, pivot_y=50.0)
pivot = torch.tensor([[50.0, 50.0]])
out = transform_points(matrix, pivot)
assert torch.allclose(out, pivot, atol=1e-4)
def test_expanded_bbox_of_rotated_square():
# A 100x100 square rotated 45 degrees about its center should expand to
# roughly its diagonal (100*sqrt(2)) on each axis.
matrix = _transform(rotation=45.0, pivot_x=50.0, pivot_y=50.0)
corners = torch.tensor([[0.0, 0.0], [100.0, 0.0], [100.0, 100.0], [0.0, 100.0]])
warped = transform_points(matrix, corners)
span_x = warped[:, 0].max() - warped[:, 0].min()
span_y = warped[:, 1].max() - warped[:, 1].min()
expected = 100.0 * math.sqrt(2.0)
assert abs(float(span_x) - expected) < 1e-3
assert abs(float(span_y) - expected) < 1e-3