Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Optional geometry and self-referencing constraints

The book, adam-web-ui, and adam-lsp explicitly install cel-std operations, cel-euclid operations, and the seven geometry cell types. A standalone adam-lang host still chooses its own libraries; Adam has no mandatory geometry dependency. Enable cel-euclid’s adam Cargo feature, call cel_euclid::register_adam_types(&mut types) and cel_euclid::install(&mut lookup), and construct AdamParser::new(types, lookup). Pass the same registry and lookup to adam_lang::check_sheet for editor checking. The checker uses exact record/member metadata where types are known, visits closure bodies, and defers unresolved call-result and tuple shapes to runtime. It does not claim complete inference.

Geometry uses concrete f64 / UnknownUnit aliases, not generic Adam types. Write 1.0 or 1 as f64 rather than passing i32 fields implicitly. Field order is arbitrary; every declared field must occur once. Consult the complete signature table for fields, supported operand types, checked errors, and containment semantics.

Crop and straighten

This is the actual library example, included rather than copied. Run cargo run -p cel-euclid --features adam --example adam_geometry to execute it. cargo test -p adam-lang-book --test geometry verifies these same included sheets through the book’s parser. These cross-library demonstrations are executable Rust-driven examples, not live inspector mounts or a crop UI.

#![allow(unused)]
fn main() {
// Image coordinates -> crop coordinates. The caller rotates about the image
// point under the requested crop center. No separate requested-crop cell:
// Adam preserves the source aspiration of the self-referencing crop.
// A valid result preserves the crop center, image anchor, and all overlay
// corners within 64 epsilon times the requested maximum extent. Insufficient
// precision fails an explicit requirement, even with finite coefficients.
sheet crop_straighten {
    source image: Rect = Rect {
        origin: Point2D { x: 0.0, y: 0.0 },
        size: Size2D { width: 100.0, height: 100.0 }
    } require {
        @valid_image image.is_finite() && image.width > 0.0 && image.height > 0.0
            && image.max_x > image.min_x
            && image.max_y > image.min_y
            && Point2D { x: image.max_x, y: image.max_y }.is_finite();
    };
    source transform: Transform2D = identity_transform();
    cell crop: Rect = Rect {
        origin: Point2D { x: 0.0, y: 0.0 },
        size: Size2D { width: 100.0, height: 100.0 }
    } require {
        @positive_effective_crop crop.is_finite() && crop.width > 0.0 && crop.height > 0.0
            && crop.max_x > crop.min_x
            && crop.max_y > crop.min_y
            && Point2D { x: crop.max_x, y: crop.max_y }.is_finite();
    };
    cell shrink: f64 = 1.0 require {
        @representable_shrink shrink > 0.0 && shrink <= 1.0
            && 1.0 / shrink <= 1.7976931348623157e308;
    };
    cell precise: bool = true require {
        @sufficient_fit_and_presentation_precision precise;
    };
    // Map the effective crop back onto the unchanged source overlay.
    cell presentation: Transform2D = identity_transform() require {
        @finite_presentation presentation.is_finite();
    };

    relationship {
        (crop, shrink, precise, presentation) := (|
            r: Rect,
            i: Rect,
            inv: Transform2D,
            t: Transform2D
        |
            (|r: Rect, i: Rect, inv: Transform2D, t: Transform2D, a: Point2D, b: Rect|
                (|
                    r: Rect,
                    inv: Transform2D,
                    t: Transform2D,
                    s: f64,
                    accurate: bool
                | // Eight epsilon inward guard, only when shrinking.
                    (|r: Rect, inv: Transform2D, t: Transform2D, s: f64, accurate: bool|
                        (|
                            r: Rect,
                            inv: Transform2D,
                            t: Transform2D,
                            s: f64,
                            accurate: bool,
                            q: Rect
                        |
                            (|
                                r: Rect,
                                inv: Transform2D,
                                s: f64,
                                accurate: bool,
                                q: Rect,
                                p: Transform2D
                            | // Finite coefficients alone do not guarantee a fixed
                                // anchor or overlay after reciprocal-scale cancellation.
                                (|
                                    r: Rect,
                                    s: f64,
                                    accurate: bool,
                                    q: Rect,
                                    p: Transform2D,
                                    a: Point2D,
                                    lo: Point2D,
                                    lr: Point2D,
                                    ul: Point2D,
                                    hi: Point2D,
                                    e: f64
                                |
                                    (
                                        q,
                                        s,
                                        accurate
                                            && abs(q.width / r.width - s)
                                                <= 0.000000000000014210854715202004 * s
                                            && abs(q.height / r.height - s)
                                                <= 0.000000000000014210854715202004 * s
                                            && abs(q.center().x - r.center().x) <= e
                                            && abs(q.center().y - r.center().y) <= e
                                            && abs(a.x - r.center().x) <= e
                                            && abs(a.y - r.center().y) <= e
                                            && abs(lo.x - r.min_x) <= e
                                            && abs(lo.y - r.min_y) <= e
                                            && abs(lr.x - r.max_x) <= e
                                            && abs(lr.y - r.min_y) <= e
                                            && abs(ul.x - r.min_x) <= e
                                            && abs(ul.y - r.max_y) <= e
                                            && abs(hi.x - r.max_x) <= e
                                            && abs(hi.y - r.max_y) <= e,
                                        p
                                    ))(
                                    r,
                                    s,
                                    accurate,
                                    q,
                                    p,
                                    p.transform_point(inv.transform_point(r.center())),
                                    p.transform_point(inv.transform_point(q.origin)),
                                    p.transform_point(
                                        inv.transform_point(Point2D { x: q.max_x, y: q.min_y })
                                    ),
                                    p.transform_point(
                                        inv.transform_point(Point2D { x: q.min_x, y: q.max_y })
                                    ),
                                    p.transform_point(
                                        inv.transform_point(Point2D { x: q.max_x, y: q.max_y })
                                    ),
                                    0.000000000000014210854715202004 * max(r.width, r.height)
                                ))(
                                r,
                                inv,
                                s,
                                accurate,
                                q,
                                t.then(translation(Vector2D { x: -q.center().x, y: -q.center().y }))
                                    .then(scale(1.0 / s, 1.0 / s))
                                    .then(
                                    translation(Vector2D { x: q.center().x, y: q.center().y })
                                )
                            ))(
                            r,
                            inv,
                            t,
                            s,
                            accurate,
                            if s == 1.0 {
                                r
                            } else {
                                Rect {
                                    origin: r.center()
                                        - Vector2D { x: r.width * s * 0.5, y: r.height * s * 0.5 },
                                    size: r.size * s
                                }
                            }
                        ))(
                        r,
                        inv,
                        t,
                        if s == 1.0 { s } else { s * (1.0 - 0.0000000000000017763568394002505) },
                        accurate
                    ))(
                    r,
                    inv,
                    t,
                    min(
                        1.0,
                        min(
                            min(a.x - i.min_x, i.max_x - a.x) / max(a.x - b.min_x, b.max_x - a.x),
                            min(a.y - i.min_y, i.max_y - a.y) / max(a.y - b.min_y, b.max_y - a.y)
                        )
                    ), // Bound subtraction must retain the affine half-extents
                    // within 32 epsilon; reject cancellation, not an undersized
                    // but plausible result. This is a precision check only.
                    abs(
                        max(a.x - b.min_x, b.max_x - a.x)
                            / (abs(inv.m11) * (r.width * 0.5) + abs(inv.m21) * (r.height * 0.5))
                            - 1.0
                    )
                        <= 0.000000000000007105427357601002
                        && abs(
                            max(a.y - b.min_y, b.max_y - a.y)
                                / (abs(inv.m12) * (r.width * 0.5) + abs(inv.m22) * (r.height * 0.5))
                                - 1.0
                        )
                            <= 0.000000000000007105427357601002
                ))(r, i, inv, t, inv.transform_point(r.center()), inv.outer_transformed_rect(r)))(
            crop,
            image,
            transform.inverse(),
            transform
        );
    }

    out anchor: Point2D := transform.inverse().transform_point(crop.center()) require {
        @feasible_anchor anchor.is_finite() && anchor.x > image.min_x && anchor.x < image.max_x
            && anchor.y > image.min_y
            && anchor.y < image.max_y;
    };
    // Closed-edge coverage, not Euclid's half-open point containment.
    // Use the image coordinate/extent scale for the absolute rounding budget.
    out coverage: bool := (|b: Rect, i: Rect, e: f64|
        b.min_x >= i.min_x - e && b.max_x <= i.max_x + e && b.min_y >= i.min_y - e
            && b.max_y <= i.max_y + e)(
        transform.inverse().outer_transformed_rect(crop),
        image,
        0.000000000000014210854715202004
            * max(
                max(image.width, image.height),
                max(
                    max(abs(image.min_x), abs(image.max_x)),
                    max(abs(image.min_y), abs(image.max_y))
                )
            )
    ) require {
        @closed_edges coverage;
    };
}
}

