A document a script opens now counts the art in its graph designs and in
the original of the pattern being edited toward the growth allowance.
`Document::art_roots` and `art_roots_mut` list every tree of a document's
art; `any_node`, `edit_nodes_where` and the script's object count use them.
`script.run` checks the depth of `args` without recursion before it copies
or writes them out, so `args` nested 10,000 levels deep is refused with the
place it went past 64 levels instead of overflowing the stack.
New tests: graph-design art and the growth allowance, `args` nested 10,000
levels deep from a script (a RangeError) and from a caller, `run --cmd
script.run` on the command line with a report and with a thrown error, and
a property test that sends arbitrary source through `script.run`.
With Measure Type by Glyph Bounds on, point type is measured by its
glyph outlines and its underline and strikethrough bars, as the canvas
draws them. A typed W or H gives what is drawn that size, and underlined
or struck-through spaces are measured by their bars instead of the em
box.
The texts now say that each point type object selected as an object
(not inside a selected group) is measured this way, that moved type
snaps by its ink box, and which Arrange Tools commands follow the
option: Swap Objects, Replace with Top Object, Set Size of Each and
Sort Objects. Arrange in Grid and Group Overlapping Objects keep
measuring visual bounds.
Measure Type by Glyph Bounds (`typeGlyphBounds`, off by default) is in
Preferences > Type > Glyph Bounds and the Transform panel menu. With it
on, the bounding box, the Transform, Control bar and Properties X, Y, W
and H, `object.setBounds`, the transform tools' reference point and the
Arrange commands measure point type selected on its own by the ink of
its glyphs instead of its em box. A typed W or H scales the live type
until its ink has that size.
- The engine caches each type object's ink in text space with its
layout bounds; the cache is not saved.
- Use Preview Bounds adds the characters' strokes, and type with
geometry effects is measured as drawn. Skewed type is measured by its
outlines.
- Type that draws nothing, area type, type on a path and type inside a
selected group are measured as before. Align and Distribute keep
following Align to Glyph Bounds.
Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md,
object.setBounds params text.
With Use Preview Bounds on, a curve piece that bends more tightly than
half the stroke weight after the last split had its box grown by half
the weight on every side. A handle on an end point makes the curve's
first or last control difference zero, so such a curve always reached
that fallback and measured past its butt end: a vertical 2 pt curve
from (50,10) to (50,60) with its first handle on (50,10) measured
Rect(49, 9, 51, 60) instead of Rect(49, 10, 51, 60).
The fallback now measures the piece from the directions its tangent can
take, the nonzero control differences, when they all lie within a
quarter turn of their sum. Pieces whose tangent turns further are still
grown on every side.
File › Scripts › Other Script… picks a .js or .jsx file, shows a warning
that can be turned off in Preferences › General, and runs the script
against the open documents through the `vc` object. The new
`script.run` and `script.stop` commands run and stop a script over MCP.
- A run is one transaction. Each document it changed gets one undo step
named Run Script. An uncaught error, a limit or a stop puts every
document, the paint, the clipboard, menu state and preferences back.
- A command that fails inside a run, or whose result is too large to
reach the script, is undone and throws an error the script can catch.
- Files a command wrote stay on disk and the report names them, also
when the run is stopped from the quit question.
- While a script runs, other commands that change anything fail with a
busy error; MCP reports it as JSON-RPC error -32003.
- In the app the run advances one slice per frame with a Stop button in
the status bar. A run that adds a great many objects asks whether to
continue; Stop and Escape end it.
- Commands that change state outside the documents, such as prefs.*,
plugin.* and file.place.queue, are refused to scripts.
Docs: docs/mcp.md, docs/gaps.md, docs/roadmap.md,
docs/target-app-parity.md.
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.
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.
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.
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.
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.
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>
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>
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>
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>
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.
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.
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.