Commit Graph
3162 Commits
Author SHA1 Message Date
dotCooCoo 16da351e5d Merge main into feat/script-run 2026-11-09 19:29:33 -08:00
BFlat 4dbaa10e0a Merge pull request #6365 from dotCooCoo/fix/issue-3617
Revolve: type, groups and layers revolve, and the profile and face limits are higher
2026-11-10 10:23:52 +07:00
BFlat ef81d550e4 Merge pull request #6371 from dotCooCoo/fix/issue-6361
Part of #6361: Cut takes direct-selected anchors out of the path
2026-11-10 10:07:20 +07:00
BFlat 59cecd3d28 Merge pull request #6381 from dotCooCoo/fix/mcp-schema-keywords
MCP: docs list every JSON Schema keyword in a command's schema
2026-11-10 09:59:14 +07:00
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
BFlat 57f596126f Merge pull request #6379 from jmverges/fix/ai-separation-rasters
.ai import: images in a spot ink's Separation space keep their ink (#6350)
2026-11-10 09:46:21 +07:00
dotCooCoo dd55adb701 Merge main into feat/script-run 2026-11-09 18:39:42 -08:00
dotCooCoo 2c0d09e079 Scripts: File › Scripts › Other Script… runs JavaScript files
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.
2026-11-09 18:35:41 -08:00
dotCooCoo 1847f40fbe Docs: Cut's progress line says anchors left without a segment go too 2026-11-09 18:28:16 -08:00
BFlat 008595aa69 Merge pull request #6375 from dotCooCoo/fix/font-wheel-panel-scroll
Font menu: a panel scrolled past a closed font field keeps scrolling
2026-11-10 09:21:14 +07:00
dotCooCoo db56690f48 MCP: docs list every JSON Schema keyword in a command's schema
The Parameter schemas section of docs/mcp.md now names every keyword
ParamSchema::to_json writes: $schema, description and deprecated at the
root, items for lists, prefixItems, items: false and minItems for a
point, anyOf and minItems for a color, and description, default and
deprecated on a parameter.
2026-11-09 18:19:24 -08:00
dotCooCoo c770e3bfea Revolve: mesh export cuts an edge where it crosses another contour
Mesh export decided whether an edge walls a hole at the edge's middle alone, so an edge that crosses another closed contour turned its whole length toward the hole, or none of it. Each sampled piece of a closed contour is now cut where it crosses another closed contour, and each part faces a hole only where it walls one.
2026-11-09 18:19:22 -08: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 ecb2fbe609 Revolve: the search for holes looks only at contours near each point
Mesh export tested the middle of every segment against every closed contour's bounds, so a profile of thousands of separate contours took hundreds of millions of comparisons before the work limit counted any. The closed contours now go in a grid over their bounds, a point is tested only against the contours of its cell, and placing a contour in a cell and each candidate looked at count toward the limit.
2026-11-09 18:12:27 -08:00
jmvergesandClaude Opus 5.5 0aeb8e8792 .ai import: images in a spot ink's Separation space keep their ink (#6350)
An image whose color space XC sets to one ink (n [c m y k /DeviceCMYK
(name) /Separation] XC) opened as a gray RGB image: its samples, the ink's
tints (255 at 100 %), were read as gray levels, so the tints drew inverted
and the ink was lost.

It now opens as a grayscale image of the tints, black at 100 %, colorized
by the ink at 100 %: its spot swatch, a process plate's CMYK color, or
Registration. Its alpha channel becomes its opacity mask, a reduced large
image's too (a colorized image reduced to fit the limits no longer keeps
its alpha in its pixels). 8-bit and 1-bit samples, binary or in hex
comment lines. A device space and the ink None change nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 02:07:53 +00:00
BFlat 51498975b4 Merge pull request #6368 from dotCooCoo/fix/measure-snap-to-glyph
Measure: both ends land on glyph guides with Snap to Glyph on
2026-11-10 08:58:14 +07:00
dotCooCoo 965471d0da Docs: Cut's progress line states the segment rule for mixed selections 2026-11-09 17:57:48 -08:00
BFlat 27a64bf090 Merge pull request #6363 from dotCooCoo/fix/issue-6316
Fix #6316: a Transform effect's copies of a group are whole groups below the original
2026-11-10 08:53:49 +07:00
dotCooCoo cddebdf7f6 3D: a group's fill or stroke takes no 3D effect
Revolve, Extrude & Bevel, Inflate and Rotate on a group or layer draw only as the group's own effect. On the group's fill or stroke they drew nothing, yet effect.apply and effect.setParams accepted them. Both now refuse that placement.
2026-11-09 17:51:22 -08:00
dotCooCoo 32ddc21045 Merge main into fix/issue-3617 2026-11-09 17:39:37 -08:00
BFlat 15f34f5083 Merge pull request #6357 from dotCooCoo/fix/pathops-vc-unite-sweeps-twice
Path ops: Unite sweeps a single path once when its own sweep leaves its segments as they are
2026-11-10 08:35:40 +07:00
dotCooCoo 138bdd77e4 Font menu: a panel scrolled past a closed font field keeps scrolling
Since #5331, a closed font family field in Character, Properties or the
Control bar took every plain vertical wheel turn while the pointer was
over it. When a panel scrolled the field under a still pointer, the
field took the rest of the turns: the panel stopped scrolling and each
turn changed the selected type's font.

