Commit Graph
661 Commits
Author SHA1 Message Date
BFlat 695721f9df Merge pull request #6380 from dotCooCoo/fix/preview-bounds-butt-line
Transform: butt-capped lines measure to their end points with Use Preview Bounds
2026-11-10 09:59:06 +07:00
dotCooCoo bed632cc65 Transform: butt-capped lines measure to their end points with Use Preview Bounds
With Use Preview Bounds on, the visual box of a butt-capped stroke on an
open path grew by half the stroke weight past each end of the path, where
a butt cap paints nothing. A 2 pt vertical line set to H: 10 mm in the
Transform panel drew about 9.3 mm long.

The box of an open subpath with butt caps no longer reaches past its
ends. A straight segment's box comes from the four corners of its stroked
rectangle, and a curve's box comes from the stroke at its ends and where
its tangent is square to an axis. Bevel joins, and miter joins the limit
bevels, are measured by the stroked corners, and round joins by the arc
they paint outside the turn, instead of half the weight all round. Round
and projecting caps, arrowheads, closed paths and miter tips measure as
before. Two cases can still measure larger than the stroke paints: a
curve that bends more tightly than half the weight, and a butt-dashed
path whose first or last dash is a gap, which is measured to the path's
ends.

A horizontal or vertical butt line now measures one stroke weight less
along its length in W or H, and Align places it by its end points. A
slanted line's box shrinks by less: the stroke's corners still reach
past the end points across the line. object.setBounds measures as drawn,
after each scale it tries, a butt-capped open path with a segment at a
slant, symbol instances' art included.
2026-11-09 18:16:48 -08:00
dotCooCoo 7ff3d25697 Part of #5632: Convert to Grayscale stores an image with one gray channel
Edit Colors > Convert to Grayscale wrote the new image as an RGBA PNG, so
a PDF wrote it as DeviceRGB, in a CMYK document too. The image is now
stored as a gray PNG, with alpha only when some pixel is not opaque. Adjust
Colors with mode gray and convert does the same.

An RGB image whose pixels are already neutral now converts, and an image
already stored as gray with no visible change is left as it is.
2026-11-09 16:15:12 -08:00
adambelis 585621a661 Blend: the spine keeps only the subpath it samples
Spine stored a full clone of the blend's PathData but only ever read its
first subpath. Store that SubPath instead, which drops the extra
allocation and makes the field's purpose clear. Document the field and
the two cases where Spine::new returns None.
2026-11-09 10:06:49 +01:00
dotCooCoo f29c75a231 Plug-ins: hit tests re-evaluate live art when a swatch or color group changes
A painted live effect's pieces name swatches, so a kept evaluation could outlive a swatch that was renamed or removed and answer a click with art the document no longer draws. The kept evaluation now holds the document's swatches and color groups and is stale when they change.
2026-11-08 19:13:37 -08:00
BFlat c5c1913f48 Merge pull request #6265 from dotCooCoo/feat/plugin-effect-paint
Plug-ins: live effects at API level 2 can paint the pieces they return
2026-11-09 10:50:46 +08:00
dotCooCoo bd5ea2a1bf Merge main into feat/plugin-effect-paint 2026-11-08 17:59:33 -08:00
dotCooCoo c603030252 Merge main into feat/visibility-variables-drive-locked-art 2026-11-08 17:31:05 -08:00
dotCooCoo 3ccc919936 Merge main into feat/visibility-variables-drive-locked-art 2026-11-08 17:30:32 -08:00
dotCooCoo d6a91b6142 Merge main into feat/plugin-effect-paint 2026-11-08 17:26:35 -08:00
BFlat 636ffe78ce Merge pull request #5070 from dotCooCoo/feat/kinsoku-shori-type
Paragraph: Kinsoku Shori Type chooses Push In First, Push Out First or Push Out Only
2026-11-09 09:22:52 +08:00
dotCooCoo 3ea13d88d3 Merge main into feat/kinsoku-shori-type
# Conflicts:
#	crates/engine/src/cmd/panelcmds.rs
2026-11-08 16:47:29 -08:00
dotCooCoo 0ce616da4b Merge main into feat/kinsoku-shori-type
# Conflicts:
#	crates/engine/src/cmd/panelcmds.rs
#	crates/text/src/layout/breaking.rs
#	crates/ui-egui/src/panels/paragraph.rs
#	docs/target-app-parity.md
2026-11-08 16:13:35 -08:00
dotCooCoo e2bac253f1 Merge main into fix/fitted-gradient-turns-with-transform 2026-11-08 15:56:12 -08:00
BFlat d83bb6c0b8 Merge pull request #5058 from dotCooCoo/feat/character-anti-aliasing-method-r
Character: Anti-aliasing Method sets how a type object's edges are drawn as pixels
2026-11-09 07:47:57 +08:00
BFlat 165810aa6c Merge pull request #6104 from dotCooCoo/fix/graph-series-object-opacity
Graphs: a series keeps the object opacity and blend modes set on one of its marks
2026-11-09 07:47:29 +08:00
BFlat 0cb2d5dbd8 Merge pull request #5030 from dotCooCoo/fix/issue-3748-r
Fix #3748: in isolation mode, art outside a clip group's mask can be selected and dragged
2026-11-09 07:46:54 +08:00
dotCooCoo 83f1333d1c Effects: a Transform effect that flattens the object leaves a fitted gradient fitted with its options on
With Scale Strokes & Effects or Transform Patterns on, the path and each copy are moved by Node::transform, which fixed a fitted gradient to its fit before a scale of 0 flattened it to one color. Scaling::keep_fit_when_flat keeps the gradient fitted under a map that flattens the object (node::flattens, the gate fixes_fit already used), and the Transform effect's path transforms set it.

