Commit Graph
48 Commits
Author SHA1 Message Date
Hakan Seven e8125c8702 fix: preserve selected smooth curve geometry 2026-09-17 17:19:09 +03:00
ramox81 025398bbb1 Preserve spline grip display mode after smooth solve 2026-09-17 14:54:33 +03:00
ramox81 5497f455ef Complete spline fit and control grip modes 2026-09-17 14:29:26 +03:00
Karim Jerbi ca0ee37d26 feat(snap): independent 3D object-snap system with six modes
Grid snap locks to SNAPUNIT instead of magnetizing; snapped 3D points keep elevation instead of flattening to z=0. New SnapTypes Vertex/EdgeMidpoint/FaceCenter/Knot/FacePerpendicular/NearestFace with their own F4 master toggle and mode set, tiers, and markers, fully independent of the 2D master. Solid B-rep corners/edge-midpoints/face-centers from mesh edges and kernel faces, spline knot points, and live perpendicular/nearest-to-face on mesh triangles merged priority-aware in move/click paths. Drafting Settings 3D Object Snap tab is fully live (was dead placeholders); Nearest-to-face off by default. TDD: behavioral LINE-to-solid tests plus engine/scene unit tests.
2026-09-16 16:43:37 +01:00
Hakan Seven 7b4cef41f7 Complete parametric ribbon and constraint tools 2026-09-14 01:05:39 +03:00
Hakan Seven 21e085db7e Merge updated pull request #1212 from Ramox81/codex/draw-menu 2026-09-11 16:19:44 +03:00
Hakan Seven d08896d59d Merge pull request #1212 from Ramox81/codex/draw-menu 2026-09-11 16:17:36 +03:00
ramox81 45c9cfd2ab Show stored spline tangent values 2026-09-11 16:16:11 +03:00
ramox81 14b98231f5 Align spline fit properties and menu arrows 2026-09-11 15:53:51 +03:00
Hakan Seven 8640634e64 Merge pull request #1202 from Ramox81/codex/fix-fit-spline-bounds-20260911 2026-09-11 15:51:23 +03:00
ramox81 d24c5479c1 Keep the original interpolation hull for conservative bounds 2026-09-11 15:00:46 +03:00
ramox81 c64d8a359f Bound fit-only splines through their interpolated control hull 2026-09-11 14:59:32 +03:00
Hakan Seven dcedf945ea feat(spline): correct property semantics
Merge PR #824 while keeping periodic and coplanarity geometry in cadkernel.
2026-08-20 12:13:35 +03:00
Hakan Seven 1c408e3a73 feat(spline): improve fit and CV editing
Keep planar conversions kernel-backed and leave unsupported 3D, CV-to-fit, and closed rebuilds unavailable instead of changing curve data.\n\nRefs #815
2026-08-20 00:18:45 +03:00
ramox81 96e8d75cc5 Separate spline fit and control properties 2026-08-19 17:21:49 +03:00
ramox81 1a6b61fb3a Correct spline property value semantics 2026-08-19 16:50:49 +03:00
ramox81 df71fa61bd Show effective spline endpoint tangents 2026-08-19 15:54:28 +03:00
ramox81 5595837a69 Align spline properties and editing 2026-08-19 12:00:02 +03:00
ramox81 0d37d3ff78 Improve spline fit and control vertex grips 2026-08-19 11:02:32 +03:00
Hakan Seven c943dfe391 fix(render): draw spatial curves and widths 2026-08-10 16:30:43 +03:00
Hakan Seven ac8183ec91 refactor(cad): use direct stack dependencies 2026-08-10 15:03:31 +03:00
Hakan Seven 96be8214f9 feat(tess): use angular sampling
Route curve, hatch, sweep, loft, surface, edge, and isoline tessellation through the shared kernel policy.
2026-08-10 12:12:16 +03:00
Hakan SevenandClaude Opus 5 8c7ebdc9f1 refactor: drop truck, and evaluate every curve through the kernel
The last four truck crates are gone from the manifest. What they were doing
falls into three parts, and each now has one answer instead of two.

NURBS evaluation. truck was carrying B-spline curves and surfaces for the
SPLINE entity, hatch boundaries, BLEND's endpoint frames, the spline preview
and ACIS spline-surface faces. The kernel now has both, over one de Boor
written across however many coordinates a control point holds — so a plane
curve and a space curve cannot drift apart, and a surface is the same
algorithm applied twice. The rational and the polynomial cases stop being
separate types: weights absent means polynomial.

The entity conversion. Every LINE, ARC, ELLIPSE, SPLINE and polyline was
built into truck topology purely so it could be sampled back into points.
They are sampled through `entities::curve` now, which is where each one's
geometry is already defined once and what EXTRUDE and REVOLVE read — so a
circle drawn on screen and a circle handed to the Model tab come from the
same definition. That retires four of TruckObject's variants and the module
is renamed for what it does.

SWEEP and LOFT. Both only ever produced a mesh, so both are built from point
lists: a band of quads per span, and a lid where a profile closes. Lofting
profiles of different densities resamples them by distance rather than by
index, so a circle lofted to a square no longer twists.

Two things worth noting for anyone reading the old comments. The tolerance
globals that lived in the tessellation module were never about truck at all —
they are the per-frame chord height, and they move to `curve_tol`. And the
rule that a profile handed over as `Lines` silently broke EXTRUDE and REVOLVE
no longer holds: those read the curve directly and never look at this channel.

The two `automation` test failures are unchanged from before this and are not
caused by it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-10 01:08:50 +03:00
Hakan SevenandClaude Opus 5 ece440bcab refactor(curve): draw a fit spline and an offset preview from the curve
Two places were left describing geometry for themselves.

A fit-point SPLINE was drawn from a C² cubic solved in spline.rs while
TRIM and snap cut a separate interpolation from the kernel. Near enough
to look right, far enough apart that a trim landed beside the line it
was aimed at. It now draws the curve those commands work on, and falls
back to the local solve only where the kernel declines — a fit spline
through points in space is not a planar curve, so there is nothing for
it to say.

Closed fit splines were worse than that: the kernel's interpolation is a
clamped solve with no wrap in it, so the curve TRIM used never returned
to its start and the seam cut nothing. Repeating the first fit point
closes it. The seam is C¹ where the rest of the curve is C², which is
the honest limit of a clamped solve — the alternative was a curve with a
gap in it.

OFFSET's preview sampling had drifted the same way TRIM's had: an arc
and a circle read `center.x/.y` as world coordinates when the entity
stores them in its OCS, so an extruded one previewed on the wrong side;
every curved kind was cut at a fixed twenty segments per radian whatever
its size; and an ellipse rebuilt its axis frame from the major axis' X
and Y alone, dropping the Z the entity stores. A hundred lines of that
become one tessellation. A RAY now previews as far as an XLINE does
rather than as a stub the length of its direction vector.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-09 01:08:45 +03:00
Hakan SevenandClaude Opus 5 b36e85e46e refactor(snap): take an entity's snap points from its curve
Each entity built its own, and what they offered had drifted apart from
what the geometry actually has:

- An arc offered no quadrants at all. It now offers the ones its sweep
  covers, and no more.
- A spline offered its control points as endpoints. For a control-point
  spline those are not on the curve, so the cursor landed in empty space
  beside the geometry. It now offers its two real ends and its midpoint,
  with the fit points — which are on the curve — still on offer.
- An elliptical arc had no midpoint and no ends.

A new SnapHint::Endpoint carries an end that cannot go in key_vertices.
Consecutive entries there are taken to be joined by a straight segment
and their midpoint offered, which is right for a line or a polyline and
wrong for anything curved: an arc's two ends would also produce a snap
out on the chord. Splines lose their key_vertices for the same reason —
the chord between two fit points of a curvy spline does not run along
it.

A polyline's midpoints and arc-segment centres stay out of snap_pts
deliberately. The engine already derives a chain's midpoints from its
vertices, and a wire carrying a centre is treated as round elsewhere,
which a polyline with one bulge in it is not.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-09 00:43:44 +03:00
Hakan Seven 48332ee807 feat(area): add object and aggregate modes
Mesh-backed measurements need cached surface metrics. Keep shared loop collection available in web builds.
2026-08-03 22:31:20 +03:00
Hakan Seven 3e6693b93f feat(locale): localize full interface 2026-08-02 22:09:25 +03:00
Hakan SevenandClaude Fable 5 48e8988528 fix(spline): honour end tangents on a two-fit-point spline
fit_spline_polyline bailed to a straight chord below three fit points,
ignoring the stored begin/end tangents — a short tangent-clamped spline
rendered as a line. The C² solver handles n=2 as-is (both end rows are
clamp or natural conditions), so only the guard drops to n<2; tangentless
two-point splines still solve to the straight chord.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 23:36:15 +03:00
Hakan SevenandClaude Opus 4.8 db896ec632 fix(spline): interpolate fit-point splines with end tangents, grip via fit points
A fit-point SPLINE (no control vertices) was drawn with Catmull-Rom through its
fit points, which ignores the stored begin/end tangents and uses local slopes.
An open curve then peeled away from the true shape at its ends — budweiser's
handle 2735 lost the tangent-driven overhang at its top end.