Coordinate frames and source memory

image is an axis-aligned source-image rectangle. transform maps image points to the frame containing the axis-aligned requested crop. The runner constructs straightening rotations around the source-image point under the crop center. The relationship also works with arbitrary invertible affine translations, nonuniform scales, reflections, and shears; it does not assume a rotation or an origin-centered anchor.

The relationship reads and writes crop. Adam preserves the user’s source aspiration while it recomputes the effective value. Tightening the angle shrinks the crop uniformly; loosening it springs back. An explicit crop write replaces the aspiration. There is no extra requested-crop cell or helper-owned memory. Every typed, noncapturing IIFE forwards its needed values explicitly.

The expression maps the requested center and the four-corner outer bounds through the inverse transform. It divides each nearest-image-edge distance by the corresponding maximum inverse half-extent, then selects the minimum with one. This gives the largest uniform shrink about that center. Bounding the transformed image instead would include uncovered regions under rotation or shear. A crop with an infeasible center is an error; translating it is not a valid repair.

Numerical and error contract

Inputs need finite positive image/crop sizes, advancing finite endpoints, a finite center, a checked invertible transform, representable inverse bounds, and an image anchor strictly inside all four image edges. The runner’s validation-only write boundary checks candidates before changing source cells. It uses the general checked inverse and bounds operations, not a Rust fitting calculation.

