Expand description
Opt-in f64 / [euclid::UnknownUnit] geometry for CEL.
Install once on a fresh lookup; no parser or runtime built-ins change. Named operations support ordinary calls and receiver calls (including zero-argument property syntax). Operators require exact operand types.
use cel_parser::{CELParser, OpLookup};
let mut lookup = OpLookup::new();
cel_euclid::install(&mut lookup);
let mut parser = CELParser::new(lookup);
let mut segment = parser.parse_str(
"(Point2D { y: 2.0, x: 1.0 } + Vector2D { x: 3.0, y: 4.0 }).x"
).unwrap();
assert_eq!(segment.call0::<f64>().unwrap(), 4.0);Geometry arithmetic, bounds and containment retain Euclid semantics, including
non-finite arithmetic and half-open containment. Three operations are checked:
normalize rejects zero/non-finite lengths and non-finite results; inverse
rejects non-finite coefficients, zero/non-finite determinants, non-finite
inverses and zero inverse coefficients with nonzero exact cofactors;
outer_transformed_rect rejects invalid or unrepresentable bounds.
Ordinary point/vector mapping, constructors and composition remain unchecked.
Checked inversion permits exact-zero cofactors and nonzero subnormal results.
Translation cofactor checks compare exact products, so underflow or rounding
before determinant scaling cannot disguise a lost nonzero coefficient.
Other rounding and conditioning errors retain Euclid semantics: checked
inversion does not guarantee exact-real arithmetic or numerical stability.
degrees and radians construct angles from f64 and extract the respective
scalar from angles. Matrix accessors use Euclid’s six coefficient names.
a.then(b) applies a first, then b; vectors ignore translation.
use cel_parser::{CELParser, OpLookup};
let mut lookup = OpLookup::new();
cel_euclid::install(&mut lookup);
let mut parser = CELParser::new(lookup);
let mut segment = parser.parse_str(
"translation(Vector2D { x: 10.0, y: 0.0 }).then(scale(2.0, 2.0))\
.transform_point(Point2D { x: 1.0, y: 0.0 }).x"
).unwrap();
assert_eq!(segment.call0::<f64>().unwrap(), 22.0);Checked outer bounds enclose all four Euclid-mapped corners, not just a
diagonal, for arbitrary affine rotation, shear and reflection. They are not
an interior-containment test. Inputs must have finite matrix coefficients,
finite source origins/extents/endpoints and nonnegative source extents.
Positive source extents must advance their endpoints. Projected edges must
not overflow or completely underflow, and positive projected spans must not
collapse after mapping. Mapped corners and resulting bounds must be finite.
Zero-size rectangles and singular transforms are allowed. If reconstructing
a bound’s maximum rounds inward, its extent advances one ULP to enclose that
maximum; unrepresentable extents/endpoints return evaluation errors.
The adam feature only adds optional type registration; it is disabled by default.
§Optional Adam installation
Hosts explicitly install both cell types and operations. adam-lang itself
has no geometry dependency. Its registration-aware checker consumes the same
registry and lookup as compilation; unresolved call results remain best-effort.
Bare CEL hosts that use registered closure annotations must also configure
their [cel_parser::TypeResolver]; installing record operations alone does
not replace the parser’s annotation resolver.
let mut types = adam_lang::TypeRegistry::new();
cel_euclid::register_adam_types(&mut types);
let mut lookup = cel_parser::OpLookup::new();
cel_euclid::install(&mut lookup);
let mut parser = adam_lang::AdamParser::new(types, lookup);
assert!(parser.parse_str("sheet s { cell p: Point2D; }").is_ok());Run cargo run -p cel-euclid --features adam --example adam_geometry for
source-preserving crop/straighten and pan/zoom sheets. Crop fitting, its
eight-epsilon inward guard, and precision/coverage requirements live in CEL,
not in library operations. The example’s Rust boundary validates inputs only.
§Supported geometry signatures
Every geometry value uses f64 components and euclid::UnknownUnit. CEL exposes
concrete names, not Euclid’s generic parameters. These records require each
field exactly once; callers may write fields in any order.
| CEL / Rust alias | Required fields (schema order) |
|---|---|
Point2D | x: f64, y: f64 |
Vector2D | x: f64, y: f64 |
Size2D | width: f64, height: f64 |
Rect | origin: Point2D, size: Size2D |
Box2D | min: Point2D, max: Point2D |
Angle | radians: f64 |
Transform2D | m11: f64, m12: f64, m21: f64, m22: f64, m31: f64, m32: f64 |
Use 0.0, 0f64, or 0 as f64, not an unsuffixed integer where a field or
operand needs f64. There are no implicit numeric casts, scalar-left geometry
products, generic geometry constructors, or return-type-only overloads.
The following table is the complete set of 98 exact signatures. T is
documentation shorthand, not language syntax. A slash in an operand or result
column lists corresponding alternatives, not additional cross-product overloads.
Every named operation with a receiver accepts both name(receiver, args...)
and receiver.name(args...). Unary named operations also accept receiver.name.
identity_transform() has no receiver. Operators use their normal CEL spelling.
| Operation | Operands → result |
|---|---|
==, != | (T, T) → bool for all seven aliases |
is_finite | (T) → bool for all seven aliases |
+ | (Point2D, Vector2D) → Point2D; (Vector2D, Vector2D) → Vector2D; (Size2D, Size2D) → Size2D |
- | (Point2D, Point2D) → Vector2D; (Point2D, Vector2D) → Point2D; (Vector2D, Vector2D) → Vector2D; (Size2D, Size2D) → Size2D |
unary - | (Vector2D) → Vector2D |
*, / | (Vector2D, f64) → Vector2D; (Size2D, f64) → Size2D |
x, y | (Point2D) → f64; (Vector2D) → f64 |
width, height | (Size2D) → f64; (Rect) → f64; (Box2D) → f64 |
origin | (Rect) → Point2D |
size | (Rect) → Size2D; (Box2D) → Size2D |
min, max | (Box2D) → Point2D (coexist with cel-std scalar overloads) |
min_x, min_y, max_x, max_y | (Rect) → f64; (Box2D) → f64 |
center | (Rect) → Point2D; (Box2D) → Point2D |
dot, cross | (Vector2D, Vector2D) → f64 |
square_length, length | (Vector2D) → f64 |
normalize | (Vector2D) → Vector2D, checked |
distance_to | (Point2D, Point2D) → f64 |
translate | (Rect, Vector2D) → Rect; (Box2D, Vector2D) → Box2D |
inflate | (Rect, f64, f64) → Rect; (Box2D, f64, f64) → Box2D |
to_box2d | (Rect) → Box2D |
to_rect | (Box2D) → Rect |
contains | (Rect, Point2D) → bool; (Box2D, Point2D) → bool |
contains_rect | (Rect, Rect) → bool |
contains_box | (Box2D, Box2D) → bool |
intersects | (Rect, Rect) → bool; (Box2D, Box2D) → bool |
union | (Rect, Rect) → Rect; (Box2D, Box2D) → Box2D |
degrees, radians | (f64) → Angle; (Angle) → f64 |
m11, m12, m21, m22, m31, m32 | (Transform2D) → f64 |
identity_transform | () → Transform2D |
translation | (Vector2D) → Transform2D |
scale | (f64, f64) → Transform2D |
rotation | (Angle) → Transform2D |
then | (Transform2D, Transform2D) → Transform2D |
determinant | (Transform2D) → f64 |
inverse | (Transform2D) → Transform2D, checked |
transform_point | (Transform2D, Point2D) → Point2D |
transform_vector | (Transform2D, Vector2D) → Vector2D |
outer_transformed_rect | (Transform2D, Rect) → Rect, checked |
a.then(b) applies a first, b second. A translation by (10, 0) followed
by scale (2, 2) maps (1, 0) to (22, 0), not (12, 0). Vectors ignore
translation. The matrix maps x' = m11*x + m21*y + m31 and
y' = m12*x + m22*y + m32.
Ordinary arithmetic retains Euclid’s floating-point semantics, including
nonfinite results. Record is_finite checks stored components, not derived
endpoints. Rectangles and boxes use Euclid containment; ordinary point
containment is half-open at maximum edges. Crop coverage instead permits
corners on either image edge and uses explicit closed inequalities.
§Checked-operation errors
normalizerejects zero/nonfinite lengths and nonfinite results.inverserejects nonfinite coefficients, zero/nonfinite determinants, nonfinite inverse coefficients, and zero inverse coefficients with exact nonzero cofactors (including translation-cofactor underflow). Exact-zero cofactors and nonzero subnormals are allowed. Other rounding and conditioning errors retain Euclid semantics; this is not exact-real inversion.outer_transformed_rectrejects nonfinite coefficients, origins, sizes, endpoints or mapped corners; negative sizes; positive sizes whose endpoints do not advance; overflowing/completely underflowing projected edges; collapsed positive projected spans; and unrepresentable bounds. Zero sizes and singular transforms are otherwise allowed. The operation encloses all four mapped corners, advancing an extent one ULP when reconstructing its maximum would round inward. It does not prove that an axis-aligned rectangle fits inside the transformed shape.
All three return evaluation errors with source context. They never substitute an empty shape for an error. A domain-specific sheet can impose stricter requirements, as the crop example does, without changing these general operations.
Functions§
- install
- Installs seven record schemas and the complete exact geometry signature table.
- register_
adam_ types - Registers the seven exact geometry names with their Euclid defaults.
Type Aliases§
- Angle
- An angle stored as
f64radians. - Box2D
- An axis-aligned box represented by minimum and maximum points in unknown units.
- Point2D
- A two-dimensional point with
f64coordinates and unknown units. - Rect
- A rectangle represented by an origin and size in unknown units.
- Size2D
- A two-dimensional size with
f64extents and unknown units. - Transform2D
- A two-dimensional affine matrix mapping unknown units to unknown units.
- Vector2D
- A two-dimensional vector with
f64components and unknown units.