docs/target-app-parity.md also says that the Gradient tool still shows a fitted gradient's vector at its unturned fit.
2026-11-08 15:38:07 -08:00
dotCooCoo da7194030f Merge main into feat/plugin-effect-paint 2026-11-08 15:01:41 -08:00
bflatastic 8e45313501 Blend: the spine's ends are its exact end points 2026-11-09 05:06:28 +07:00
dotCooCoo f88815abd9 Merge main into fix/graph-series-object-opacity 2026-11-08 14:02:59 -08:00
bflatastic d58000b5df Objects on Path: the stretched-group test allows the spine's rounding 2026-11-09 04:52:48 +07:00
bflatastic 863023d7de Blend: a vanished spine tangent takes the curve's direction nearby; the unreachable copy after the loop goes 2026-11-09 04:45:17 +07:00
adambelisandCraft Bot 2709a5191f M2.1: blend Align-to-Path smooth on curved spines
Spine::at_length computed tangents from flattened polyline chords, which
are piecewise constant and jump discretely between chords on curved
spines. Steps oriented to the spine with Align to Path stuttered at
large step counts.

Fix: store the original PathData in Spine and compute the tangent from
the original cubic at the true arc-length position via ParamCurveDeriv,
proportionally mapping flattened arc-length s onto each segment's curve
arclength using anchor_length values, instead of using piecewise-constant
chord directions.

Updated segment-selection condition in Spine::at_length to skip segments
whose flattened end is not greater than start before checking whether s is
within the segment. Preserve the existing final-segment fallback so
at_length(0.0) selects a nonzero segment and uses its tangent.

Added zero-derivative fallback tangent for blend-step sampling to keep
Align to Path from passing a zero tangent to atan2.

Added missing docstrings for Spine::new and Spine::length to satisfy
docstring coverage requirements.

Co-authored-by: Craft Bot <bot@storytold.dev>
2026-11-09 04:42:48 +07:00
adambelisandCraft Bot cb5300415a M2.1: blend Align-to-Path smooth on curved spines
Spine::at_length computed tangents from flattened polyline chords, which
are piecewise constant and jump discretely between chords on curved
spines. Steps oriented to the spine with Align to Path stuttered at
large step counts.

Fix: store the original PathData in Spine and compute the tangent from
the original cubic at the true arc-length position via ParamCurveDeriv,
proportionally mapping flattened arc-length s onto each segment's curve
arclength using anchor_length values, instead of using piecewise-constant
chord directions.

Updated segment-selection condition in Spine::at_length to skip segments
whose flattened end is not greater than start before checking whether s is
within the segment. Preserve the existing final-segment fallback so
at_length(0.0) selects a nonzero segment and uses its tangent.

Added missing docstrings for Spine::new and Spine::length to satisfy
docstring coverage requirements.

Co-authored-by: Craft Bot <bot@storytold.dev>
2026-11-09 04:42:48 +07:00
adambelisandCraft Bot b8d23e280b M2.1: blend Align-to-Path smooth on curved spines
Spine::at_length computed tangents from flattened polyline chords, which
are piecewise constant and jump discretely between chords on curved
spines. Steps oriented to the spine with Align to Path stuttered at
large step counts.

