Skip to content

Vector comparison, clamp ownership and the legacy viewport helper

Vector equality is exact component equality for equal declared dimensions. Different dimensions compare unequal in both operand orders; two empty Vectors compare equal and empty/nonempty compare unequal. Inequality is the logical complement of equality for supported Vectors. Both special methods return NotImplemented for other operand types. No approximate comparison is added. Malformed component storage remains outside this cleanup's domain.

clamp(size, value, minS, maxS) and Vector.clamp(size, value, minS, maxS) return fresh Vector/list storage. Value and bound lists remain unchanged, including when multiple Vectors share them. The method receiver is preserved. Vector.i_clamp(size, value, minS, maxS) still returns and changes the receiver, but replaces its component list without changing a separate caller list or another Vector sharing the old list. Clamping retains max-bound comparison followed by min-bound comparison; no new validation of reversed bounds is added. Aliased bounds are read from their original data rather than incidentally changed by clamping the value list.

from gem.vector import Vector, clamp

values = [-2, 2, 10]
a = Vector(3, values)
b = Vector(3, values)
result = clamp(3, values, [0]*3, [5]*3)  # [0, 2, 5], fresh storage
assert values == [-2, 2, 10]
a.i_clamp(3, values, [0]*3, [5]*3)
assert a.vector == [0, 2, 5]
assert b.vector == values == [-2, 2, 10]

getViewPort

getViewPort(coords, width, height) retains the historical source formula. For a finite nonzero Vector with at least two components, it normalizes the whole Vector, including Z/W where supplied, using the existing stable normalization routine. If L is its mathematical norm:

x_result = (coords.x / L + 1) * width / 2 + coords.x
y_result = (coords.y / L + 1) * height / 2 + coords.y
result = [x_result, y_result, width, height]

Vector2/3/4 are covered by known-answer regressions. Original coordinates are both normalized direction components and additive XY offsets; this unusual formula is preserved, not replaced with conventional NDC-to-window mapping. It returns a new four-element list without changing the Vector or its storage. Repeated calls are independent. Zero Vectors retain ZeroDivisionError. Width/height are used arithmetically without a new positivity rule. Unsupported shapes/types and nonfinite inputs receive no new validation or error policy.

from gem.common import getViewPort
from gem.vector import Vector

assert getViewPort(Vector(2, [3, 4]), 100, 200) == [83, 184, 100, 200]
# Including Z changes the norm, even though only XY are returned.
assert getViewPort(Vector(3, [3, 4, 12]), 130, 260) == [83, 174, 130, 260]

This helper neither sets an OpenGL viewport nor projects world/object points. For modelview/projection matrices, perspective division and window depth, use the existing project/unproject API.

Compatibility

Cross-dimension comparisons previously returned inconsistent values or raised IndexError; empty comparisons returned None. They now return the documented booleans. Returning clamp no longer changes caller data; code relying on that side effect must explicitly assign the result or use i_clamp on its receiver. External references to old receiver storage remain unchanged after i_clamp. Viewport calls with supported Vector inputs now execute instead of raising the Vector-subscripting TypeError. The normalized-coordinate formula, zero error, function names/signatures and namespace remain unchanged.