Commit Graph
437 Commits
Author SHA1 Message Date
BFlat bae885e393 Merge pull request #6876 from dotCooCoo/feat/outline-object-image-alpha
Effects: Outline Object on an image's fill or stroke follows its opaque pixels
2026-11-13 12:27:50 +07:00
BFlat 62aee53c50 Merge pull request #7025 from dotCooCoo/fix/followup-6236
Tests: hidden layer children and edited freeform gradients under the Warp effects
2026-11-13 11:27:02 +07:00
BFlat be389b8b59 Merge pull request #7030 from dotCooCoo/fix/followup-6960
3D: mapped graphics keep paint order and hide handles on hidden fills
2026-11-13 11:26:46 +07:00
dotCooCoo ac76040014 Merge main into feat/outline-object-image-alpha 2026-11-12 20:16:33 -08:00
BFlat e899f3fdb4 Merge pull request #6881 from dotCooCoo/feat/image-color-mask
Image Tools: Color Mask hides chosen colors of an image without changing its pixels
2026-11-13 10:27:04 +07:00
dotCooCoo 3e9e58afb1 3D: mapped graphics keep paint order and hide handles on hidden fills
A solid now shares its mapped graphics only with the run of solids just
before it that have the same opacity and blending mode. Before, it
joined any earlier solid with the same settings, so with members A, B
and C where A and C match, A's graphic was drawn after C and over B.

The Materials tab shows no artboard handles for graphics on a hidden
fill or stroke, which draws no solid and no graphics.

docs/three-d.md now says that pattern tiles in a graphic keep their own
colors instead of taking the face's light.
2026-11-12 18:59:02 -08:00
dotCooCoo aa113a9ac2 Tests: hidden layer children and edited freeform gradients under the Warp effects
A Warp on a layer whose hidden circle keeps its shape past the budget
moves the layer's placed gradient from the layer's bounds to the warped
square's bounds, as the art and inside the art. Edited freeform points
on a path, on a group inside the art and on the art itself move from the
bounds before the warp to the bounds after it.
2026-11-12 18:44:25 -08:00
BFlat 23cae99a84 Merge pull request #7011 from dotCooCoo/fix/expand-symbol-item-effects
Effects: a symbol instance's fill or stroke effects reshape its art and expand
2026-11-13 09:30:26 +07:00
BFlat 9c007ec416 Merge pull request #7009 from dotCooCoo/fix/3d-clip-image-outside-shape
3D: an image inside a clip's box but outside its shape is left out
2026-11-13 09:29:50 +07:00
dotCooCoo df155042d4 Merge main into feat/image-color-mask 2026-11-12 17:40:13 -08:00
BFlat ae014cc896 Merge pull request #7002 from dotCooCoo/fix/3d-plain-fill-keeps-compound
3D: a fill or stroke without the effect keeps an even-odd path's holes
2026-11-13 08:29:49 +07:00
dotCooCoo e51685dc23 Effects: a symbol instance's fill or stroke effects reshape its art and expand
A geometry effect on a symbol instance's topmost fill now reshapes the
filled shapes of the symbol's art, and one on its topmost stroke reshapes
the stroked shapes, keeping their colors, strokes and other detail. Only
a visible topmost fill or stroke with an opacity above 0 counts. Clipping
paths, guides and opacity-mask art keep their shapes; a group's own fills
and strokes in the art take the effects; a nested instance passes them on
to its own art, except one whose symbol holds the outer symbol.

The canvas, Use Preview Bounds and the drawn bounds, SVG and PDF export
and Object > Expand Appearance use the same art. Expand Appearance is
enabled for such an instance and turns it into an editable group in one
undo step. Bounds measure the art with a dynamic symbol's overrides, as
the canvas draws it.

Existing documents with a geometry effect on a symbol instance's fill or
stroke draw differently: before, the effect drew nothing.
2026-11-12 17:15:04 -08:00
dotCooCoo c5f406ffab 3D: an image inside a clip's box but outside its shape is left out
A 3D effect left an image in a clipping group out only when the image's
box missed the clipping path's box. An image in the corner of a round
clip's box, a turned image whose box reaches a clip but whose frame does
not, an image in an even-odd hole, or one inside each of two nested clips
but outside the area they show together was refused with the Image Trace
message, although it draws nothing.

The image's frame, the pixel rectangle as its transform places it, is now
cut to every clipping path around it in turn, each with its own fill rule,
and the image is left out when nothing is left. An image the clips show
any part of, a strip narrower than a hundredth of a point included, is
still refused. One effect's art cuts image frames at most 1,024 times;
past that, or when a cut fails, the art is refused.
2026-11-12 17:07:13 -08:00
BFlat 23825d2943 Merge pull request #6993 from dotCooCoo/fix/revolve-plugin-item-context
Effects: a fill's 3D Revolve tells the plug-in effect before it which fill it sits on
2026-11-13 07:48:14 +07:00
dotCooCoo 9b65bf2708 3D: a fill or stroke without the effect keeps an even-odd path's holes
With a 3D effect on one fill or stroke, the object's other fills and
strokes were drawn as nonzero paths, so an even-odd compound path or
path filled its holes in them. Extrude & Bevel, Inflate, Rotate,
Revolve and their Classic kinds now draw those items with the source's
fill rule, through the group code's shape_node helper.
2026-11-12 16:28:54 -08:00
dotCooCoo 3570efa0a9 Effects: a fill's 3D Revolve tells the plug-in effect before it which fill it sits on
The per-item Revolve loop passed a copy of each fill or stroke to the
geometry context, which finds the item by identity in the object's
appearance, so a plug-in effect before the Revolve was told it sat on
the object. Each copy now keeps its original item beside it.
2026-11-12 15:48:59 -08:00
dotCooCoo d5a02ee872 Merge main into feat/3d-graphics-planar 2026-11-12 15:19:09 -08:00
BFlat 1461bb2123 Merge pull request #6963 from dotCooCoo/fix/transform-effect-moves-member-raster-effects
Effects: a group's or object's Transform effect moves the raster effects inside it
2026-11-13 06:11:24 +07:00
dotCooCoo 505d11dae5 3D and Materials: straight graphic sides count once, and graphic handles stay with the drawn art
- Under a perspective, the shared 400,000-element limit of a 3D effect's graphics now counts
  what each path draws once mapped: a straight side stays one element, and only curves count
  as their 8 pieces. Two copies of a 30,000-sided polygon now both draw. The symbol check
  counts the same way, and without a perspective it counts each curve once.
- The Materials tab's handles on the artboard are hidden while a geometry effect (such as
  Transform) comes after the 3D effect of a selected object, since that effect draws the
  graphics away from the handles.
- A press on the canvas while its context menu is open only closes the menu, and no longer
  grabs a graphic's handle.
- A symbol instance inside a graphic's art shows its changes to a dynamic symbol, and a symbol
  with 9-slice guides is placed through them, as the canvas draws the instance. Paths that
  cross a guide are cut at it.
2026-11-12 15:02:34 -08:00
BFlat 822d4101b5 Merge pull request #6890 from dotCooCoo/feat/3d-expand-as-wireframes
3D and Materials: Expand as Wireframes turns a 3D object into its edges as paths
2026-11-13 05:23:34 +07:00
BFlat e0a8fdae5b Merge pull request #6958 from dotCooCoo/fix/bevel-join-visual-bounds
Transform: a bevel stops at the corners of the segments it joins with Use Preview Bounds
2026-11-13 05:23:09 +07:00
BFlat c2f97d2efa Merge pull request #6961 from dotCooCoo/fix/gradient-repeat-runs
Gradients: a repeated gradient with many stops keeps every run
2026-11-13 05:22:59 +07:00
dotCooCoo cafbaec053 Merge main into fix/transform-effect-moves-member-raster-effects 2026-11-12 14:02:27 -08:00
dotCooCoo 554d86765a Merge main into feat/3d-graphics-planar 2026-11-12 13:46:05 -08:00
dotCooCoo 367ec9998e 3D and Materials: graphic curves whose control points pass behind the eye still draw
Under a perspective, a piece of a graphic's curve that lies in front of the eye while one of its control points does not is halved until its control points map, instead of dropping the piece. A curve that dips behind the eye between the ends of its pieces is still clipped. The extra pieces count toward the effect's element limit, and a path that passes the limit is not sent to the clipping.
2026-11-12 13:32:37 -08:00
dotCooCoo 312d4a3914 Merge main into feat/image-color-mask 2026-11-12 13:30:24 -08:00
BFlat 3434e46240 Merge pull request #6825 from dotCooCoo/feat/custom-arrowheads
Stroke: custom arrowheads from an arrowheads file join the arrowhead menus
2026-11-13 04:25:55 +07:00
dotCooCoo 950494802e Merge main into feat/outline-object-image-alpha
# Conflicts:
#	crates/effects/src/tests.rs
2026-11-12 12:45:45 -08:00
dotCooCoo c31af9898e 3D and Materials: one element limit per effect, pasted graphics keep their symbols, safer handle drags
- The graphics of one 3D effect share one limit on drawn path elements
  across every member and opacity group, so a group effect with many
  members no longer multiplies the limit.
- Copy and paste follow the symbols that mapped graphics name, in object
  and graphic style appearances: a document without the symbol gets it,
  and one whose symbol of the same name is other art gets the copied
  symbol under a free name, which the graphic then names.
- A face's inverse map refuses the points its page map refuses, so a
  handle drag cannot save an offset whose handle is not shown.
- A press on a graphic's handle with Space held, or with the Hand, Zoom
  or Rotate View tool, pans, zooms or turns the view.
- A handle drag ends, keeping its preview, when the selection or the
  document changes while the pointer is down.
2026-11-12 12:42:59 -08:00
BFlat 87485971b0 Merge pull request #6959 from dotCooCoo/fix/item-transform-later-raster-effects
Effects: a fill's or stroke's own Transform effect leaves the effects listed after it unscaled
2026-11-13 03:28:16 +07:00
dotCooCoo bd973548d7 Effects: an object's Transform effect moves its fills' raster effects beside a fill's 3D effect
When only a fill or stroke has an Extrude & Bevel, Inflate, Rotate or Revolve effect, the object's
Transform effects reshape the path, and now the raster effects of every fill and stroke follow them
too, as they do on an object without a 3D effect: a shadow after the fill's 3D effect and another
fill's shadow scale and turn with the art.
2026-11-12 12:21:38 -08:00
dotCooCoo 197d033b4f Merge main into feat/custom-arrowheads 2026-11-12 12:04:01 -08:00
BFlat 86f8a6dcbd Merge pull request #6927 from dotCooCoo/fix/roughen-size-zero
Effects: Roughen at size 0 keeps the outline
2026-11-13 02:46:39 +07:00
dotCooCoo 043e517ef6 Effects: a group's or object's Transform effect moves the raster effects inside it
A Transform effect on a group reshaped its members' paths but left the
raster effects inside it at their own values: a member's Drop Shadow
kept its 10 pt offset under a 200% Transform effect, and with Scale
Strokes & Effects on, it scaled by the mean scale without turning. The
same held for the raster effects of an object's own fills and strokes
under the object's Transform effect.

- A Transform effect that moves the art once now transforms the raster
  effects of a group's members (their own and their fills' and
  strokes'), of nested groups, of the art of the linked opacity masks
  in it and of an object's fills and strokes, after the members' own
  Transform effects: sizes scale by the mean scale and a Drop Shadow's
  offset turns, reflects and scales. A raster effect that a member's
  own Transform effect with copies or random holds back keeps its value
  through that effect and still transforms with the group's. Scale
  Strokes & Effects does not scale them a second time. A group's or
  object's Transform effect that makes copies or moves at random leaves
  them as before.
- The canvas, the redraw region, export, Expand Appearance, Object ›
  Expand and Outline Stroke read the same values.
- The Transform effect's params text and docs/target-app-parity.md
  describe the rule.
2026-11-12 11:35:03 -08:00
dotCooCoo 0691e1acbe Gradients: keep every run of a repeated gradient with many stops
When a repeated or reflected gradient needs more stops than a limit
allows, each run now leaves out the stops that change its colors
least, so every run is still written, instead of writing fewer runs
and padding the rest with the last color. Each run keeps its ends,
sharp steps, swatch-linked stops, tint and CMYK ink changes and spans
shaped by an off-center midpoint, and the colors are compared as the
gradient's method mixes them. This applies to gradients along or across
a stroke, CSS conic gradients, and PDF, EPS and .ai export.

- Outline Stroke's mesh fits the gradient to at most 1,024 stops, and
  splits a stroke into several meshes when one would hold more than
  2^20 points.
- Object > Expand leaves a gradient as it is when the box needs more
  than 256 runs, instead of expanding only the first runs.
- CSS writes the stop positions of repeating gradients with four
  decimals, so the period stays in place across the box.
- A swatch chip of a gradient that repeats gets at least four grid
  cells a run along each side (at most one a point, and 128), so it
  does not show one color when the count matches the chip's cells.
- docs/mcp.md states the stop limits and the PDF import sample budget.
2026-11-12 11:30:44 -08:00
dotCooCoo 5f7d4d3922 3D and Materials: graphics take the opacity mask of a cap in a transparent gradient
Under a colored light a surface in a gradient with transparent stops carries an
opacity mask. A graphic drawn on such a cap now takes that mask, and each such
solid gets a graphic of its own, so a graphic is hidden where its cap is.
2026-11-12 11:27:38 -08:00
dotCooCoo be50e5328b Transform: a bevel stops at the corners of the segments it joins with Use Preview Bounds
A bevel join, and a miter join whose miter limit bevels a corner, end the
stroke at the outer corners of the two segments they join. The visual box
measured such a corner as if the stroke reached half its weight round it:
a 40 x 100 pt triangle with a 20 pt stroke and a miter limit of 2 measured
10 pt above its tip, where the stroke paints about 1.96 pt above it. Use
Preview Bounds, and the alignment and sizing that use it, took that box.

- With bevel and miter joins, closed paths and open paths with round or
  projecting caps are now measured along their segments, as butt-capped
  open paths already were, with the miter tips the limit keeps and the
  caps added. Round joins still reach half the weight round each corner,
  the round caps of dashes, which can end at a corner, reach half the
  weight all round, and the projecting caps of dashes reach the diagonal
  of their square. The visual box of
  such a stroke can be smaller than before and is never larger. A path
  with no width, such as a rectangle sized to 0 pt wide, turns back at
  its ends, where the join bevels, so its box no longer reaches past them.
- object.setBounds with Use Preview Bounds measures an object as drawn
  when a beveled or mitered corner, or a butt cap, is on a segment at a
  slant. Before, a closed path with miter spikes the limit keeps missed
  the size typed when the width and height were out of proportion.
- A click past a bevel or a butt cap, by more than the click tolerance,
  no longer selects the stroke. Strokes with a width profile or with
  dashes that have round or projecting caps, and the ends of curves that
  bend more tightly than half the weight, keep the round reach.
- Paths measured along their segments skip the segments that cannot
  reach past the box measured so far, which starts with the path's own
  bounds.
2026-11-12 11:04:25 -08:00
dotCooCoo d46d8bb1fe Merge main into feat/3d-graphics-planar 2026-11-12 11:00:58 -08:00
dotCooCoo 85b3740cca Effects: a fill's or stroke's own Transform effect leaves the effects listed after it unscaled
Effects apply in the order they are listed, so an effect listed after a
Transform effect applies to the art the Transform effect made, at its
own values, as an object's later effects do after the object's
Transform effect. With Scale Strokes & Effects on, a fill's or stroke's
own Transform effect scaled the effects listed after it in that fill's
or stroke's list by the mean scale: a 300% Transform turned a 5 pt
Offset Path after it into a 15 pt one, and a Drop Shadow after it took
three times its offset. With Transform Patterns or Scale Strokes &
Effects on, an SVG filter after it also had the origin of its user space
moved.

- The effects after such a Transform effect now keep their values, on
  the transformed art and on each copy, on every fill and stroke a
  live-effect plug-in paints the piece with, and when the copies are
  counted against the effect budget.
- With Transform Objects off, the effects before it keep their values
  too.
- The Transform params text, docs/target-app-parity.md and docs/gaps.md
  describe the rule, and drop the note that listed an object's later
  effects keeping their distances as a shortfall.
2026-11-12 10:59:21 -08:00
dotCooCoo c3f7357ec2 3D and Materials: map graphics onto the planar faces of 3D objects
The Materials tab of the 3D and Materials panel lists the document's
graphics (symbols). Add as Single Graphic keeps the selected art as a
new symbol (threeD.addGraphic), and clicking a thumbnail maps it onto
the front of each selected object's 3D effect (threeD.mapGraphic) in
one undo step. The tab lists the mapped graphics with Face, X, Y, Scale
and Rotation and Remove Graphic, and a handle on the artboard drags a
graphic over its face.