Fix: store the original PathData in Spine and compute the tangent from
the original cubic at the true arc-length position via ParamCurveDeriv,
proportionally mapping flattened arc-length s onto each segment's curve
arclength using anchor_length values, instead of using piecewise-constant
chord directions.

Added missing docstrings for Spine::new and Spine::length to satisfy
docstring coverage requirements.

Co-authored-by: Craft Bot <bot@storytold.dev>
2026-11-09 04:42:48 +07:00
adambelisandCraft Bot 5589049a47 M2.1: blend Align-to-Path smooth on curved spines
Spine::at_length computed tangents from flattened polyline chords, which
are piecewise constant and jump discretely between chords on curved
spines. Steps oriented to the spine with Align to Path stuttered at
large step counts.

Fix: store the original PathData in Spine and compute the tangent from
the original cubic at the true arc-length position via ParamCurveDeriv,
proportionally mapping flattened arc-length s onto each segment's curve
arclength using anchor_length values, instead of using piecewise-constant
chord directions.

Reviewed-by: craft-house-rules

Co-authored-by: Craft Bot <bot@storytold.dev>
2026-11-09 04:42:48 +07:00
Adam Belis 4851736364 M2.x: blend Align-to-Path smooth on curved spines
Spine::at_length computed tangents from flattened polyline chords, which
are piecewise constant and jump discretely between chords on curved
spines. Steps oriented to the spine with Align to Path stuttered at
large step counts.

Store the original PathData in Spine and compute the tangent from the
original cubic at the true arc-length position via ParamCurveDeriv,
instead of flattened chord directions.

Adds a regression test walking 200 samples along a curved spine and
asserting each tangent step stays under 0.1 radians.
2026-11-09 04:42:48 +07:00
dotCooCoo d8ff63f5d0 Merge main into feat/character-anti-aliasing-method-r 2026-11-08 13:24:43 -08:00
dotCooCoo e7c381a53f Merge main into fix/issue-3748-r
# Conflicts:
#	crates/doc/src/hit.rs
2026-11-08 13:14:33 -08:00
dotCooCoo 9d0b2b0c1c Merge main into fix/fitted-gradient-turns-with-transform 2026-11-08 13:07:04 -08:00
dotCooCoo 11703bd350 Merge main into feat/plugin-effect-paint 2026-11-08 12:56:42 -08:00
dotCooCoo 7b8d5ba8f5 Type: Tab and Shift+Tab move a list item down and up a level
Squashed from #5037 onto main.
2026-11-09 03:39:05 +07:00
dotCooCoo 4724475e1a Effects: hidden layer children and edited freeform gradients follow the Transform and Free Distort effects
A path that keeps its shape because the effect would pass the geometry
budget holds a group's own placed gradients in place. A hidden path in a
layer, which the layer's bounds leave out, no longer holds them, so the
layer's gradient moves with its visible art. The art's own gradients
follow the same rule.

A freeform gradient whose points were edited keeps its vector fitted to
the bounds, so it did not count as placed and its points stayed behind
under a Transform effect and Free Distort unless another placed gradient
was in the same appearance. Its points now move like a placed gradient.
2026-11-09 03:14:41 +07:00
dotCooCoo 11f1d228de Type: Align to Path moves each character by its own font's metrics
Align to Path moved the whole line by the largest ascent and descent
among its characters. Each character now moves by its own font, size
and vertical scale. Ascender runs the top of the font's em box along
the path: the ideographic em box that the font's BASE table gives, or
in a Chinese, Japanese or Korean font the one that its typographic or
hhea box, a one-em box-drawing cross or its ideographs give; else an
em box centered on the cap height. Descender runs the bottom of the font's
bounding box along the path, and Center runs a line at half the height
of the top of the font's H along it. In vertical type, Ascender and
Descender move the characters half an em, and Center leaves them on
the path.

Existing documents with Align to Path set to Ascender, Descender or
Center lay their type out at a new distance from the path.
2026-11-09 03:14:41 +07:00
dotCooCoo 4b5f89e903 Merge main into fix/fitted-gradient-turns-with-transform 2026-11-08 12:09:50 -08:00
dotCooCoo b88f4ce311 Merge main into feat/character-anti-aliasing-method-r 2026-11-08 12:08:40 -08:00
dotCooCoo c94269ff72 Type: Composite Fonts… sets each class of character in a font of its own
Squashed from #6244 onto main.
2026-11-09 03:08:23 +07:00
dotCooCoo 81c5b047bc Merge main into feat/plugin-effect-paint 2026-11-08 11:36:48 -08:00
dotCooCoo aee3a76e2a Merge main into fix/fitted-gradient-turns-with-transform 2026-11-08 11:16:42 -08:00
bflatastic 8199f30c74 Export for Screens: the asset-name test sets main's 3D format field 2026-11-09 02:03:07 +07:00
NetterSilverandClaude Haiku 5.5 c085185296 Ignore non-digit suffixes in next_asset_name
Found while testing crates/doc/src/assets.rs:43 (Document::next_asset_name)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 02:03:06 +07:00
VladimirCeric e3817d6b50 Typographer's Quotes: a lone typed ” opens like a straight quote
Squashed from #5459 onto main.
2026-11-09 02:03:00 +07:00
dotCooCoo 64817c2e8c Merge main into feat/plugin-effect-paint 2026-11-08 10:46:47 -08:00
dotCooCoo cca9a42c6c Effects: Effect › SVG Filters applies the document's SVG filters as live effects
Squashed from #6200 onto main.
2026-11-09 00:34:23 +07:00
dotCooCoo 51bab66409 Envelopes: document how a straight segment's handles count in the top object corner test
A top object's corner test treats a straight segment as having its
handles a third of the way along it, so an anchor between two straight
segments is a corner only when it turns out of the outline. docs/mcp.md
and the doc comment now say so, and a new test covers an inward anchor
between straight segments. The bulging-curve corner test checks against
a fixed point instead of the same nearest-point routine the code uses.
2026-11-09 00:31:07 +07:00
dotCooCoo ed6c0d434f Envelopes: a top object's corners keep their saved anchors when reflected
Saved corners from an .ai file stay on the same anchors when the
envelope is reflected, as they already did when it was moved or
rotated. A reflected outline runs counter-clockwise; the corners were
then mapped as if the outline had been drawn the other way round, no
longer fit it, and were found again from the outline.

On a tie between two corner anchors, or two nearest outline points,
the one met first along the outline in the order it was drawn wins,
for outlines drawn counter-clockwise too.

The docs state the corner rule in full: a handle 0.01 pt long or
shorter makes an anchor a corner whichever way it turns; otherwise its
tangent unit vectors must differ by 0.01 or more in x or y and turn
out of the outline. Content spreads evenly along a segment without
handles.
2026-11-09 00:31:07 +07:00
dotCooCoo 63dfcba8ba Envelopes: a top object's sides share the content by control polygon length
A top-object envelope splits its outline into four sides at four corners
and maps the content onto the Coons patch of those sides. The sides
shared out the content by length, and the corners were the anchors or
curve points nearest the bounding box corners.

Each piece of a side between anchors and corners now takes a share of the
content's width or height in proportion to the length of its control
polygon, spread along the piece by its curve parameter. A straight
segment spreads it evenly by length. A rounded rectangle stays close to
the identity, and the content follows the curves of a side whose segments
differ in size.

Each corner is now the nearest anchor to its bounding box corner among
the sharp anchors that point out of the outline and lie within 47.5% of
the box's width and height from that corner. A handle shorter than
0.01 pt, or tangent unit vectors that differ by 0.01 or more in x or y,
makes an anchor sharp, and the first one along the outline wins a tie.
Without such an anchor, the corner is the outline's nearest point to the
box corner, moved onto the end anchor of its segment within 0.5% of the
segment's parameter. Envelopes opened from .ai files keep the corners the
file saves.

These are VectorCraft's own rules. Existing top-object envelopes redraw by
them. Gradient meshes made from a path keep their sides measured by length.

- crates/doc/src/live/mesh.rs: Coons::from_top_object and the corner
  search, used for top-object envelopes.
- EnvelopeKind::TopObject gains an optional `corners` field (anchor
  indices); the .ai importer fills it from the mesh's corners.
- Params text of object.envelope.makeWithTopObject, docs/mcp.md
  (Envelopes) and a progress line in docs/target-app-parity.md.
2026-11-09 00:31:06 +07:00
dotCooCoo 581ced3213 Transform: Align to Pixel Grid keeps an object's horizontal and vertical edges on the pixel grid
Squashed from #6169 onto main.
2026-11-09 00:29:28 +07:00
dotCooCoo b568977af4 Asset Export: 3D objects export as USDA, USDZ, glTF or OBJ meshes
Squashed from #6168 onto main.
2026-11-09 00:29:05 +07:00