Skip to content

Base/adam ef57e1f6#3

Open
giardiello wants to merge 12 commits into
mainfrom
base/adam-ef57e1f6
Open

Base/adam ef57e1f6#3
giardiello wants to merge 12 commits into
mainfrom
base/adam-ef57e1f6

Conversation

@giardiello
Copy link
Copy Markdown
Owner

No description provided.

davisadam10 and others added 12 commits April 15, 2026 19:01
fallback to look for the label in the framing_intent
if the label in the framing_intent is also not set, fall back and display the id of the framing_decision as there needs to be something to select
Contexts in the FDL spec have no id field and label is optional.
The viewer was using context labels as identifiers throughout the
selection pipeline, which broke when a context had an empty label.

The underlying C++ core API already identifies contexts by array
index via fdl_doc_context_at(). Aligned the Python layer to match
by using index-based selection across the viewer, imaging, and
frameline generator packages. Labels are still used for display
in dropdowns with the array index as fallback when empty.
Changed public API functions and get_fdl_components to accept
context_index (int) instead of context_id (str). Labels are
resolved to indices at test boundaries.
The settings class is named Settings not AppSettings. This caused
the export image button to silently fail.
…_display

Ensure contexts are treated via index across the python layer
Align FDL rounding with spec 7.4.12 by treating the rounding strategy as
applicable only to canvas.dimensions (and the integer-typed
canvas.effective_dimensions for schema consistency).  Inner geometry
(protection, framing dims/anchors) stays floating-point so sub-pixel
precision is not lost.

- Remove the NONE sentinel entirely; an omitted "round" field now
  returns the spec default {EVEN, UP} directly (no magic third mode).
- Skip round when maximum_dimensions + pad_to_maximum (per spec 7.4.12).
- After crop, re-round effective_dims with the template strategy,
  enforce hierarchy (effective >= ceil(max inner dims)), and apply
  half-delta compensation to effective_anchor so the rounded effective
  area stays centered on the original float area, clamped to canvas.
- Surface fdl_canvas_template_has_round in C ABI and bindings.
- Regenerate 52 scenario golden FDLs and C++ template vector affected
  by the new anchor compensation and inner-float geometry.

Tests: 134/134 C++, 457 Python (4 skipped).
Made-with: Cursor
Consolidate all schema-integer rounding and anchor compensation into a
single step at the end of the CanvasTemplate apply pipeline (post-crop),
replacing the prior two-pass design (Phase 5 round + Phase 9b
effective-only re-round).  This removes redundant intermediate
quantization, fixes a latent asymmetry where canvas rounding was not
absorbed into anchor positions, and makes the behavior easier to reason
about end to end.

fdl_geometry_round now does everything the schema-integer step requires:

  1. Round canvas_dims; shift ALL anchors by +canvas_delta/2 to keep
     content centered within the rounded canvas.  Previously the
     canvas rounding delta was discarded, introducing a small
     directional bias whenever canvas_dims wasn't already integer.
  2. Round effective_dims; enforce hierarchy (effective >= ceil(max
     inner dims), effective <= canvas).
  3. Shift effective_anchor by -effective_delta/2 to keep the rounded
     effective rectangle centered on its pre-round extent.
  4. Clamp all anchors to [0, canvas - dim] so symmetric absorption
     degrades gracefully to one-sided at canvas edges.

The skip_round gate for pad_to_maximum + maximum_dimensions is no
longer needed: when that branch is active, canvas_dims is set from
max_dims (already integer) before the final round, so its rounding
delta is zero and no anchor shift occurs on that axis — matching spec
7.4.12's "round has no effect" semantics automatically.  Inner
effective_dims is still integerized as the schema requires.

geometry_round_effective_post_crop is removed from the internal
header, internal implementation, and the C ABI (wrapper +
fdl_core.h declaration).  It was only ever called from fdl_template.cpp
and was not exposed in Python or Node bindings.

Test vectors updated:

- Geometry round vectors: all 3 cases now reflect canvas_delta/2
  shifts on all anchors plus -effective_delta/2 on effective_anchor;
  inner (protection, framing) dims and anchors stay float.