The CEL relationship leaves a fitting crop bit-for-bit unchanged. When it must shrink, it multiplies the computed factor by 1 - 8*f64::EPSILON to round conservatively inward. The precise flag rejects inverse-bound cancellation that differs from affine support half-extents by more than 32*EPSILON; it also rejects subnormal rounding that changes either uniform size ratio by more than 64*EPSILON relative to the shrink factor. The relationship computes the fitted crop and its presentation together, while the source request is still available. It checks the fitted center, the presented source-image anchor, and all four effective corners mapped through the inverse and presentation against the requested center and overlay corners. Each component must agree within 64*EPSILON * max(requested.width, requested.height). A large absolute overlay offset does not relax this extent-relative budget. Finite presentation coefficients alone do not guarantee these postconditions: reciprocal-scale cancellation can move the anchor or distort the overlay.

The named sufficient_fit_and_presentation_precision requirement reports insufficient precision, even when fitting, coverage, and coefficient finiteness pass. The precise flag is validation metadata, not another requested rectangle. The expression contains these policies openly, without a crop-specific operation.

Acceptance uses a 64*f64::EPSILON scale-relative tolerance for closed-edge coverage, center preservation, maximality, and repeated application. For image coverage the scale is the maximum image extent or absolute image endpoint; crop-center and presentation comparisons use the requested overlay extent as specified above. For dimensionless maximality comparisons the scale is one. Ordinary Euclid point containment is half-open; crop coverage allows corners on maximum edges. The conservative result is maximal within this tolerance, not an exact-real arithmetic guarantee. Identity preserves a fitting crop exactly; applying the expression to its effective crop again is stable within tolerance.

Finite extreme magnitudes are supported when these computations remain representable. Nonfinite values, singular/lost-cofactor inverses, overflow, complete underflow, collapsed endpoints, infeasible centers, zero effective sizes, unrepresentable reciprocal scales, or insufficient precision produce a checked evaluation error or a named failed Adam requirement. Propagation alone does not mean requirements passed: the runner checks every requirement and refuses invalid effective values. Inspect errors and requirements instead of silently accepting an empty or numerically misleading crop.

Presentation

The runner reads parsed.sheet.source::<Rect>(crop_id) for the unchanged overlay, never the previously fitted value. shrink describes the current source-to-effective size ratio. presentation applies the image transform and then its reciprocal shrink around the same crop center. Thus the effective crop fills the original overlay and the same image anchor stays under its center, provided the explicit precision requirement passes. In a.then(b), a runs first. A fixed anchor alone does not certify the overlay corners.