A fit spline is the C² cubic that passes through every fit point clamped to the
stored end tangents. Replace Catmull-Rom (kept only for closed splines, whose
wrap the clamped solve doesn't model) with `fit_spline_polyline`: chord-length
parameterisation (honouring knot_parameterization), a tridiagonal Thomas solve
for the knot slopes with clamped-tangent or natural end rows, and Hermite
evaluation. Verified on 2735: the curve passes through all five fit points and
its ends follow begin_tangent/end_tangent (dir·tan = 0.997).

Also fixes grip-selection: grips()/apply_grip() only ever looked at
control_points, so a fit spline (0 CV) showed no grips and couldn't be
grip-selected or edited. Both now fall back to fit_points when there are no
control points — dragging a grip moves the fit point and the curve re-fits on
the next tessellation, keeping the end tangents. The mirror path already
transformed fit_points; move/rotate/scale run through acadrust.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-18 12:46:09 +03:00
Hakan SevenandClaude Opus 4.8 3f060a570a fix(pick): hit-test lineweight width and thickness walls
Selection tested a flat 8 px radius around each wire's centreline and
nothing else, so two things the user can see were not pickable.

A wire renders as a band `line_weight_px` wide, so pick now widens to that
rendered half-width via `pick_tolerance_px`, mirroring the wire shader's
`select(0.5, half_width, lwdisplay_enable)`. Behaviour is unchanged for
every standard weight (the widest, 2.11 mm, renders 7.97 px half-width —
just under the 8 px threshold); this keeps the two sides from drifting
apart if the display boost in `lineweight_to_px` ever changes.

Entities extruded by a DXF thickness draw their wall as four edges with
nothing in between, so the interior was a hole the cursor fell through.
WireModel gains `pick_tris`: triangles hit-testing treats as solid and the
renderer never uploads. Kept apart from `fill_tris` because that channel
reaches the GPU, and a wall that rendered shaded would change how every
thickness drawing looks. Ranked below `fill_tris` — those are drawn, these
are not, so where they overlap the drawn face is what the user means.

Walls are dropped when a wire is projected into a floating viewport:
`points` are reprojected and clipped into paper coords there, so cloned
model-space triangles would hit-test at a model-scale offset. Extruded
entities stay selectable by their edges, which do get projected.

The wall loop runs on every hover that misses everything else, so it
rejects on the wire's AABB first — an extruded circle is ~128 triangles
and a drawing full of thickness would otherwise project every wall on
each mouse move.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-17 14:58:55 +03:00
Hakan SevenandClaude Opus 4.8 e515fa3156 feat(spline): draw fit-point splines via Catmull-Rom interpolation
A spline stored by fit points (DWG R2013+ scenario 2) has no control points
or knots, so the renderer — which built its curve from control points —
produced nothing and the spline was invisible. When a spline has fewer than
two control points but at least two fit points, interpolate a smooth
Catmull-Rom curve through the fit points and emit it as a dense polyline.
Open ends use reflected phantom points; closed/periodic splines wrap.

Pairs with the acadrust bump (e8e422a) that decodes the R2013+ fit-point
scenario correctly — together they make the fit-point spline in
Annotation-2D-Mesh-Solid-BIM.dwg render.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-04 19:52:56 +03:00
Hakan SevenandClaude Opus 4.8 4d2471cafd feat(properties): audit editable vs read-only across all entities
Reclassify every property row to match standard palette behaviour: a row
is editable only when its value maps directly to a stored field AND its
apply_geom_prop handler is wired; computed/derived/count/handle/
cross-object values stay read-only.

Newly editable (all apply-wired): attribute tag/prompt; ellipse major
radius; hatch Double; lwpolyline closed/linetype-gen; mline style;
multileader annotative/landing/background-fill; polyline 2D+3D vertex
coords/segment widths/linetype-gen; shape style; spline control/fit
point coords, weight, fit tolerance, tangents; table break-option flags;
text alignment point, upside-down, backward; underlay display flags.

Corrections: spline Degree -> read-only (editing desynced the knot
vector); removed a duplicate leader "Arrowhead" row.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-03 08:06:00 +03:00
Hakan SevenandClaude Opus 4.8 f50ad0dde4 feat(properties): fill placeholder rows from acadrust fields + doc lookups
Bump acadrust to a46cb8b (spline knot parameterization + mtext line-space
style now stored) and populate rows the builders left blank:

Entity-local: spline Knot Parameterization + closed-spline Area (shoelace),
mtext Line space style, hatch Spacing (pattern-line offset), leader Text
offset (annotation offset), region Area + Perimeter (wire-loop approx).

Doc-dependent (resolved in app/properties.rs where the document is
reachable): insert Block Unit + Unit factor (block record units vs
INSUNITS), underlay Name + Path (definition object), leader Text style /
vertical placement / overall scale and tolerance Text style (dimension
style), multileader Max points + segment angles (mleader style).

Rows needing an annotation-scale / sheet-set subsystem or an internal
PDF/DWF layer parser are intentionally left blank.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-03 00:05:58 +03:00
Hakan SevenandClaude Opus 4.8 1043a3d561 feat(properties): fill spline Fit Tolerance + tangent rows from acadrust
The Fit Tolerance and Start/End Tangent X/Y/Z rows were placeholders while
the pinned acadrust lacked those fields. Now that the bump exposes
Spline::fit_tolerance / begin_tangent / end_tangent, populate them.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-02 23:34:01 +03:00
Hakan SevenandClaude Opus 4.8 b9cebab951 feat(properties): conform entity property groups to PROPERTIES.md
Rewrite each entity's property builder to the per-entity group/row spec:
the correct sections (Geometry, Misc, Text, Pattern, Lines & Arrows, Leaders,
Block, …) with the spec's rows and real computed values — Diameter,
Circumference, Area, Delta X/Y/Z, Angle, Total angle, Arc length, Radius ratio,
etc. — plus apply routing for the editable rows.

Covers ~26 entities (Line, Circle, Arc, Point, Ellipse, Spline, polylines,
Ray/XLine, Insert, Text, MText, Hatch, Leader, MultiLeader, Tolerance, Solid,
Solid3D/Region/Body, meshes, MLine, Raster/Wipeout, Underlay, Table, Viewport,
Attribute, OLE). The 3D Visualization/Material group and the full Dimension
spec are deferred (they need document access for material/dimstyle name
resolution). Rows whose data is not in the pinned acadrust revision are shown
as empty placeholders, to be filled when the dependency is bumped.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-02 23:03:18 +03:00
Hakan SevenandClaude Opus 4.8 b2b2bfb246 refactor(properties): entity property groups return Vec<PropSection>
Groundwork for making the Properties panel conform to the per-entity group/row
spec (PROPERTIES.md). The panel already renders a Vec of sections, but each
entity could only express a single geometry section.

Change PropertyEditable::geometry_properties (and the EntityTypeOps enum
dispatch) to return Vec<PropSection>, update the two impl macros, and flatten
in properties_sectioned so the panel shows [General] + the entity's groups.
Every entity builder is wrapped to return its existing section as a one-element
Vec — pure mechanical change, no rows or titles altered, panel output identical.

Enables Phase 2: rewriting each entity's groups/rows to match the spec.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-02 22:46:19 +03:00
Hakan SevenandClaude Opus 4.8 7fe7e0230d refactor(scene): group modules into role-based subfolders
Move the 30 flat modules under src/scene/ into six folders by role:
convert (entity→geometry), text (fonts/shaping), model (render models),
pick (hit-test/selection/grips/xclip), view (camera/transform/render),
cache (block + property caches). pipeline/ is unchanged.

Full path migration — all `scene::X` references become
`scene::<group>::X`, no compatibility re-exports. render.rs items that
were `pub(super)` (parent was scene) become `pub(in crate::scene)` to
keep their original scene-wide visibility.

No behavior change; build and tests are green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 11:33:46 +03:00
Hakan SevenandClaude Opus 4.8 25715d1b03 fix(grips): compute grip positions in f64 so they sit on the wire
Grip markers drew visibly offset from the entity on large-coordinate
(UTM-scale) drawings: every Grippable impl cast the f64 entity coordinates
to f32 BEFORE the world-offset subtraction, and at 1e7 magnitude an f32
holds ~0.9 drawing units of rounding error. The wires subtract the offset
in f64 first (precision-preserving), so the grips landed up to ~1 unit
away from the geometry they belong to.

GripDef.world is now DVec3: producers fill it straight from the f64
entity data, refresh_selected_grips subtracts the world offset in f64,
and only the small offset-local result is cast to f32 for screen-space
projection (grips_to_screen / find_hit_grip) and drag state.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-11 00:04:37 +03:00
Hakan SevenandClaude Opus 4.7 4b4fe5ca6d feat(grip): popup menus for polylines, spline, mline, hatch, leaders, text
Vocabulary added: AddFitPoint, RemoveFitPoint, Refit, RefineVertices,
TangentDirection, MoveWithText, StackText, UnstackText.

Polyline/Polyline2D/Polyline3D: vertex Add/Remove. MLine: same.
Spline: CV Add/Remove plus Refine Vertices (chord-midpoint insertion,
weights kept in sync, knots cleared so to_truck rebuilds uniform vec).
Hatch: centroid Origin/Angle/Scale (Angle and Scale prompt for value).
Leader: vertex Add/Remove (arrow + centroid stay Stretch-only).
MultiLeader: vertex Add/Remove Leader; MText location Move-with-Leader /
Move-Independent (menu live, structural edits stubbed). Text/MText:
Rotate prompts for degrees and sets rotation.

Spline, Leader, MultiLeader switched from impl_entity_basics! to manual
trait impls so grip_menu can be overridden.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-29 15:14:43 +03:00
Hakan SevenandClaude Opus 4.7 9b5073752d refactor(entities): collapse trait wrapper boilerplate via macro
Every entity file ended with the same three-trait wrapper block:

    impl Grippable for T {
        fn grips(&self) -> Vec<GripDef> { grips(self) }
        fn apply_grip(&mut self, ...) { apply_grip(self, ...) }
    }
    impl PropertyEditable for T { ... }
    impl Transformable for T { ... }

— 23 lines of pure delegation around five same-named free functions.

Replace with `crate::impl_entity_basics!(T);` (a macro in
entities/traits.rs that expands to the three trait impls).

Applied where the file already follows the free-fn delegation
convention (Arc, Circle, Ellipse, Insert, Leader, Line, LwPolyline,
MultiLeader, Ole2Frame, Point, Spline, Viewport). Files that inlined
the trait method bodies directly (Ray, XLine, Shape, Solid, Tolerance,
MLine, Mesh, Solid3D/Region/Body, Table, RasterImage, Wipeout,
Underlay, Hatch, Polyline, Polyline2D, Polyline3D, Dimension) left
alone — they already skip the indirection. Text/MText/Attribute use
the with-text-style flavour and will move once that variant is needed
beyond the existing two manual impls.

A second macro `impl_entity_basics_with_text_styles!` is included for
the text-like flavour but no entity uses it yet.

Net: -204 lines.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-26 17:16:06 +03:00
Hakan Seven 288ccff95a fix(render): preserve f64 precision until world_offset subtraction
Entity converters were casting WCS coordinates to f32 before the renderer
subtracted world_offset, which destroyed sub-unit precision on drawings
sitting at large coordinates (typical UTM-style CAD files). The multileader
case made it visible — glyphs warped under rotation — but every entity
that emits TruckObject::Lines/SegmentedLines/key_vertices/fill_tris was
affected.

Type changes (in src/scene/acad_to_truck.rs):
- TruckObject::Lines / SegmentedLines: Vec<[f32; 3]> → Vec<[f64; 3]>
- TruckEntity::key_vertices / fill_tris: f32 → f64

All ~17 producer files updated to keep WCS coordinates in f64 right up to
the TruckEntity boundary. The consumer in scene/tessellate.rs now performs
`(x - ox) as f32` (f64 subtract, then cast) for points, key_vertices, and
fill_tris — preserving precision near the camera origin even when the
drawing sits at million-unit coordinates.

catmull_rom_pts (multileader spline) is now f64; the scene path widens its
offset-relative f32 ctrl points to f64 around the call and narrows back at
the output.

Notes:
- Table layout still uses glam::Vec3 (f32) internally; widened to f64 only
  at the TruckEntity boundary for type compatibility. Separate cleanup to
  switch its internal math to f64 is tracked.
- snap_pts and tangent_geoms remain f32 — they are UI feedback (snap
  candidates, tangent hints) and the precision is sufficient there.
2026-05-12 23:47:40 +03:00
Hakan ca07268c7c cargo fmt 2026-05-12 10:36:15 +03:00
Hakan Seven 048bb41aef feat(render): solid fill for PolyfaceMesh and PolygonMesh entities
PolyfaceMesh and PolygonMesh were wireframe-only. They now render with
the same flat-color solid fill as Face3D entities.

- Add fill_tris: Vec<[f32;3]> to TruckEntity and WireModel
- PolyfaceMesh: fan-triangulate each face into fill_tris
- PolygonMesh: triangulate each grid quad into fill_tris
- tessellate.rs Lines branch: apply world_offset to fill_tris
- Face3DGpu::from_wires: collect fill_tris from all wires in addition
  to Face3D key_vertices quads
- upload_face3d: pass all_wires so fill_tris are batched into one draw call
2026-05-06 23:24:26 +03:00
Hakan SevenandClaude Sonnet 4.6 7b7186a470 feat: use rational NURBS (NurbsCurve) when spline weights are present
Splines backed by rational NURBS (circles, conics) were tessellated as
uniform B-splines, losing the weighting that defines the curve shape.
Now BSplineCurve<Point3> is used only when weights are absent or empty;
when weight data is present a NurbsCurve<Vector4> is built from the
homogeneous control points so circles and conics render accurately.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-02 17:10:38 +03:00
Hakan SevenandClaude Sonnet 4.6 0bb4209e2c fix: closed spline gap, hatch spline boundary, and spline commit grouping
- Spline: if flags.closed/periodic and B-spline endpoints don't meet,
  add an explicit closing Wire segment to eliminate the visual gap.
- Hatch BoundaryEdge::Spline: evaluate the B-spline at 16 uniform
  parameter samples (de Boor via truck BSplineCurve) instead of
  plotting raw control points as a polyline; falls back to control-
  point polyline for degenerate knot/degree inputs.
- Ellipse arc key_vertices and LWPolyline midpoint grips committed
  separately; this commit wraps the spline-related fixes.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-27 02:26:26 +03:00
Hakan SevenandClaude Sonnet 4.6 5216c247c2 fix: Spline snap points and TextStyle backward/upside-down rendering
Spline entities now expose fit_points (or control_points when no fit
points are defined) as key_vertices, enabling Endpoint and Midpoint
snap to reach spline construction points.

TextStyle is_backward flag is applied by negating the width_factor,
producing horizontally mirrored text. is_upside_down rotates the text
180°. Both tessellate_text_ex and text_local_bounds now handle negative
width_factor without clamping it to zero.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-26 22:10:32 +03:00
Hakan SevenandClaude Sonnet 4.6 320312e81a fix: replace degenerate same-vertex edges with Point in lwpolyline and spline
Empty LwPolyline and single-control-point Spline fallbacks were calling
builder::line(&v, &v) with the same Rc vertex, triggering the
truck-topology panic "Two same vertices cannot construct an edge."

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-25 20:30:26 +03:00
Hakan Seven d2fdac7f1e first commit 2026-03-22 14:56:27 +03:00