- Template vectors: all 10 cases regenerated.  Notable knock-on
  changes now exposed: scaled_bounding_box reports the true float
  pre-round canvas (previously the post-Phase-5-round integer), and
  output_effective_dims is now consistent with output_canvas_dims
  rounding (the old JSON had an effective_dims value that the old
  pipeline wouldn't actually produce).
- generate_geometry_vectors.py: adds an inline _round_like_cpp helper
  that reproduces the new C++ semantics so regenerations stay in sync
  without requiring matching changes to the Python reference
  implementation.

Python and Node bindings are intentionally untouched; they are
autogenerated from the C++ core and will pick up the new semantics
when regenerated.

Tests: 134/134 C++.
Made-with: Cursor
Cross-binding work to make `round` optional in canvas-template creation,
defaulting to FDL spec §7.4.12 `{EVEN, UP}`. Changes the default mode
from ROUND to UP, and regenerates golden scenario fixtures accordingly.

Facade/API:
- fdl_api.yaml: give `round` param default `RoundStrategy()`
- Python: accept `round=None` in FDL.add_canvas_template / CanvasTemplate
- Node: `round: RoundStrategy = new RoundStrategy()` in fdl.ts
- C++: default-arg `{FDL_ROUNDING_EVEN_EVEN, FDL_ROUNDING_MODE_UP}` on
  add_canvas_template, from_framing_intent, populate_from_intent
- Python FramingDecision ctor: move DimensionsFloat/PointFloat mutable
  defaults to None-sentinels

Codegen:
- adapters.py: flip RoundStrategy aggregate literal EVEN,ROUND -> EVEN,UP
- python_context.py: new _resolve_python_default helper — constructor-call
  defaults emit `foo=None` in signature + `if foo is None: foo = Ctor()`
  unwrap in the body (avoid mutable default args)
- class.py.j2: render `unwrap_expr` blocks for init/builders/methods
- cpp_gen.py: plumb `global_fallback` through all param builders; inline
  `{EVEN, UP}` as default for nullable round_strategy params
- node_gen.py: when a default is a `new X()` constructor call, import
  the class as a value (not just a type) so the default compiles
- rounding.ts.j2: doc the new spec default

Test/infra:
- node scenarios.test.ts: tolerance-aware deep compare for nested
  custom_attrs objects (mirror Python's __eq__ semantics)
- python testing/base.py: `generate_result` now reads FDL_REGEN_FIXTURES
  env var, so refreshing golden .fdls doesn't require editing code
- binding.cc: expose fdl_canvas_template_has_round, update comments
- regenerated ~25 Scenarios_For_Implementers/*/Results/*.fdl fixtures
  under the new {EVEN, UP} default
- uv.lock refresh

Snapshot commit for rollback — not a final, reviewed change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewrite geometry_round as a 5-step algorithm:
1. Ceil effective_dims; absorb ceil delta into effective_anchor.
2. Round canvas_dims per strategy.even / strategy.mode.
3. Clamp effective / protection / framing against rounded canvas.
4. Shift all anchors by +canvas_delta/2.
5. Clamp anchors to [0, canvas - dim].

This fixes eight scenarios that previously emitted invalid FDL because
canvas rounded DOWN while inner float dims (protection, framing) stayed
above the rounded integer, violating the schema hierarchy invariant.
Under the new rule inner dims <= effective_float by construction, so
ceil(effective_float) >= inner automatically; canvas-clamping inner in
step 3 covers the max_dims case where canvas is forced smaller than
ceil(effective_float).

strategy.even / strategy.mode no longer apply to effective_dims (pure
ceil, may be odd when even=EVEN). Canvas keeps strategy-respecting
rounding.

Also clarify scaled_bounding_box and content_translation are unrounded
floats by design (captured post-scale, pre-round).

- native/core/src/fdl_geometry.cpp: new geometry_round.
- native/core/src/fdl_template.cpp, fdl_constants.h: docstrings.
- native/core/tests/vectors/geometry/: _round_like_cpp + golden vectors
  refreshed.
- resources/FDL/Scenarios_For_Implementers/**/Results/*.fdl: 14 fixtures
  regenerated (8 previously-invalid + 6 where ceil replaces EVEN round).
- native/bindings/python/tests/test_optional_rounding.py: assert EVEN
  strategy on canvas_dims instead of effective_dims.
Refines the round-pipeline step 5 so the integer effective dim adapts
to fit inside (canvas - anchor) instead of pulling the anchor back to
fit (canvas - dim).  Anchor truthfulness is prioritised.

  5a) Lower clamp: negative anchors -> 0 (unchanged behaviour, just
      separated from the upper bound).
  5b) Upper bound: shrink dim if anchor + dim overflows canvas.
        - effective is an integer schema field, so use
          floor(canvas - anchor) -> strict invariant
          anchor + dim <= canvas always holds.
        - protection / framing remain float; use raw
          canvas - anchor.
  5c) Re-establish hierarchy: the floor on effective in 5b can drop
      it below previously-valid float inner dims.  Clamp protection /
      framing down to the (post-floor) effective, but only when they
      actually exceed it (preserves the 0 = "unset" sentinel for
      optional protection).

Mirrors the same algorithm in the Python golden vector generator and
refreshes the one template-vector expectation that lands in the upper
bound (right_bottom_alignment).

Empirical impact: 3 scenario fixtures change (Scen17 A/B/C, the
pad_to_max + bottom-aligned cases).  In those, effective_anchor.y
preserves the float position computed by the pipeline, the integer
effective.height is one pixel smaller (floor of canvas - anchor), and
framing.height is clamped to match.  All other 50 scenarios are
byte-identical with the previous commit.

Test status:
  - C++ ctest: 134/134.
  - Python native bindings: 457 pass, 4 skip.
  - Node bindings: 325 pass.
  - fdl_imaging FDL parity: passes; image-comparison failures remain
    deferred (tracked separately).

OPEN DISCUSSION - Scen17 logic still under exploration:

  1. Trade-off in step 5b/5c.  The current choice prioritises anchor
     truthfulness (anchor preserved exactly as the pipeline computed
     it, sub-pixel and all) over geometric fidelity (the previous
     anchor-clamp produced an exact-fit edge but moved the anchor by
     up to delta/2).  The 1-pixel effective shrink and the
     sub-floor-pixel gap at the canvas edge are visible in the new
     fixtures.  Whether this is the right call vs. shifting the
     anchor (committed previously) is still open.

  2. Single-round-at-end exploration.  The pipeline is already mostly
     all-float internally; geometry_round is the only quantization
     point.  Question on the table is whether step 1's ceil and step
     5b's floor-shrink should collapse into a single rule -- e.g.
     skip the step-1 ceil entirely and just do
       effective = floor(canvas - effective_anchor)
     at the end -- so that the integer effective is computed once
     from the float anchor + canvas, with no symmetric-delta
     bookkeeping in the middle.  That would eliminate the
     "step 1 inflates, step 5 shrinks" tension entirely.  Not
     attempted yet; needs design + corpus diff before committing.
@giardiello giardiello requested a review from davisadam10 as a code owner April 25, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants