With a mixed selection and the Appearance panel's active row, paint.invert
and paint.complement recolor each object's row equal to the active one,
but remembered the color at the active row's index in the first object in
paint order, which can be another row. They now remember the row they
recolor there, as the gradient and freeform edits do.
The params text of paint.setFill, paint.setStroke, paint.invert,
paint.complement, paint.lastColor, paint.lastGradient, stroke.setAdvanced,
paint.editGradient, paint.setGradientGeom and the paint.freeform edits now
lists `aligned`. docs/mcp.md names paint.lastColor and paint.lastGradient,
which take the same rows as paint.setFill, and docs/target-app-parity.md
gets a line for the rows of a design's art.
With a graph design's art given first and an item without aligned, the
art is left out, but paint.invert and paint.complement remembered the
art's unchanged color as the last color. They now read the first object
they recolor.
paint.setFill, paint.setStroke, paint.invert and paint.complement with an
item, stroke.set, stroke.setAdvanced, paint.editGradient,
paint.setGradientGeom and the paint.freeform edits read an item given with
aligned: true and the art of a graph design in a series first as a row of
the art's stack, as appearance.setItem and transparency.set do. Each other
object edits the row equal to it and an object without one is left as it
is. A row the art does not have, a row of the other kind, or a row no
other object has is an error, also when the art is the only object given,
and the document and its undo history stay as they are. Without stroke,
the gradient commands edit the kind of the art's row.
The objects and ids the commands report, a gradient spanning the
selection, and the last color and last gradient count only the objects
whose row was edited.
With points of a gradient mesh picked (Mesh tool, Direct Selection or the
Lasso), a solid fill color from the Color panel recolors just those points,
but the Fill proxy, the Color panel, the Color Wheel and the Control bar's
fill chip kept showing the mesh object's own fill, so the panel's sliders
jumped back after each drag.
- The new `Session::fill_mesh_points` resolves the targets as
`paint.setFill` does: the picked points of every selected mesh, unless the
Appearance panel's active row is a fill or an object of a symbol instance
is selected. When the first selected object is a mesh among them, the Fill
proxy (`paint.proxies`) shows its first picked point's color, and "?"
compares the picked points' colors with each other and with the other
selected objects' fills.
- `Session::mesh_points_picked` lists the points `transparency.set` fades: it
is empty while the Appearance panel's active row takes the opacity, and
`transparency.info` and the Transparency panel now follow it there too.
- The cached "?" state and the Transparency panel's values are recomputed when
other points are picked, which gives the document no new revision.
- The Control bar's opacity shows the first picked point's opacity, which
`transparency.set` changes then.
- The `paint.setFill` and `transparency.set` params texts name the picked
points instead of the point last clicked, and the `paint.proxies` text,
docs/mcp.md and the progress doc describe what the panels show.
The Appearance and effect commands leave the art of a graph design in a
series out, and aligned rows were then matched against the first object
left over. With the art given first, appearance.removeItem with index 1
and aligned: true on art [red fill, blue fill] and a mark [blue fill,
stroke] removed the mark's stroke instead of its blue fill.
When the first object given is design art, rows and effects given with
aligned: true are now rows of the art's stack, and each other object
edits the row or effect equal to it. An object without one is left as
it is. This covers appearance.setItem, appearance.removeItem,
appearance.duplicateItem, transparency.set with an item, effect.apply,
appearance.addEffect and the effect commands that edit or remove an
effect. When no object has one, appearance.setItem and
appearance.removeItem change nothing, as they do for a row no object
has, and the other commands are an error. appearance.moveItem,
effect.move and appearance.duplicateItem with `to` compare the art's
stack too, and are an error when it differs from the other objects'.
The effect dialog reads the effects of such a row from the art's stack,
and passes the art on with the other objects.
docs/mcp.md states the rule.
The graph draws the art of a column or marker design in a series again
from the design when its data, type or designs change, and an edit made
to that art is lost.
- stroke.set, stroke.weightUp, stroke.weightDown, stroke.setAdvanced,
paint.editGradient, paint.setGradientGeom, object.setOverprint,
paint.swap, paint.default, and paint.invert and paint.complement with
an item leave the art out and edit the other objects given.
- transparency.set without an item, the paint.freeform.* commands that
edit points and lines, graphicStyle.breakLink and
effect.expandAppearance are an error when all their objects are
design art. Expand Appearance is disabled for a selection of only
design art, and it leaves the design art inside a selected graph or
series group as it was.
- When the first selected object is design art, the commands do not
use the Appearance panel's active row, which is a row of that art.
- The error names the command that ran.
docs/mcp.md lists the commands. The params texts of the appearance.*
commands and graphicStyle.apply name the rule, and the Graph column
designs entry in docs/target-app-parity.md states it.
Invert Colors, the Convert to commands, Saturate, Adjust Color Balance,
the Blend commands and Recolor Artwork recorded an undo step even when
they changed no color, for example Invert Colors with fills and strokes
off, Saturate at 0 or a second Blend Front to Back. The empty step also
cleared the redo steps. paint.invert and paint.complement on whole
objects, Apply as Filter (addon.color.filter) and Apply Nearest Swatch
run the same pass and did the same.
These commands now run on a copy of the document and record the step
only when something changed. Convert to Indexed Color uses the same
helper. Recolor Artwork also compares the documents when it counted no
recolored paint, so rewriting a color group's swatches still records
its step.
A recolor of a graph (Invert and Complement on the proxy, the Edit Colors
commands, Recolor Artwork, the color filters, Apply Nearest Swatch and the
ink limit of Print Ink Fixes) stored the new paint of the series marks but
not of the labels. stroke.set, the stroke weight steps, the gradient edits,
Swap Fill and Stroke and Default Fill and Stroke stored nothing when they
changed a label's characters. When the graph was drawn again, the labels
went back to their stored style.
- These commands now store, for every label of the kind they reach, the
label's object rows when they change, and the fill, stroke, stroke weight,
caps, joins, miter limit, dashes and stroke alignment of its characters
when all of them show the change alike.
- A gradient is stored without its placement, and each label fits it to its
own box. A stroke weight and dashes are stored as a scaled label shows
them.
- When labels of one kind change differently, the frontmost one sets what
the kind keeps.
- On a pie with Legends in Wedges, the legend labels' fill is stored only
when every legend label reached ends up with the same fill, so a recolor
of the whole pie leaves them black or white by their wedge.
- Axis lines, ticks and radar rings are not stored and are drawn again with
their default paint.
Docs: docs/mcp.md, the commands' params text, docs/target-app-parity.md.
A fill or stroke row that the Appearance commands gave a graph label, its
object opacity, and the characters the Eyedropper gave it lasted only
until the graph was drawn again: the graph stored nothing for a label's
rows, and the Eyedropper's character attributes were not stored.
- The graph keeps, for value axis numbers, category labels and legend
labels, the paint, weight, opacity and blend mode of a label's topmost
visible fill and stroke row, their order and place relative to the
Characters row, and the label's object opacity and blend mode
(labelStyles valuesRows, categoriesRows, legendRows). Every label of
the kind gets those rows when the graph is drawn again, with a placed
gradient fitted to each label.
- Every appearance.* command stores them, and so do graphicStyle.apply
and redefine and the paint, stroke, gradient and transparency edits
aimed at a row, even when the label already showed them.
transparency.set without a row and object.setProps store only the
object opacity and blend mode. With several labels of a kind, the last
one edited wins.
- appearance.copyFrom onto all of a label's text stores the character
attributes it applied and, from type, the paragraph attributes; a
transparency pick stores the object opacity and blend mode, and
append stores the rows.
- paint.setFill and paint.setStroke store a character fill or stroke
only when the characters take the paint (the whole label, or all of
its text highlighted with the Type tool); aimed at a row, they store
the row.
- Document Color Mode and swatch edits reach the kept rows.
Docs: docs/mcp.md, the commands' params text, docs/target-app-parity.md.
The Gradient panel's Method menu gets two methods of VectorCraft's own
beside Classic and Perceptual. They mix a linear, radial, conic or
diamond gradient's colors around the hue wheel: hue, saturation and
brightness each change evenly from one stop to the next, and the hue
angle keeps one direction, as the fixed-direction rules of
https://www.w3.org/TR/css-color-4/#hue-interpolation do. Hue Clockwise
lowers the angle (red, magenta, blue), which is clockwise on the color
wheels, and Hue Counterclockwise raises it (red, yellow, green).
Two stops whose hues are less than half a color level apart count as
one hue and keep it, so a color and a tint or shade of it do not turn
the whole wheel. A stop whose RGB numbers are less than half a color
level apart is gray and takes the hue of the stop it mixes with, and
black takes that stop's saturation too.
- `paint.editGradient` and `paint.setFill` take `method: hue-clockwise`
or `hue-counterclockwise`, in any case, with a space or an underscore
for the hyphen or without it; other spellings are an error. The native
file writes `HueClockwise` and `HueCounterclockwise`.
- The canvas and raster exports draw the gradient through stops on its
curve, within half a color level.
- Reverse turns the method the other way with the stops, and a Reflect
spread is repeated from the curve's stops, so both keep the colors.
- SVG and PDF write the gradient as an RGB image, CMYK and spot color
stops included, with the spot warning. EPS writes a shading in process
colors with a warning for spot stops. A legacy .ai file and the .ai
editing data get the colors between the stops as stops of their own,
in process colors, at most 256, with a warning. CSS gets those stops
too.
- The `method` error names the four values, in every catalog.
- The Method menu is as wide as its longest name in the interface
language, within the panel.
Docs: docs/mcp.md, docs/plugins.md, docs/target-app-parity.md, ROADMAP.
A graph draws its series groups again with no rows of their own, so a
fill, stroke, opacity or graphic style that a command put on a series
group was lost the next time the graph's data, type or size changed,
and the graph stored nothing. paint.setFill and paint.setStroke already
pass a series group's id on to the paths directly inside it.
- appearance.addFill, addStroke, setItem, removeItem, duplicateItem and
moveItem, graphicStyle.apply and transparency.set with a fill or
stroke item now edit the paths directly in a series group given by id
or picked with Group Selection, and the graph stores what they change.
A series group with no such path, or with a fill or stroke row of its
own, is still edited itself, and appearance.moveItem from "contents"
still moves the group's own Contents row.
- appearance.clear, appearance.reduceToBasic, appearance.showAllHidden
and graphicStyle.breakLink edit the group as well as its paths.
- graphicStyle.apply without add and appearance.transfer onto a series
group clear the group's own opacity, blend mode, isolation, knockout,
effects and style link, and graphicStyle.apply with add unlinks the
group. A transfer source that is one of the group's paths keeps its
look, and a source that is the group's only path is an error.
- graphicStyle.redefine passes the style to the paths of a linked series
group, other than the source and the linked paths whose look was
edited, and clears and unlinks the group.
- graphicStyle.new, graphicStyle.redefine and appearance.transfer read a
series group that holds no row, effect, opacity, blend mode,
isolation, knockout or style link of its own through the topmost path
directly in it, and a move clears each of those paths. A series group
that holds one of them is read itself, as before.
- The Graphic Styles panel reads the style link of such a selected
series group from that path.
Docs: docs/mcp.md, docs/target-app-parity.md. Not yet: the Appearance
panel and appearance.setActiveItem still read the selected group's own
rows, and the effect commands and the paint, stroke and gradient
commands with an item still edit the group itself.
Follows #5501.
paint.setFill, paint.setStroke, paint.none, Apply Fill to Stroke, Apply
Stroke to Fill and a library gradient or pattern applied with library.use
stored the topmost fill or stroke row of a graph series mark on the graph
even when that row was hidden, and stored a stroke set on a Filled Lines
band. When the graph was drawn again, every mark of the series took a
paint that the edited mark never showed, and a band took the stroke color
as its fill.
These commands now follow the rules of the Appearance commands:
- The graph stores the paint only when the row the command wrote is the
topmost visible row of its kind, or when the command changed what the
mark shows. With `item`, that is the named row.
- A series that draws Filled Lines bands stores no stroke paint or
weight; the band keeps its own outline.
- A row without paint before and after the command keeps what the graph
stores, so None on a marker without a stroke no longer removes the
line's stored stroke.
- When one command writes several marks of a series, the frontmost mark
sets what the graph stores, whatever the order of the ids.
- Apply Fill to Stroke and Apply Stroke to Fill on a mark whose row
already has the paint store it, as paint.setFill does.
A document without graph series marks among the edited objects skips the
comparison of paints.
Docs: docs/mcp.md and the params text of the paint commands.
A stroke weight set with stroke.set or stroke.weightUp/weightDown, a
gradient made, placed or edited with paint.editGradient,
paint.setGradientGeom or the freeform gradient edits, and colors changed
by paint.invert, paint.complement, the Edit Colors recolor and Blend
commands, recolor.apply, addon.color.filter,
addon.color.applyNearestSwatch or the ink limit of
addon.color.printFixes changed a series mark but not the paint the graph
stores, so the mark went back to the old paint when the graph was drawn
again.
appearance::edit_items now stores what its edit changes on the topmost
visible fill and stroke of series marks, as the Appearance commands do,
and the recolor pass does the same for the series marks it reaches,
including those inside a series group or a graph it is given. When one
command changes several marks of a series, the frontmost one sets the
stored paint. A gradient is stored with its placement.
These commands do not store the colors they give a graph's axes and
labels. docs/mcp.md, the params text and the Graphs line in
docs/target-app-parity.md say what is stored.
A colorized image whose linked file is missing shows the placeholder
preview, whose pixels are not grayscale. paint.setFill skipped such an
image and added a fill item instead, while the Fill proxy showed the
image's colorize color. A solid color now replaces the colorize color.
While that color is a spot color, black or white, a process color is
refused, since the relinked pixels may have an alpha channel.
Color Mask checked the whole document for a grayscale image colorized
with a process color only when the document passed that check before
the run. With another such image already in the document, the check
was skipped, and a process-colorized gray image could take masked
pixels with transparency.
Each image whose mask changes is now checked on the pixels the new mask
shows, made from the image's own pixels, so an image elsewhere in the
document no longer affects the result. A mask that hides nothing keeps
the image's own pixels and is still set. When new pixels replace a
masked gray image's own pixels under a process color, the mask made for
them hides nothing, and Apply Color Mask refuses them. The check shares
its colorize predicate with paint's check_colorized.
A process color can't colorize a grayscale image that has an alpha channel,
and paint.setFill refuses that combination. Remove Background gave such an
image an alpha channel and kept its process colorize color. It now refuses
the run, changing nothing, when an image it is given is gray and colorized
with a process color. It checks this before any image is decoded. Another
image in the document that already has that combination does not affect a
run on other images. Black, white and spot colors still colorize the result.
The rule check_colorized applies to each image is now one function,
paint::gray_colorized_with_process, which both checks call.
Docs: docs/mcp.md (Remove Background), docs/target-app-parity.md.
A linear, radial, conic or diamond gradient has a spread: Pad (the
default, as before), Repeat or Reflect, with a count of 1 to 100
(VectorCraft's own limit). Repeat and Reflect run the stops that many
times along the vector (around the turn for a conic) and keep going past
both ends, Reflect running every other run backwards, as SVG's
spreadMethod does.
The Gradient panel has a Spread menu and a Repeats field, and the
Gradient tool marks where the runs meet. paint.setFill and
paint.editGradient take `spread` and `repeats`; bad values are errors.
Presets, swatches, blends, strokes along or across a path, Outline
Stroke and Object › Expand follow the spread; Expand gives each run the
same whole number of objects, and Outline Stroke's mesh keeps the color
on each side of a sharp step between two stops at one offset.
SVG writes and reads spreadMethod: SVG import now reads a repeating or
reflecting gradient as one that repeats or reflects once, instead of
expanding its stops over the shape. CSS writes repeating gradients.
PDF writes a conic's sweep shading over one run. PDF, EPS, legacy .ai
and the .ai editing data write the other kinds with their stops
repeated across the shape: at most 256 runs and 16,384 stops (256 stops
in .ai files), and for a radial or diamond no farther than 65,536 runs
from its center, with a warning past those. PDF import now finds sharp
steps in any axial or radial shading instead of blurring them.
Docs: docs/mcp.md (Spread), docs/target-app-parity.md, docs/gaps.md.
Object › Plug-ins › Image Tools gains Color Mask… (`addon.image.colorMask`),
Remove Color Mask and Apply Color Mask. Color Mask hides colors of the
selected images without changing their pixels. It matches colors as Remove
Background does: the edge color, the corner color or picked colors, with
Contiguous, Match, Tolerance and Softness. The mask is kept with the image
object and saved with the document: its settings, and a PNG of the image's
pixels with the hidden colors made clear, at the source's depth with its
color profile and resolution. It is a VectorCraft addition.
- The canvas (clip groups and white-shape opacity masks included),
printing and separations, PDF (soft masks of CMYK images included), SVG,
EPS, the raster formats, the legacy .ai and .ai editing data, EMF/WMF,
DXF and the flattener draw the masked pixels. A linked image stays
linked, and an output that would refer to its file writes the masked
pixels instead.
- Copy and paste, placing a document and placed documents carry the masked
pixels with the image, under the names they are given there.
- The dialog opens on the selected image's mask settings, with a Preview.
Without settings, the command returns them and changes nothing.
- Setting, removing and applying a mask are each one undo step, and no
step is added when nothing changes. Apply makes the masked pixels the
image's own and embeds a linked image. A mask that hides nothing is only
taken off, and the image keeps its pixels and its link.
- When an image's own pixels are replaced (Update Link, Relink, Crop
Image, a recolor, a linked file read again on open), its mask is made
again for the new pixels, in every art tree and in opacity-mask art.
Crop Image cuts the image's own pixels. When the new pixels can't be
masked, the mask keeps its settings and hides nothing.
- Objects in `ids` that are not images, CMYK images, linked images that
show their preview, images past the decode allowance, masks that would
hide every pixel, and grayscale images colorized with a process color
are refused. The Eyedropper, Object Mosaic, Create Gradient Mesh, Crop
Image's trim and Image Trace read the image's own pixels.
- An image is selected by a click anywhere in its frame, as before.
Remove Background now also refuses a grayscale image colorized with a
process color. Its result has an alpha channel, and a process color is
refused on a grayscale image with transparency.
Docs: docs/mcp.md (Image Tools add-on › Color Mask),
docs/target-app-parity.md.
A new gradient kind, `diamond`, runs its stops from the center (the
start) out to a square with a corner at the end of the vector; the
aspect stretches the square across the vector into a rhombus. The
Gradient panel's Type row has a Diamond button, the Gradient tool draws
the square with the aspect handle and center ring, and a gradient
fitted to an object covers its box.
- Model and canvas: the level is |x| + |y| in the square's frame; a
square with no area (no vector, an aspect of 0, numbers out of range)
paints the last stop. The canvas draws it as pixels, out to a
stroke's miters, caps and arrowheads. Transforms keep the vector and
take the mapped size across it as the aspect.
- Commands: `paint.setFill`, `paint.editGradient` and
`paint.setGradientGeom` take `kind: "diamond"`; a negative aspect or
a focal point is an error. A conic or diamond gradient that a plug-in
returns with a focal point loses the focal point.
- Gradient tool: dragging the aspect handle of a radial or diamond
gradient whose file gave it a negative aspect now sets a positive
aspect; only a conic's negative aspect mirrors it.
- Export: PDF, SVG, EPS and legacy .ai write four linear gradients, one
for each quarter of the square, clipped to the shape (PDF as axial
shadings). PDF and SVG write an image for translucent stops, a
dithered fill or a flat square, and for the Perceptual method unless
PDF writes the stops as spot color shadings. SVG writes a diamond on
type characters as a radial gradient through the corners, and so
does CSS. A swatch library saved as CSS gets four corner layers, a
value for the `background` property (a circle when the swatch is
turned by an angle that is not a multiple of 90 degrees). The .ai
editing data holds a radial gradient through the corners. The
document exports give a warning.
- SVG export: a stroke drawn as an image, as conic wedges or as diamond
quarters now covers the miters, caps and arrowheads that reach past
its paint box.
- PDF import reads a function-based shading whose color is the same
along each square about the origin of its space as a diamond.
- Object > Expand gives nested squares or a mesh of rings.
Docs: docs/mcp.md, docs/target-app-parity.md.
A fourth gradient kind, conic (also accepted as `angle` or `conical`),
sweeps the stops once around the center (the gradient's start),
clockwise on the page from the vector, with a seam where the last stop
meets the first. A negative aspect mirrors the sweep, and reflecting an
object turns its sweep the other way, also for a conic without a
vector or with a flat extent.
- Gradient panel: a Conic button in the Type row; the Aspect Ratio
field works for conic gradients and keeps a mirrored sweep mirrored.
- Gradient tool: the extent ellipse, aspect handle and center ring of a
radial gradient, without a focal point.
- Rendering, swatch chips, transforms, warps, plug-in round trips, and
Object > Expand into wedges or a mesh of columns around the center.
- Commands: a negative `aspect` is an error for linear and radial
gradients, and so is a `focal` point for a conic gradient.
- PDF export writes a function-based shading whose calculator function
turns the angle into the ramp, and PDF import reads a function-based
shading that turns around the origin of its space back as a conic
gradient, also when that origin lies outside the painted area. A
PDF/A file, or a conic with more than 97 stops, gets an image
instead.
- SVG export writes 360 solid wedges clipped to the shape for opaque
stops and an image otherwise, and a linear gradient on characters.
- CSS export writes `conic-gradient(from ...)`, with the stops mirrored
for a mirrored sweep.
- EPS and legacy .ai export write 360 solid wedges; the .ai editing data
holds a linear gradient along the vector with the same stops.
- .af files: conical gradients open as conic gradients.
- An export that writes the gradient in another form gives a warning.
Supersedes #2326 and includes switch87's code from it.
Co-authored-by: Gert Pellin <gert@pellin.be>
Ruler guides were a list kept beside the layers. Each guide is now an
object of its layer or sublayer, always directly in it and above the
layer's art. These behave as on main:
- Guides draw on screen only. Exports (SVG, PDF, PNG, PSD), Fit All,
Select All, the art bounds, clicks and marquees, and a layer's
Pathfinder and Transform effects leave them out. The Layers panel
lists them as guide rows of their layer, the highest guide.list
index first.
- Deleting a layer deletes its guides, and merging layers keeps their
guides in the merged layer.
- The guide commands number guides as guide.list does, in the order
they were made.
New: each guide takes an id from the document's object ids. An object
command given a guide's id fails with an error, and node.move cannot
put a guide into a group or out of its layers. A guide made while an
opacity mask, a symbol or a pattern is edited moves to the top
visible, unlocked layer when editing ends.
File format: native files still save the guides in the document's
guides list, in every file version, so older builds read them. Each
entry may now also carry the guide's id, name, locked and hidden
flags; older builds ignore those fields. Opening a file puts each
saved guide on its layer (on the top visible, unlocked layer when the
file names none or a layer it does not have), and each saved guide
counts against the object limit for opening a file. A ruler guide node
that a file holds below a layer's art moves up to the layer's guides,
and one anywhere else (in a group or another object, beside the
layers, or in a symbol) is removed. Ruler guides read from an .ai file
become guide objects on the layer they were on, numbered in the order
the file lists them.
The params docs now state the direction (and the zero) of each angle or
rotation a command takes; behaviour is unchanged. Shape creation, text.create,
the grids, add-on shapes, saved views, Liquify, Puppet Warp pins, Mirror and
Radial Repeat and Vector Halftone turn clockwise; text character rotation,
gradients, the eraser tip and print screen angles turn counter-clockwise.
view.saved.new's rotation note changes its schema, so the API level goes to 3.
A test walks every command's params doc and fails when a param named like an
angle or rotation doesn't say clockwise or counter-clockwise.
Direct Selection of a path, compound path or type inside a dynamic instance now applies transparency and gradient or pattern fills through the usual paint commands, and the G5 docs catch up on Overlap and Pattern auto corners.
Recent Colors now keeps up to 30 solid colors instead of 10, newest
first and each once; applying a 31st drops the oldest. The Session
holds the list, so it stays the same when another document is opened
or made active, and it lasts for the session.
The Color and Swatches panels wrap the chips onto further rows when
the panel is too narrow for one row, so every color can be clicked.
A list that fits one row draws as before.
Docs: paint.recent params text, docs/mcp.md, docs/target-app-parity.md.
The help text of appearance.addFill, addStroke, clear, reduceToBasic,
removeItem, duplicateItem, moveItem, copyFrom, showAllHidden and transfer,
paint.default, and graphicStyle.apply and redefine said the graph stores
only the paint and weight of the topmost visible fill and stroke. These
commands also store that row's opacity and blend mode, as
appearance.setItem's help and docs/mcp.md already say.
An object opacity or blend mode set on a path directly in a graph series, and
the blend mode of its topmost visible fill or stroke, were lost when the graph
was drawn again. The graph now stores them per series next to the row opacity
it already stores, and puts them on every path directly in the series when it
draws the series again.
- transparency.set and object.setProps store the object's opacity and blend
mode. A value the mark already has still replaces the value another mark of
the series stored.
- appearance.clear, appearance.transfer, appearance.copyFrom with Transparency
picked up, graphicStyle.apply without add and graphicStyle.redefine store
the object's opacity and blend mode they leave on the mark.
- The other Appearance and style commands store them when they change them
on a mark.
- Every command that stores the opacity of the topmost visible fill or stroke
also stores its blend mode. A Filled Lines band's outline takes the stored
stroke opacity and blend mode and keeps its black 0.25 pt stroke.
- An opacity and blend mode are stored as a pair from the edited mark:
setting only the opacity of one mark also stores that mark's blend mode.
- A graph design's copy in a series keeps its own values.
Docs: docs/mcp.md, docs/target-app-parity.md.
Applying a graphic style, Redefine Graphic Style, Default Fill and Stroke
and Swap Fill and Stroke on a path in a graph series changed the path
but not the paint the graph stores for the series. When the graph was
drawn again, the series went back to its earlier paint.
These commands now store the mark's topmost visible fill and stroke on
the graph, in the same undo step as the command:
- graphicStyle.apply stores the paint, weight and opacity of both kinds
of row. With `add` it stores only the kinds of row that are visible
in the style.
- graphicStyle.redefine stores the paint, weight and opacity of the
marks still linked to the style.
- paint.default stores the white fill and the 1 pt black stroke, both
opaque.
- paint.swap stores the swapped paints and the stroke weight and keeps
the stored opacity. It swaps the topmost rows even when they are
hidden, and a hidden row it swaps is not stored.
The rule for each kind of mark is the one docs/mcp.md gives for the
Appearance commands. docs/mcp.md also says what the graph does not
store from a graphic style and that a series group given to a graphic
style command is not stored. The params text, docs/mcp.md and the
Graphs line in docs/target-app-parity.md list the commands.
As asked in review: the reference app has no such commands, so they ship
in the Color Effects add-on. paint.fillToStroke / paint.strokeToFill are
now addon.color.fillToStroke / addon.color.strokeToFill in
cmd/addons/color.rs, listed in Object > Plug-ins > Color Effects instead
of the Color panel menu, with catalog rows in all 26 catalogs.
Two fixes from CodeRabbit's review:
- A graph's series marks and labels give the graph the copied paint in
the same undo step (capture_series_paints / capture_label_styles, as
paint.setFill does), so drawing the graph again keeps it. Targets go
through graph::paint_targets, so a series group stands for its marks.
- A copy that would leave the effective paint as it is skips the object,
so None copied onto a missing fill or stroke adds no item and records
no undo step.
paint.fillToStroke and paint.strokeToFill copy each target's own top fill onto its top stroke, or the other way, so a mixed selection keeps each object's colour. They aim and undo like Swap Fill and Stroke: groups and compound paths by their members, each text run with its own paint, and only the characters the Type tool has selected. A stroke given paint at width 0 gets 1 pt, skipNone leaves objects whose source paint is None alone, and the defaults for new art change the same way. Both are in the Color panel menu after Invert and Complement.
The enabled check of artboard.deleteEmpty built the full list of empty
artboards, measuring the visual bounds of the art against every
artboard, each time the Artboards panel menu was drawn. It now returns
false at once in a document with one artboard (that artboard is never
deleted). With two or more, it asks the new
Document::any_artboard_without_art whether any unlocked artboard has no
art, which is the same answer as a non-empty list.
Document::any_artboard_without_art builds the symbol boxes once for all
the artboards and first tests each path and compound path by a quick
box: its control points' box grown by the farthest its strokes reach
(Appearance::outset), with a margin for rounding. An artboard that no
quick box meets is reported empty without measuring along any path.
Only when every artboard may hold art is each one measured.
Document::artboard_has_art also skips measuring an object whose quick
box misses the artboard; its answers are unchanged.
The check of gradient.presets.save found the proxy paints through
Node::proxy_paint, which measures the object's geometric bounds for a
gradient box the check does not use. Node::proxy_paint_only picks the
same paint without measuring, and the engine's proxy_paint uses it, so
the Fill and Stroke proxies and the mixed-paint test no longer measure
each object either.
With an open stroked path of 200,000 anchors selected, the two checks
take under 0.01 ms instead of 21.9 ms and 17.2 ms in a release build, and
Delete Empty Artboards' check takes 0.85 ms instead of 42.1 ms with a
second, empty artboard.
Tests compare each check with what its command does (one artboard, all
empty, one empty, locked empty, nested art; with and without a
gradient), compare any_empty_artboards with the previous check and with
measuring every object on 120 seeded documents, compare the quick boxes
with the visual bounds on seeded strokes, and compare proxy_paint_only
with proxy_paint on seeded objects.
Docs: docs/target-app-parity.md, one line under Delete Empty Artboards.