The field now takes the wheel only when the pointer was moved onto it
after the field last moved, and the wheel stream began there. A stream
that began anywhere else, a sideways scroll included, stays there until
0.5 s pass with no wheel input. A stream that leaves its field does not
return to it. It leaves when the pointer moves off the field or out of
the window, when the field moves or misses a layout pass, and when a
turn arrives while an overlay covers the field. Trackpad momentum stays
with the stream it follows and never starts one on a field. The rule and
its 0.5 s are VectorCraft's own.

Wheel turns the field does not take stay in the input in their order.
Moving the pointer onto the field and turning the wheel still steps the
font, as #5253 asks, in floating panel windows too. When the pointer was
moved onto the field while a window or overlay covered it, the pointer
has to move again after the cover goes away before the wheel steps the
font.
2026-11-09 17:32:58 -08:00
dotCooCoo bb142cc063 Revolve: the fills and strokes of one object share the paint-copy budget
Each fill or stroke with its own Revolve counted the copies of its paint from zero, so an object with several of them could draw past the 256 MiB budget. They now share one count, and past it the source path draws, as for the members of a group.

effect.apply and effect.setParams on a fill or stroke now check the Revolve in that item's paint, after the item's geometry effects before it, together with the object's other items that carry a Revolve. Before, they checked the object's top fill.

docs/revolve.md marks the axis position, type handling and member paint as VectorCraft's own rules, and docs/mcp.md says how the budget is counted.
2026-11-09 17:19:13 -08:00
dotCooCoo 8d4ef8a202 Merge main into fix/issue-6361 2026-11-09 17:03:53 -08:00
dotCooCoo cb086ef1b7 Part of #6361: Cut takes direct-selected anchors out of the path
With anchors picked with Direct Selection, Edit > Cut removed only the
segments between them, so the picked anchors stayed in the path. Cut now
removes picked anchors and the segments attached to them from a path or
a compound path's member, as Clear does. Picked segments still lose only
those segments. When a path has picked segments, Copy and Cut leave out
anchors picked on its other contours, as Clear does.

Docs: docs/target-app-parity.md.
2026-11-09 17:03:43 -08:00
dotCooCoo f91f16d993 Docs: list the groups whose Transform copies stay per member
A clipping group, a group with a fill or stroke of its own and a group
whose type is kept as type still copy each member in place, and a
group's copies leave out its guides.
2026-11-09 17:01:16 -08:00
dotCooCoo 28fc436ef3 Measure: both ends land on glyph guides with Snap to Glyph on
The Measure and Measure & Inspect tools ignored View › Snap to Glyph:
their press and end points snapped only to targets that hold no glyph
guides.

With Snap to Glyph, Smart Guides and Alignment Guides on, and Snap to
Grid and Snap to Pixel off, a drag's press and end land within the
snapping tolerance on the guides of live type that the Character
panel's options allow, except Anchor Point. A Shift-drag's end slides
along its 45° step to them, and the guide shows during the drag. With
Snap to Glyph off, the tools snap to the same targets as before and
show no guide. The targets are kept while the document, the window and
the snapping options stay the same, so a drag lays out the type once.
2026-11-09 16:59:54 -08:00
BFlat bca98461c4 Merge pull request #6366 from dotCooCoo/fix/issue-5632
Part of #5632: Convert to Grayscale stores an image with one gray channel
2026-11-10 07:50:43 +07:00
BFlat c7c6c12ca8 Merge pull request #6364 from dotCooCoo/feat/command-param-schemas
Commands: the View commands check their params against machine-readable schemas
2026-11-10 07:42:57 +07:00
dotCooCoo 35db931810 Fix #6316: a group copies each member in place when a member makes more art
A group with a visible member that carries a geometry, Pathfinder or 3D effect, also in an opacity mask, keeps its previous copies. Copies stop at the budget with what each copy's own effects make, and a group's copies count the art of opacity masks.

The PDF and SVG tests for a group's image copies now expect the original on top.
2026-11-09 16:24:13 -08:00
dotCooCoo 4060f14ce0 Path ops: Unite sweeps a single path again when two segments lie within twice the sweep tolerance
The second sweep of `unite_all` joins contours that lie within the sweep
tolerance of each other, and the first sweep can leave such contours
apart. `solid::tests::meets_within_twice_the_boolean_tolerance` needs
that join. A single input now skips the second sweep only when no two
of its segments lie within twice the tolerance. The check compares at
most 512 pairs per segment and runs the second sweep past that.

`unite_parts` tests the single-input case before it builds the combined
path, and it takes `parts` and `inputs` by value so that both are freed
before the second sweep.
2026-11-09 16:19:01 -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
dotCooCoo b07c808093 Revolve: type, groups and layers revolve, and the profile and face limits are higher
Type sweeps as its glyph outlines and stays editable. On a group or layer every visible member sweeps about one axis on the edge of the members' bounds together, each in its own paint.

A surface now takes up to 16,384 profile points and 163,840 faces. Curves take fewer samples, down to 4, when a surface passes those limits, and the command reports the failure below that, after the geometry effects before the Revolve and with the cumulative size of its paint copies counted. Mesh export turns the walls of holes toward them. Expand Appearance works on type with Extrude as well as Revolve.

Fixes #3617
2026-11-09 16:09:03 -08:00
dotCooCoo cf8b6cb3b8 Docs: the params text equals the text the schema writes
The params literals are kept in the source, and a test checks that each equals the text its schema writes.
2026-11-09 16:06:46 -08:00
Drew Dibble b242f5acf5 Fit to Artwork Bounds: a change in place is measured again; the fix's evidence names EPS export
A new test changes the document in place (Arc::make_mut, with no other
holder and no change of revision) and checks the answer is measured
again: keyed on the document's address alone, it would keep the old
answer. The kept answer's doc says what it holds after the measure,
and that the fonts set the bounds of all type. Should measuring ever
ask again while it runs, it is measured without being kept, rather
than borrowed twice.

The stack overflow fixed in d5d04f868 also hit EPS export, and the
parity rows now say so. Two test docs are corrected: the fill-only
clipping is an older bug on main, and the outline-view difference runs
both ways on a few dozen hairline pixels.
2026-11-09 16:50:36 -07:00
Drew Dibble a9a061c8a9 Merge main into type-3d-redraw
# Conflicts:
#	docs/target-app-parity.md
2026-11-09 16:36:25 -07:00
dotCooCoo 27c9fc8dd2 Merge main into feat/command-param-schemas 2026-11-09 15:16:23 -08:00
BFlat 25cfa9d3cf Merge pull request #6355 from dotCooCoo/fix/issue-6125
Fix #6125: raster exports keep a Screen glow on the bare page
2026-11-10 06:05:32 +07:00
dotCooCoo d186d73c42 Commands: a schema with no required parameter allows null params
The engine reads null params as {}, but engine.commands listed every
schema with the root type "object", so a client that validated a call
against the schema refused one the engine accepts. A schema with no
required parameter now has the root type ["object", "null"]. A schema
with a required parameter keeps "object", because the engine refuses
null params for it as a missing parameter. Nested objects are unchanged.
2026-11-09 14:56:42 -08:00
dotCooCoo 93f75def04 Merge main into feat/command-param-schemas 2026-11-09 14:38:20 -08:00
BFlat 8591637999 Merge pull request #6359 from ruchirguitar/g2-effect-gallery-preview
G2: Effect Gallery thumbnails and an in-dialog preview
2026-11-10 05:33:33 +07:00
BFlat 39c1f0283f Merge pull request #6360 from dotCooCoo/fix/touchpad-ctrl-zoom
Canvas: Cmd/Ctrl-wheel input finer than a whole notch zooms; only whole notches scroll sideways
2026-11-10 05:28:23 +07:00
dotCooCoo 59304f9964 Fix #6316: a Transform effect's copies of a group are whole groups below the original
A Transform effect with copies on a group copied each member on its own,
so one member's copies lay over the fills of the next member. Each copy
is now a whole group, the original on top and each copy below the one
before. A path's copies follow the same order. Guides are not copied,
and the copies stop at the node limit. A group with its own fill or
stroke, a clipping group and live type keep their previous copies.
2026-11-09 14:27:25 -08:00
dotCooCoo 2e6ba25598 Commands: parameter schemas cover every route, bound hostile values and translate their errors
Check a new call's params against the schema in execute_spec, after the
key check and before the parser, and check every command.batch step
before the first one runs. menucmds::run_raw and the two other direct
runs go through the same check. Saved steps keep keys the schema does
not name and have the values it names checked.

Limit nesting to 16 levels, cut error text and paths to a fixed length,
and copy nothing before it has passed. Treat a whole number written
with a fraction as an integer. Check declared defaults and duplicate
names once at first use.

Give the five schema errors rows in the 22 complete-message catalogs.
2026-11-09 14:19:22 -08:00
Drew Dibble 59cc303912 Canvas redraw: the stack overflow fix's evidence, and two tests' known differences
- The parity rows say what the fix covers: a 3D effect on a fill or
  stroke that builds nothing overflowed the stack with no effect of the
  object's own too, on the canvas, in Object › Rasterize and in raster
  exports.
- The builds-nothing test says the fill-only difference (a raster
  effect clipped to the path, cutting Roughen's spikes, when drawn
  without the 3D effect) is an older bug, not the design.
- The traced-shape test says why its area is bounded rather than drawn
  again: a partial render in outline view differs from the whole by one
  in alpha on 37 pixels, older than the test and unrelated to the edit.
- A doc comment rewrapped.
2026-11-09 15:13:42 -07:00
dotCooCoo 5eb6b62632 Flatten: blend modes do not read the white under the art
Flatten Transparency composited each region over white, so a blend mode
on the bare page read that white. Raster renders now draw their
background under the finished art, and the flattened color no longer
matched them: a Color Burn object on the bare page flattened to white
and rendered in its own color. The region now composites the art alone
and puts the white under it.

- Tests that relied on the white under the art now draw art under the
  style (`every_builtin_style_paints_visibly_on_a_rectangle`) or compare
  only whole-pixel pans with a fresh render
  (`a_coarser_glow_kept_whole_draws_after_a_pan_as_made_again`).
- New tests cover every blend mode on white and blue backgrounds, a
  thumbnail and the ink planes with a Screen glow, and
  `over_background` at background and pixel alpha 0, 128 and 255.
2026-11-09 14:13:08 -08:00
dotCooCoo 39d538dadf Canvas: Cmd/Ctrl-wheel input finer than a whole notch zooms
With Scroll Sideways with Cmd/Ctrl + Wheel on, wheel() turned all
Cmd/Ctrl wheel motion into a sideways scroll unless it arrived as a
pinch event. A wheel turn now zooms about the pointer once it holds an
event from a trackpad source, point deltas from an unnamed source in the
desktop app, or line deltas that are not whole notches. Whole notches
keep scrolling sideways, and the option's default is unchanged. The same
rule applies with Zoom with Mouse Wheel on. egui-winit reports a wheel
source only on macOS, so the delta decides elsewhere.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-09 13:48:07 -08:00
ruchirguitarandCursor e1b73f38bc G2: Effect Gallery thumbnails and an in-dialog preview
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-10 03:12:54 +05:30
dotCooCoo 1796c77765 Merge main into feat/command-param-schemas 2026-11-09 13:31:22 -08:00
dotCooCoo 10909656d9 Path ops: Unite sweeps a single path once when its own sweep leaves its segments as they are
`unite_all` and `try_unite_all` swept each input under its own fill rule
and then swept all the resulting contours again under NonZero. With one
input whose sweep split and dropped none of its segments, the second
sweep ran over the same simple contours and doubled the time. Such an
input is now refit from the first sweep's contours. Inputs that cross
themselves and unions of several inputs are still swept again, and the
input paths are converted once instead of twice.
2026-11-09 13:20:20 -08:00