Commit Graph
21 Commits
Author SHA1 Message Date
adambelis a08937661b Width profiles: near-outline sampling and review fixes (#3145) 2026-11-11 22:09:10 +01:00
adambelis 1c43204d15 PR 3145: fixes gradient and stroke outline issues; resolves review items 2026-11-10 13:08:47 +01:00
Sven Lafebre 1b62f86b8c Fix #5714: doc comments on the width profile tests 2026-11-07 05:15:10 +07:00
Sven Lafebre 9da56717a0 Fix #5714: widths run smoothly between width points
The width between two width points was a straight line, so a variable-width
stroke turned a corner at every width point.

- Each side of a width profile now follows a monotone cubic (Fritsch-Carlson)
  through its points: no kink at a point, flat at a point wider or narrower
  than both neighbours, never past either point of a span. Points on one line
  stay on it and two-point tapers are unchanged; stacked points still step.
- widest() counts each span's wider left and wider right, since the two sides
  needn't peak together.
- The outliner samples straight runs between width points closely enough to
  follow the curve to the drawing tolerance (at most 64 samples per span and
  4,096 per piece); straight spans get none.
- Tests that expected straight-line widths now expect the curve. The stroke
  gradient coverage test uses Teardrop instead of Wave until #5964 is fixed.
2026-11-07 05:15:09 +07:00
dotCooCoo e6819a86f0 Rendering: open width-profile strokes outline only near the view 2026-11-06 12:42:33 +07:00
Dylan M. Taylor 13b0bbe639 Linear-time scans: Add Anchor Points, swatch library adds, placed-document export, width profiles, symbolism previews
Add Anchor Points builds the new anchor list in one pass, Swatch Library add looks names up in sets, full_documents dedups placed keys with a set, width profile lookups and mark insertion use binary search, and the symbolism tools cap and thin the previews of a long drag as Liquify does.
2026-11-04 08:13:18 +07:00
Adam 565235b54b Merge remote-tracking branch 'origin/main' into feat/width-smooth-profile
# Conflicts:
#	crates/doc/src/appearance.rs
#	crates/render/src/lib.rs
#	crates/ui-egui/src/dialogs/width_point.rs
2026-10-27 10:01:09 +01:00
Dylan M. TaylorandClaude Opus 5.5 e2a870e602 Security: bounded flattening, plug-in budgets and caches, loaded textures, Revolve paint copies, color book names and ICC names
CodeRabbit findings #4, #34, #46, #51, #77, #101, #170, #171, #188:

- `vectorcraft_geom::flatten`: kurbo's flatten with each curve's tolerance raised so it makes
  at most ~4096 segments (non-finite curves become a line); the canvas outline and
  variable-width strokes use it.
- Plug-ins: a heavy filter's budget is the host's `Limits::heavy_fuel`/`heavy_wall_time_ms`,
  never more; a start function runs on one fuel slice; the effect cache holds at most ~1M
  anchors of inputs and results, and doesn't keep a result past a quarter of that.
- Loaded textures are cached by their PNG (base64 spacing doesn't matter), and live textures
  hold at most 512 MB together.
- Revolve draws the source path when the faces' paint copies would pass 256 MB.
- Color books: names with prefix and suffix take at most 64 MB.
- ICC: localized-name (`mluc`) records over 1 MB together are refused before moxcms copies them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 17:19:54 +07:00
Adam 361cb43c4f #3145: fit width-profile outlines to cut export size 2026-10-23 10:54:09 +02:00
Adam 5759fd3d69 Width profiles: sample outlines adaptively 2026-10-23 01:06:43 +02:00
Adam 478b3db262 M3.64: width profiles end with one-sided PCHIP slopes 2026-10-22 21:31:05 +02:00
Adam c6c359fb76 effects: keep width samples on their flattened branch 2026-10-21 05:19:36 +02:00
Adam 2c363d6b88 effects: offset profile samples along the source tangent
Profile samples added inside a flattened chord used the chord's direction for their normal, so a zoomed outline jogged at every flatten vertex. Keep the source segment that drew each flattened piece and ask it for the exact tangent at added samples; fit runs with coordinate-wise Fritsch-Carlson tangents. Adds a regression test and records the change in the parity evidence.
2026-10-21 03:24:01 +02:00
Adam 67933d404f effects: keep a wide constant stroke over its centre
When a constant profile's half-width reaches the local radius of curvature, the smooth inner offset crosses the centre and the non-zero fill leaves a hole. Cut through the centre at those samples; the cut is inside the painted stroke. Varying profiles keep the offset because their crossings can be real caustics, where a cut would show as a slit. Regression test: a radius-5 circle with a 30 pt stroke contains its centre.
2026-10-20 10:55:33 +02:00
Adam 11e33a85b8 effects: keep width-profile outlines smooth on curves
Offset smooth points along the curve's normal instead of the flattened polyline's miter (w/cos), which spiked the edge at every sample on tight curves. Fit runs with chord-length Catmull-Rom tangents so a short sample cannot flatten the long segment beside it, and add a coarse wide-circle regression test.
2026-10-20 10:02:38 +02:00
Adam 04dbee48db M3.64: width outlines stay smooth where their width changes fast 2026-10-20 10:02:37 +02:00
Adam bcbd31f3a4 M3.64: width profiles run smooth between their points
A width profile's sides now run as a cubic Hermite curve through the
points, its slope at each one the weighted harmonic mean of the secants
either side (Fritsch-Carlson). That is C1 continuous and monotone
between points, so it never overshoots them, a width change eases in and
out instead of making a corner, and moving one point only reshapes the
two segments beside it. The ends are flat; two points at one t are still
a step.

The outline samples the curve along the path (densify subdivides each
profile segment) so the smooth width draws on straight runs too, and a
flat profile keeps its old straight pieces.

Hit testing now counts every segment near enough to reach the point, as
the wider side of a farther one can paint past the nearest; gradient
strips reach over projecting dash caps. Tests updated for the new
sampled widths; new tests cover smoothing, monotonicity, locality and
steps.
2026-10-20 10:02:37 +02:00
bflatasticandClaude Opus 5.5 025d758abe M3.83: gradients within, along or across a stroke
The Gradient panel's Stroke buttons now work: with the Stroke proxy on a
linear or radial gradient they lay it within the stroke (placed on the
page, as before), along the path (start to end of each subpath) or across
it (left edge to right edge; inside/outside strokes span their visible
side). `paint.editGradient {stroke: true, strokeMode}` sets it (new art
too with nothing selected); `document.inspect` reports
strokeOptions.gradientMode. Model: StrokeLayer.gradient_mode (default
Within, not written).

Shared geometry in effects::stroke::gradient: ribs on the bisectors of
the flattened path (fans round sharp corners, widened under arrowheads,
following width profiles) cut slices each painted with a linear piece of
the gradient (Gradient::span). The canvas and raster export clip them to
the stroke's outline and arrowheads, grown a pixel and composited with
copy so there are no seams and translucent stops paint once. SVG and PDF
write the same slices clipped to the outline, with a warning (new
vectorcraft_svg::export_with_report). Outline Stroke and Expand make
gradient meshes clipped to the outline. Mesh quads now grow along their
edges so thin quads show no seams. Dashes, profiles and arrowheads keep
working in both modes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 05:47:54 +07:00
bflatasticandClaude Opus 5.5 b79262d4f4 M3.64: Width tool depth: Width Point Edit, multi-select, Alt-copy and discontinuous points
Double-clicking a width point opens Width Point Edit (Side 1, Side 2, Total Width, a link
keeping the sides equal, Adjust Adjoining Width Points, Delete; dialog `widthPoint`, also
`ui.widthPointEdit`). Shift-click selects several width points, which drag and widen together
as one undo step; Alt-dragging a point's centre copies it (`stroke.widthPoint.copy`); dropping a
point onto another makes a discontinuous point (two points at the same t), and the stroke's
width steps there. `stroke.widthPoint.set` takes `adjustAdjoining`, keeps a discontinuous point
when only its widths change, and `stroke.widthPoint.remove` takes `indices`. Flipping a profile
along the path swaps the sides of its steps; `document.inspect` reports `widthPoints`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 04:38:09 +07:00
bflatasticandClaude Opus 5.5 454c5f8620 M3.39: width profiles follow the path through dashes, and their corners take the join
Each dash and dot of a profile stroke is as wide as the profile where it sits
along the whole path (before, dashes dropped the profile). Corners of profile
strokes take the stroke's miter (within the miter limit), round or bevel join on
the outer side and stay clean on the inner side, instead of a miter clamped at
4x; smooth points of curves bend round. Canvas, SVG/PDF outlines and Outline
Stroke share the geometry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:40:52 +07:00
bflatasticandClaude Opus 5.5 09d7aaa7f4 M3.18: shared stroke-geometry module for arrowheads, width profiles and the profile preset catalogue
Arrowheads, width-profile outlines and dashed line outlines now come from
vectorcraft_effects::stroke (stroke_pieces, line_outline, width_outline), used by
the canvas and PDF export alike; the renderer caches profile outlines too. Width
profiles are a doc-level catalogue (WidthProfile::PRESETS, preset, preset_id,
id_of) shared by stroke.set and the Stroke panel; stroke.set rejects unknown
profile ids and its params doc lists the arrowhead names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 17:16:17 +07:00