The executable chapter test also covers unequal source/overlay magnitudes. With scale(3.0, 3.0), an image centered at zero of size 1e-6 × 1e-6 and a requested crop centered at zero of size 1e6 × 1e6 pass, preserving the center and overlay mapping despite a shrink near 3e-12. In contrast, an image with both origin coordinates 0.03333283332557231 and size 1e-6 × 1e-6, and a requested crop with both origin coordinates -499999.9 and size 1e6 × 1e6, fail the precision requirement under the same transform. All presentation coefficients remain finite, but the anchor maps to 0.10000228881835938 instead of the requested 0.09999999997671694, exceeding the approximately 1.42e-8 overlay budget. Rejection preserves the source aspiration; it does not translate the crop or silently narrow support to rotations or uniform scales.

For a [0,100] × [0,100] image, a centered square at 45 degrees has side 100 / sqrt(2) within 1e-12. For [0,100] × [0,80] with a requested 80 × 40 crop centered at (20,40), identity yields origin (0,30) and size (40,20) within tolerance. The center never moves to hide infeasibility.

Pan and zoom policy

#![allow(unused)]
fn main() {
// Standalone demonstration; the live graph owns a separate Adam sheet.
// content includes the renderer's fit margin; empty content uses the viewport.
sheet pan_zoom {
    source content: Rect = Rect {
        origin: Point2D { x: 0.0, y: 0.0 },
        size: Size2D { width: 100.0, height: 100.0 }
    };
    source viewport: Size2D = Size2D { width: 200.0, height: 200.0 } require {
        @nonzero_viewport viewport.is_finite() && viewport.width > 0.0 && viewport.height > 0.0;
    };
    out bounds: Rect := if content.width > 0.0 && content.height > 0.0 { content } else {
        Rect { origin: Point2D { x: 0.0, y: 0.0 }, size: viewport }
    };
    out fit_scale: f64 := min(
        viewport.width / max(bounds.width, 1.0),
        viewport.height / max(bounds.height, 1.0)
    );
    out fit_pan: Vector2D := Vector2D {
        x: viewport.width * 0.5 - fit_scale * bounds.center().x,
        y: viewport.height * 0.5 - fit_scale * bounds.center().y
    };
    cell zoom: f64 = 2.0;
    cell pan: Vector2D = Vector2D { x: 0.0, y: 0.0 };
    relationship { zoom := clamp(zoom, fit_scale, max(fit_scale, 8.0)); }
    relationship {
        // Compare actual endpoints: extent arithmetic can round differently at fit.
        pan := Vector2D {
            x: if viewport.width - zoom * bounds.max_x >= -zoom * bounds.min_x {
                viewport.width * 0.5 - zoom * bounds.center().x
            } else {
                clamp(pan.x, viewport.width - zoom * bounds.max_x, -zoom * bounds.min_x)
            },
            y: if viewport.height - zoom * bounds.max_y >= -zoom * bounds.min_y {
                viewport.height * 0.5 - zoom * bounds.center().y
            } else {
                clamp(pan.y, viewport.height - zoom * bounds.max_y, -zoom * bounds.min_y)
            }
        };
    }
    out view: Transform2D := scale(zoom, zoom).then(translation(pan));
}
}

The caller supplies measured visible-node bounds including the fit margin and a positive viewport. Empty content falls back to viewport bounds. Fit uses min(viewport.width / max(width,1), viewport.height / max(height,1)). Zoom clamps to [fit_scale, max(fit_scale,8)]. Each pan axis centers undersized content; otherwise it clamps to the actual scaled content edges. Self-referencing writes retain pan/zoom aspirations. For initial fit or reset, the caller writes fit_scale to zoom and fit_pan to pan.

This standalone sheet demonstrates the policy. The live graph uses its own Adam sheet in adam-web-ui, with a Dioxus proposal/approval bridge shared by Begin’s desktop and web builds and the book’s graph inspectors. JavaScript owns measurement, layout, and gestures, but renders only matching Adam approvals. The bridge waits for real positive viewport observations and retains the last valid viewport during transient zero-size observations. See graph view ownership and verification for the live model, lifecycle, and interaction contracts.