The graphics are the new `graphics` parameter of threeD.extrude and
threeD.rotate: drawn on Extrude & Bevel's front and back caps and on
the Plane type, turned, foreshortened, twisted and tapered with the
face, shaded with its light and clipped to it. Symbol renames follow
into the graphics, and Select All Unused counts a symbol a graphic uses.

Docs: docs/mcp.md, docs/three-d.md, docs/effects-parity.md.
2026-11-12 10:31:37 -08:00
dotCooCoo f4e1db029e Merge main into feat/outline-object-image-alpha 2026-11-12 10:12:18 -08:00
dotCooCoo 70a4c44033 Effects: every fill a painted effect returns follows the Transform effect
A painted live effect on a fill can return a piece with several fills,
each a copy of that fill with its effects. A Transform effect after it
moved the piece, but only the first returned fill's raster effects
followed it: with a Drop Shadow before the painted effect and a 200%
Transform effect after it, the first fill's shadow offset became 20 pt
and the second fill's stayed 10 pt. Each returned fill and stroke now
takes the followed values.
2026-11-12 10:08:53 -08:00
dotCooCoo 00e07698b1 Image Tools: a live color adjustment recolors an image whose masked pixels are missing
When the document does not hold a color mask's masked pixels, the image is
drawn from its own pixels. A live color adjustment now recolors those pixels
and drops the mask on the recolored copy, instead of pointing the image at
the missing masked pixels, which drew nothing and left Expand Appearance with
an image whose pixels were not in the document.
2026-11-12 09:59:11 -08:00
dotCooCoo 556123e691 3D and Materials: Expand as Wireframes keeps a layer's path guides and cuts Revolve's art for Revolve
A layer expanded to its edges keeps the paths made into guides inside it, as it keeps its ruler
guides. A clipping group with Revolve and Rotate is cut for Revolve, the effect the canvas draws,
so its fills go in one piece and art with more fills than a 3D effect takes still expands.
2026-11-12 09:47:53 -08:00
bflatastic cfce336215 Dashes fitted to corners take a subpath with no segments
An HP-GL wedge of almost no radius drawn with an adaptive line type
(LT-1;EW0.000001,0,0;) leaves a lone moveto. Fitting the dashes to its
corners indexed the empty segment list and panicked while rendering. Such
a subpath now uses the exact plan, which has no runs to fit.

Found by import_fuzz::random_hpgl_instructions_never_panic.
2026-11-13 00:47:30 +07:00
dotCooCoo fe34c2b818 Merge main into feat/custom-arrowheads 2026-11-12 09:23:20 -08:00
dotCooCoo 87d1f0b3d7 Merge main into fix/transform-effect-scales-raster-effects 2026-11-12 09:23:19 -08:00
dotCooCoo 8d27e53115 Merge main into feat/image-color-mask 2026-11-12 08:56:33 -08:00
dotCooCoo 91e1412d77 Effects: Roughen at size 0 keeps the outline
With Size 0, Roughen moved no point but still replaced each segment
with its Detail's points joined by straight lines (Corner) or by a
smoothed curve through them (Smooth), so a curved path lost its shape.

At Size 0 each segment is now split at the Detail spacing into pieces
of equal length on the segment itself, with `pathtools::insert_at`: a
straight segment gets corner points without handles, and a curve gets
smooth points whose handles follow it. The path keeps its outline with
Smooth or Corner points alike. The params text of `distort.roughen`
describes size 0.

`insert_at` now counts the anchors added before each returned mark in
one pass. It counted them again for every split segment, which took
quadratic time on a path with many split segments. Its results are
unchanged.

Existing documents with a Roughen of Size 0 on a curved path, or with
Smooth points, draw differently.

This is VectorCraft's own rule. What Roughen does is described in
https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/distort-and-transform-effects-in-illustrator.html
2026-11-12 08:55:07 -08:00
BFlat cc6509827d Merge pull request #6878 from dotCooCoo/fix/opacity-mask-follows-reshape
Opacity masks: linked masks follow group effects, the Free Transform distort and envelopes
2026-11-12 23:10:24 +07:00