Reversal read the midpoints after Gradient::reverse had reordered the stops, so each stop
took the wrong segment's midpoint: red-to-blue with its midpoint at 20% reversed to a 50%
midpoint instead of 80%, moving the transition.
Gradient::reverse now mirrors each segment's midpoint with it, so a reversed gradient is the
same ramp the other way round; paint.editGradient's own (wrong) pass is gone, and the SVG
importer's reflect spread, which had a correct copy of the same mirroring, uses it too.
Tests: two- and three-stop gradients with off-centre midpoints sample the same reversed (t vs
1 - t) and come back after two reversals; paint.editGradient reverse turns a 0.2 midpoint into
0.8.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
# ROADMAP.md
add_text passed `color` to text.create only, and text.createInPath built its own character
style without a colour, so type on a path and path-based area type took the current fill.
text.createInPath now uses the same new-type style as text.create (size, font, style, color,
else the current fill), and add_text sends `color` on both routes.
The MCP test draws a blue rectangle (the current fill), turns it into on-path and area type
with color #ff0000, and reads red back through inspect_document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
# ROADMAP.md
With every layer hidden or locked, the insertion layer fell back to the top layer anyway, so
drawing a shape, typing, pasting, making a graph, a flare, a grid, trim marks or a symbol put
the art on a hidden or locked layer: invisible, or unselectable by Select All. Illustrator
refuses to draw on a locked or hidden target layer (public forum answers, e.g.
https://community.adobe.com/t5/illustrator/can-t-draw-a-new-object-in-my-document/m-p/11107182).
DocState::target_parent is insertion_parent for new art, failing with "the target layer is
locked or hidden" when it would be a layer that isn't editable; those creation paths use it.
Another layer that is shown and unlocked still takes the art, as before. The message is in the
de, es, fr, it, ja, ru and uk catalogs. File > Place is left as it is (another branch reworks
it).
The test hides, then locks, the only layer: an ellipse, a polar grid, text and a paste each
fail with the message and leave the document unchanged; a new layer then takes a rectangle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
# ROADMAP.md
Character and paragraph styles checked a new attribute against the keys its base style
serializes, and the JSON leaves out Auto leading and kerning, no mojikumi and other defaults.
So charStyle.new / setAttrs refused leading or kerning, paraStyle.new refused mojikumi, and a
style captured from text with explicit leading failed to apply to Auto-leading text, to
duplicate or to come back out of a library.
The check now also accepts every field name the style type deserializes (read from serde's
derive), still refusing unknown keys and the file-only justify_auto.
The test creates and edits styles with kerning, leading, small caps, mojikumi and direction,
applies a captured leading style to fresh text with and without Clear Overrides, duplicates it,
uses it from a library, and checks `nope` and `justify_auto` are still refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
# ROADMAP.md
Asset Export, File › Export, Save for Web and Object › Rasterize drew
each shape with its own smoothed edge, so where two shapes met edge to
edge (Divide, Shape Builder, abutting art) both half-covered the pixels
on the line and the background showed through as a thin ghost border.
With Art Optimized (the default) these now supersample, as the option's
name in Illustrator says: the art is drawn with hard edges at four times
the size (three or two times for large images, within a 40-megapixel
budget and the renderer's 65,535-pixel side) and each block averaged
into one pixel. Pixels shared by two shapes are fully owned by one or
the other at the finer size, so no seam is left, and the outer edges
are still smooth. The canvas keeps shape-by-shape smoothing for speed;
None and Type Optimized are unchanged.
RenderOptions::supersample turns it on (exports and Rasterize set it).
A render test checks two triangles sharing a diagonal: a seam with
shape-by-shape smoothing, none supersampled, and the factor's limits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Same checks in a third of the code; the corners destructure into the
four points instead of being indexed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PR added `checked_anchor_from_json` next to the lenient
`anchor_from_json` and dropped path.rs's import of the latter, which
`path.appendAnchor` still called, so the engine didn't compile.
`anchor_from_json` is now the strict parser itself (handles through
`finite_numbers`), used by path.create, path.setAnchors and
path.appendAnchor alike: the Pen's appended anchor with a malformed
handle fails too instead of losing the handle. Tests: a non-boolean
`closed` and a malformed appended handle change nothing. rustfmt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`text.reshapeArea` read its anchors with the lenient `parse_refs`, so a
malformed reference among good ones moved only the good ones. It now
uses `checked_refs` as well, and `parse_refs` (no other user) goes.
Test: a mixed list fails without moving anything.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A rejected Stroke panel edit with nothing selected leaves the next
object's stroke as it was; an accepted one sets its weight and dashes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `finite_numbers::<N>` (cmd/mod.rs) reads an array of exactly N finite
numbers, nothing dropped or padded; `rect` uses it, and the rect cut,
Free Distort and anchor handles reuse it in the PRs stacked on this one.
- `at` is parsed once (`at_param`) instead of point_param plus a second
shape check that indexed the params; `at: null` means no position, as
null does for the other optional params, and gets a test.
- rustfmt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eight raster effects on the pixel-filter pipeline
(vectorcraft_effects::pixel::brushstrokes), under a new Effect › Brush
Strokes submenu between Blur and Distort in the Raster Effects section:
- Accented Edges (edgeWidth pt 1..14, default 2; edgeBrightness 0..50,
38; smoothness 1..15, 5): the edges of the softened object painted
towards white chalk at high brightness or black ink at low brightness;
Smoothness also leaves the faint edges out. The shape stays as it was.
- Angled Strokes (directionBalance 0..100, 50; strokeLength pt 3..50, 15;
sharpness 0..10, 3): light areas in right diagonal strokes, dark areas
in left diagonal ones; 0 makes every stroke a left diagonal, 100 every
stroke a right diagonal. Sharpness brings detail and bristles back.
- Crosshatch (strokeLength pt 3..50, 9; sharpness 0..20, 6; strength
1..3, 1): the detail kept under pencil hatching along both diagonals,
Strength passes of it, the edges roughened.
- Dark Strokes (balance 0..10, 5; blackIntensity 0..10, 6;
whiteIntensity 0..10, 2): short strokes towards black in the dark
areas, long strokes towards white in the light areas.
- Ink Outlines (strokeLength pt 1..50, 4; darkIntensity 0..50, 20;
lightIntensity 0..50, 10): black outlines over the edges, ink hatching
in the shadows, shadows darkened and highlights lightened.
- Spatter (sprayRadius pt 0..25, 10; smoothness 1..15, 5): each pixel
shifted by noise up to 0.4 x the radius; Smoothness makes the grains
larger and smoother.
- Sprayed Strokes (strokeLength pt 0..20, 12; sprayRadius pt 0..25, 7;
strokeDirection Right Diagonal, Horizontal, Left Diagonal or Vertical):
strokes along the direction, scattered by up to 0.3 x the radius.
- Sumi-e (strokeWidth pt 3..15, 10; strokePressure 0..15, 2; contrast
0..40, 16): wet strokes, ink bleeding from the darks along paper
fibres, more ink with pressure and an S-curve contrast for rich blacks.
It works in value only: hues and saturation stay, and so does the
object's shape (no fringe in the transparency).
A stroke averages the colour along a segment through the pixel (16
samples at most, the art softened first when they lie more than a pixel
apart); strokes lie in rows around the object's centre, each row
starting at random and each stroke shifted along itself, with streaked
noise for the bristles. Everything is in document space, so the effects
look the same at any zoom or resolution, move with the object and draw
the same every time; outsets and reaches are exact. On a 2000 x 2000
raster in release the slowest settings take 0.3-2.0 s (Crosshatch at
50 pt with 3 passes the most).
Shared helpers instead of copies: edges::sobel (Glowing Edges now uses
it, unchanged), distort::displace and texture::Tones are pub(super).
Catalogue rows with parameter docs, menu entries, Sprayed Strokes'
direction dropdown, translations (de, es, fr, it, ja, ru, uk, zh-hans,
zh-hant; menu labels in cs and pt-br), tests (parameters and clamping,
outsets and reaches, each filter and option showing, chalk and ink on
the edges only, shadows and rich blacks, Sumi-e keeping the shape and
hues, hatching keeping the tone, strokes smearing along their direction,
Spatter's radius, resolution and move invariance, canvas, multithreaded,
PDF and SVG, the command sweep with junk parameters, the menu) and
ROADMAP.
Behaviour from public documentation only:
https://helpx.adobe.com/photoshop-elements/using/brush-stroke-filters.htmlhttps://www.computerhope.com/jargon/p/photoshop-sprayed-strokes.htm
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A visibility variable could hide an object but never show it again: dataset
application, capture and binding refused any object that wasn't visible
(`is_editable`), so a row that hid an object left it hidden for good and a
capture after hiding it in the Layers panel didn't record it. They now use
`Document::variable_target`, which ignores the object's own visibility
(locked art and art on hidden or locked layers are still skipped).
- Renaming a variable renamed the active dataset when the two shared a name,
leaving it pointing at a dataset that doesn't exist.
- `variable.delete` and `dataset.delete` without `names` act on the
highlighted variable and the active dataset, so Window > Variables > Delete
Variable / Delete Data Set no longer fail with "missing `names`".
- `ids` is checked like other commands' explicit targets (a non-list no longer
falls back to the selection, bad ids are errors), names are trimmed and
capped at 255 characters, and the bind checks are shared by Bind and Make
Dynamic.
- Make Dynamic's fallback name no longer becomes " 2" for unnamed art.
- `dataset.list` reads the art once for every row's drift check, and
`Variables::prune` looks names up in a set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Select Bound Object ran variable.unbind on the selection, so choosing it silently
removed the bindings; Delete Variable and Delete Data Set deleted every row. Now
the panel keeps a highlighted variable row (a click highlights and selects what
it drives, as the reference panel does): Select Bound Object selects what the
highlighted variable drives via select.set, Delete Variable deletes only it, and
Delete Data Set deletes only the current set. Variable Options and Rename Data
Set open their dialogs preselected with the highlighted variable / active set
(the double-click used to invoke variable.rename with a bare name, which only
errored). Unbind enables only over a bound selection; Make Text Dynamic only for
exactly one type object.
The highlight is per-document session state with its own command
(variable.highlight, no undo step, like layer.highlight): rename carries it,
delete clears it when stale. New engine tests (18) and panel tests cover the
reviewer's regression case, verified to fail on the old wiring.
The engine side was called a solid base, but the panel could only list and
apply: variables and datasets were made through commands, which most people
won't find. The reference app's Variables workflow is now what the panel does,
and every button runs a `variable.*` / `dataset.*` command, so the panel and an
agent drive the document the same way.
New commands (all also in Window › Variables, the palette and over MCP):
- `variable.makeTextDynamic` / `variable.makeVisibilityDynamic`: define a
variable of that kind over the selection, named after what it drives, and
bind it — one step, as the panel's two buttons.
- `dataset.capture`: record what the bound objects hold right now as a new row
("Data Set 1"… ) and make it active. This, rather than typing values, is how
rows are made: change the art, then capture.
- `dataset.update`: write what the art holds now into the active row. Without
an active row it says so rather than picking one.
- `variable.rename` / `dataset.rename` (Variable Options…, Rename Data Set…):
a name is how a variable is referred to, so renaming carries its bindings and
its values in every row with it.
- `dataset.list` reports `matches` per row: whether the art still holds what
that row says.
`Document::captured_values` is the one place that reads a bound object's value,
so capture, update and the drift check can't disagree about what the art says.
Panel: the current data set across the top as a dropdown with ◀ ▶; variable
rows with their kind's icon and the name of the object they drive ("Multiple
objects" when it is more than one), a click selecting it and a double click
opening Variable Options; a dataset row in italics once an edit has moved the
art away from what it says; Make Text/Visibility Dynamic, New, Unbind and Delete
below, Capture and Update on a second line; and a (≡) menu with the same
commands for the ones that act on a row rather than the selection.
Checked on Rust 1.99 (the current stable), which is what the review asked for:
cargo xtask ci, all 7 steps pass.
`bindings` mapped an object id to a single variable name, so binding an object
to a second variable silently dropped the first — a binding lost with nothing
to show for it. The reference app binds the selected object to the selected
variable, one click at a time, and nowhere says a second binding replaces the
first (https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/
automate-actions/work-with-variables.html), so this allows several instead.
`bindings` is now a set of variable names per object. `dataset.select` walks
them in the order the variables are defined, which keeps two text variables on
one object deterministic (the later one's value is what is left, and `applied`
counts both). `variable.bind` reports `bound` (added) and `already` (was there
already); `variable.unbind` takes an optional `variable` to drop one binding
instead of all of them.
This is done before the field ships rather than after: `variables` has never
been in a released file, so the shape can still change for free. Once a version
with it is out, the same change would need a format version bump and a
migration, since `crates/format` keeps every version from 1 loading.
A strict superset of what worked before: every binding a document had is still
there and still applied, in the same order.
cargo xtask ci: all 7 steps pass.
Review of #761 turned up three real defects and a feature that was visible
but not usable. All of it is fixed here, each defect with a regression test
that fails without the fix.
- A binding outlived the object it named. Deleting art left its entry in
`variables.bindings` forever: it counted as a skip in every dataset
application, `variable.list` counted it, and the saved file grew with every
delete. `Document::prune_variable_bindings` now lets them go after each
edit, as `prune_assets` does for Asset Export's.
- `Variables::prune` cleared `active_dataset` by comparing it against the
*variable* names being deleted. A dataset named like a variable lost its
active state when the variable went. The two are separate namespaces; the
block is gone (dataset.delete already did it right).
- `variable.delete` reported the number of names asked for, not the number
deleted, so deleting a name that was not there still said `deleted: 1`.
- `dataset.set` (new): a dataset's values could only be set when it was
created, so changing one meant deleting and rebuilding the row — losing its
place in the list and the active one. This replaces the row's values and
reports what it dropped.
- Window › Variables, and dialogs for the "…" commands. Every command had an
empty menu path and the command palette runs those with `{}`, so nothing
could be defined from the UI at all. The commands now carry their menu
path, the "…" ones open a Variables dialog (a name and a kind; a name and a
field per variable, its kind's editor), and `dataset.set` opens with the
row's own values.
- Locked art is neither bound nor rewritten by a dataset, and says so.
- `set_plain_text` told a node that existed but was not type that it was
missing.
- The panel had Document Info's icon, so the two were indistinguishable in
the collapsed column; it gets its own, plus New/Delete buttons, and its
rows say what their count is.
Format round trips now cover variables (the testkit fixture defines both
kinds, binds them and applies a row), and ROADMAP/README no longer list the
feature as missing.
cargo xtask ci: all 7 steps pass.
Three raster effects on the pixel-filter pipeline
(vectorcraft_effects::pixel::distort), under a new Effect › Distort
submenu between Blur and Pixelate in the Raster Effects section (the
vector Distort & Transform stays its own submenu):
- Diffuse Glow (graininess 0..10, default 6; glowAmount 0..20, 10;
clearAmount 0..20, 15): the highlights, softened over 2 points, glow
white from the brightness Clear Amount leaves clear; white grain as
dense as Graininess says is sprinkled over the object, thicker in the
glow. Transparency stays as it was.
- Glass (distortion 0..20, 5; smoothness 1..15, 3; texture Blocks,
Canvas, Frosted or Tiny Lens, default Frosted; scaling % 50..200, 100;
invert): the object seen through a glass surface; each pixel takes the
colour the surface's slope bends it to, sampled between pixels. Slopes
are taken over a span Smoothness widens. The surfaces are made in code
(Canvas is Texturizer's weave); Load Texture is left out.
- Ocean Ripple (rippleSize 1..15, 9; rippleMagnitude 0..20, 9): smooth
noise ripples about Ripple Size + 2 points across shift the object by
up to 0.03 x magnitude x their size.
The shifts are capped, so the effects' outsets and reaches are exact,
and every surface lies in document space around the object's centre:
the same at any zoom or resolution, moving with the object, identical
on every redraw.
Catalogue rows with parameter docs, menu entries, the Texture dropdown,
translations (and German's missing Canvas), tests (parameters and
clamping, outsets, each texture and Invert, the ripples' reach, the glow
and its grain, resolution and move invariance, canvas, multithreaded,
PDF and SVG, the command sweep with junk parameters, the menu) and
ROADMAP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each axis now takes its own tick marks, and the value axes a prefix and
suffix for their numbers:
https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/visualize-data/change-graph-value-axes.html
- Value axes (the left one, or a bar graph's bottom one, and the right
one): tick marks of no, short or full length, and a count per
division; labels stay at the divisions.
- Category axis: the same, at the labels or, with Draw Tick Marks
Between Labels, between them (halfway between labels when they sit
at the plot's edges).
- Add Labels: prefix and suffix per value axis ($, %).
- The Graph Type dialog offers the lengths and value axis sides as
choices, and counts it sends back as floats are read.
Own choices, as the page doesn't say: the lengths are none, short (the
4 pt ticks as before) and full (across the plot); category axes start
with no tick marks, as they had none before. Counts are capped at 20
per division and 10,000 category tick marks, affixes at one line of 64
characters. A full-length tick on the zero line or an axis line is left
out.
The legend moves past a bar graph's last value label when a suffix
runs it into the legend's gap. New fields are written only when set;
graphs with the default options draw the same art as before.
The value axis can now go on the left side (as before), the right side
or both sides of column, stacked column, line, area and scatter graphs:
https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/visualize-data/change-graph-value-axes.html
With the axis on both sides, Separate Scales gives the series assigned
to the right axis a scale of their own there, with its own tick values
(rightTicks, rightAxisMin, rightAxisMax):
https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/visualize-data/assign-different-scales-to-value-axes.html
- A series goes on an axis with Group Selection (or `seriesIndexes`)
and `valueAxis` left or right. Without separate scales both sides
show the one scale.
- Each axis stacks its own stacked columns and areas; stacked columns
of the two axes take a slot of the cluster each instead of covering
each other.
- An axis with no series of its own shows the other one's scale. The
right scale's zero gets a line of its own when it isn't the left's.
- The right axis' labels push the legend further right.
- With series on both axes picked, the Graph Type query returns no
value axis, so the dialog's OK leaves each series where it is.
- Graph Data drops right-axis assignments for series the new data no
longer has.
Own choices, as the pages don't say: bar graphs keep their value axis
along the bottom, and scatter graphs draw every series on one scale.
New fields (valueAxis, separateScales, rightSeries, rightTicks,
rightAxisMin, rightAxisMax) are written only when set, so other files
are unchanged and older versions draw the left axis as before. Graphs
with the axis on the left draw the same art as before.
- Preferences hides System Title Bar on macOS and the web, where it did
nothing (it fell through to the plain checkbox row), and lists it with
the window options instead of under Language.
- The next-launch note reuses "Applies the next time VectorCraft starts."
instead of a second wording in every catalog.
- A new window title asks for one more frame: AppKit lays the title bar
out again for it, and the next frame centres the window buttons (#968).
- The menu bar drops the bold branch for the VectorCraft menu it no
longer shows; theme is imported where the tests use it.
- Docs: Language is Preferences › User Interface › Language (VectorCraft
› Language on macOS); the control protocol describes System Title Bar;
ROADMAP Last updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The link watch checks the active document's linked files every two
seconds and as the window comes back to the front, so the separate check
on focus that #925 added (missing_links::after_focus and its asked-files
set) goes: with both, a change was acted on twice. The watch also
walks the document's links only when a look is due, not every frame, and
wakes the app every two seconds only while the document has links. The
doc comments of status and links::groups are back on their functions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Linked files another app changes while their document is open now follow
Preferences > File Handling > Update Links, as they do when it opens:
Automatically reads them again (an Update Links undo step), Ask When
Modified offers to (a question worded for files changed on disk while
working), Manually leaves them modified in the Links panel.
The desktop app stamps the active document's linked files (size +
modification time) on a worker thread every two seconds and when its
window comes back to the front; no look starts while a dialog is open.
Stamps are kept per document; a change is acted on once; a file still
being written is tried again. links.updateChanged reads changed files
again regardless of the preference (CLI, MCP).
New strings translated in all ten catalogs; ROADMAP updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Graph Type on series picked with Group Selection (or listed in the new
`seriesIndexes` param) now sets the type of those series only, so a
column graph can carry a line series:
https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/visualize-data/combine-different-graph-types.html
- Column, stacked column, line and area mix on the vertical value axis;
bar and stacked bar on the horizontal one. Scatter, pie and radar take
no other type. The help page doesn't list which types mix, so this is
our own choice: types that share the value axis.
- Stacked columns take one slot of the column cluster (a graph of
stacked columns only is unchanged); stacked series stack with each
other, areas with each other, and the value axis covers both totals.
- Areas draw at the back, lines in front of columns. Category labels,
areas and lines sit at the category centres once a series that isn't
edge to edge is in the graph.
- With only series selected, the Graph Type query returns their type,
so the dialog's OK keeps it, and they stay selected after the change.
An explicit `id`, or anything else in the selection, is the whole
graph, as before.
- A series given the graph's own type follows the graph again. A type
the graph's type can't take is drawn as the graph's and kept.
Series types are stored in a new `seriesKinds` field, written only when
a series has its own type; versions that don't know it draw the whole
graph as the graph's type. Graphs of one type draw the same art as
before.
Six raster effects on the pixel-filter pipeline
(vectorcraft_effects::pixel::texture), under a new Effect › Texture
submenu between Sharpen and Video in the Raster Effects section:
- Craquelure (crackSpacing pt 2..100, default 15; crackDepth 0..10, 6;
crackBrightness 0..10, 9): the object on relief plaster that cracks into
plates about Crack Spacing apart (a Voronoi network over jittered points)
and along the quarter, half and three-quarter contours of its softened
tones. Depth sets how dark and wide the cracks are and how high the
plates' bevels and the tones stand (lit from the top left); Brightness
how brightly the surface is lit.
- Grain (intensity 0..100, 40; contrast 0..100, 50 = unchanged; grainType
Regular, Soft, Sprinkles, Clumped, Contrasty, Enlarged, Stippled,
Horizontal, Vertical, Speckle; default Regular): coloured 1-point grains,
smooth ones, clumps, 2-point grains, streaks along rows or columns, dark
specks; Sprinkles and Stippled use the background colour, white.
- Mosaic Tiles (tileSize pt 2..100, 12; groutWidth 1..15, 3; lightenGrout
0..10, 9): irregular tiles bevelled at their edges, with sunken grout half
a point wide per step, lightened as Lighten Grout says.
- Patchwork (squareSize pt 0..10, 4; relief 0..25, 8): squares filled with
the colour around their centres, raised to heights that follow the
highlights and shadows, a little more or less at random.
- Stained Glass (cellSize pt 2..50, 10; borderThickness 1..20, 4;
lightIntensity 0..10, 3): single-coloured panes leaded in the foreground
colour (black, half a point per step), lit from behind at the object's
centre and fading out towards its corners (PixelSpace gains `extent`).
- Texturizer (texture Brick, Burlap, Canvas, Sandstone, default Canvas;
scaling % 50..200, 100; relief 0..50, 4; lightDirection Bottom, Bottom
Left, Left, Top Left, Top, Top Right, Right, Bottom Right, default Top;
invert): the object on a surface in relief. The four surfaces are height
fields generated in code (bricks in stretcher bond, plain weaves coarse
and fine, octaves of smooth noise); no image files. Load Texture is left
out.
Every pattern lies in document space around the object's centre, so the
result is the same at any zoom or raster resolution, moves with the object
and is identical on every redraw (hash noise of cell indices); slopes are
taken in document units. Work per pixel is bounded (a 5 x 5 cell search,
four or five height samples, box blurs); parameters are clamped to their
ranges and finite-checked. The Pixelate helpers (Place, the cell search,
the softened colours) are shared, Mezzotint uses the shared smooth noise,
and the effect dialogs' dropdowns translate their labels in an "effect"
context where the plain word means something else (Soft, Horizontal and
Vertical are type settings in Japanese and Chinese).
Catalogue rows with parameter docs, outsets and reaches, menu entries,
dropdowns (Grain Type, Texture, Light Direction), translations, tests
(parameters and clamping, cracks and contours, ten distinct grains, tiles
and grout, squares in relief, leaded panes and the centre light, the
textures lit and inverted, the same at twice the resolution and when
moved, canvas, multithreaded, PDF and SVG, the command sweep with junk
parameters, the dialogs and the menu) and ROADMAP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A graph's data can now hold blank cells: an empty CSV cell, a null in
`rows` (any cell that isn't a finite number), the rest of a short row,
or a row that has only a label.
- Columns, bars and stacked segments leave a blank cell out instead of
drawing a zero-height mark.
- Line and scatter graphs break their lines around it and draw no
marker there; the value axis ignores it.
- Area, pie and radar graphs draw it as 0, which is what it adds to
them.
The help pages don't describe blank cells, so this is our own choice:
a cell with no value has nothing to draw, which also matches how a
series with a missing year reads in a report.
In the file a blank cell keeps a 0 in `rows` and is listed in a new
`blanks` field ([category, series] pairs, written only when there are
any). A version that doesn't know blanks ignores the field and reads
the zeros, so files still open there, and files without blanks are
unchanged.
A number in straight quotes is a label, as the help page on graph
labels says (a year as a category name, for one). The CSV reader used
to strip the quotes first and read it as a value. Label columns are now
decided for the whole column: once any row starts with a label, every
row's first cell is its label, so a row with an empty label no longer
shifts its values one place left. The data window writes a header row,
labels that are empty, read as numbers or have spaces around them in
quotes, and blanks as empty cells, so they come back as they were.
graph.setData {} returns blanks in `rows` as null, so the rows it
returns can be edited and sent back. A label-only row keeps its place
with blank values; a row with neither a value nor a label is dropped,
and a first row that is a label followed by empty cells (`Year,,`) is
still read as a header with no series names.
The data grid is built in full for drawing and for the data window, so
it now stops at 10,000 categories of 256 series, which keeps a file
with one very long row from allocating without bound.
Behaviour (quoted labels): Add graph labels and data sets:
https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/visualize-data/add-graph-labels-and-data-sets.html
Double-clicking a Scatter, Art or Pattern brush in the Brushes panel (or the
panel menu's Brush Options...) now opens its options dialog, as Calligraphic
and Bristle brushes already did. OK runs brush.options and every stroke
painted with the brush redraws.
- Scatter Brush Options: Name; Size (10-1000 %), Spacing (10-10000 %),
Scatter (-1000-1000 %) and Rotation (-180-180 deg), each Fixed (the first
value), Random (between the two values) or Pressure (the first value at
the lightest pen pressure, the second at the heaviest); Rotation relative
to Page or Path. Scatter brushes now keep these modes (`modes`); brushes
saved before keep their look (Random where the two values differ).
- Art Brush Options: Name; Width (1-1000 %); Scale Proportionately, Stretch
to Fit Stroke Length or Stretch Between Guides with the guides' Start and
End (% of the art length); the four Directions; Flip Along and Across.
- Pattern Brush Options: Name; Scale (1-10000 %), Spacing (0-10000 %); the
five tiles (Outer Corner, Side, Inner Corner, Start, End) drawn with their
art or None; Flip Along and Across; Stretch to fit, Add space to fit or
Approximate path.
- All three: Colorization (None, Tints, Tints and Shades, Hue Shift with its
Key Color picked from the art's colours, the most used one by default) and
a live preview of a stroke painted with the edited brush.
Brush definitions from files and commands are now kept within these ranges
(Brush::sanitize, also for Calligraphic and Bristle values); unknown modes,
directions, fits or colorization methods are refused. The brush.options and
ui.brushOptions docs list the new params and fields. Tests: the brush model
(modes, pressure, sanitizing, art colours), engine params reaching the
brush and strokes redrawing, junk params in command_sweep, and headless
dialog tests for each dialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New line graphs put their points at the centres of their categories, the
option's default in the reference app; graphs saved before the option
existed were drawn edge to edge and keep it (a field default of its own).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Line graphs take the Edge-to-Edge Lines option (`edgeToEdge` on
graph.setType). On, the lines run across the whole plot, which is how
they always drew and stays the default, so existing graphs and files
look the same. Off, the points sit at the centres of their categories,
like the columns of a column graph, so a line graph lines up with a
column graph of the same size. The page describes only what the option
does when on; the off position is its opposite.
Radar graphs drew their ring and markers whatever Mark Data Points and
Connect Data Points said. They now follow both, like line and scatter
graphs.
Behaviour: Format columns, bars, and lines, Graph Type options for
line, scatter and radar graphs (Mark Data Points, Edge-to-Edge Lines,
Connect Data Points):
https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/visualize-data/format-columns-bars-and-lines.html
The corner widgets only showed when the selection was exactly one path.
Now every selected path that has them shows its widgets: live rectangles
and polygons with the Selection tool, any path with Direct Selection,
each with its own corners (all of a path selected whole, the
Direct-Selected ones otherwise). Dragging any widget rounds every shown
corner of every path in one step, Alt-click cycles their kind, and the
red limit outline covers them all; a double-click opens Corners for the
path it was on. Each widget is drawn in its own layer's colour.
object.setLiveShape takes items: [{id, corners?}], several objects each
with its own corners, which the drag sends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Search: a search field at the top of the Libraries panel filters the
current library's colours, character and paragraph styles and graphics
by name, ignoring case; groups without a match are hidden, groups with
one open, and a search nothing matches says so ("No results").
Groups: items can be put in user-named groups, kept in the library file
(`groups: [{name, items: [{kind, item}]}]`, left out when there are
none, so files written before groups load as they are). New commands:
library.createGroup {library?, name?, items?}, library.renameGroup
{library?, group, name}, library.deleteGroup {library?, group} (its
items stay, ungrouped) and library.moveItem {library?, kind, item,
group?}. An item is in one group at most; deleting an item takes it out
of its group. The panel shows each group as a collapsible section with
its item count after the ungrouped items (rename and delete on its
context menu), each item's context menu has Move to Group (No Group,
the groups, New Group...), and a New Group button sits in the footer.
library.get lists the groups, library.list counts them. The generic
command dialog takes fixed params in `__params` for this.
Export / import: library.export {library?, path?} writes a library
(colours, styles, graphics with art and thumbnails, groups) as a
.vclibrary file, the JSON the library folder keeps (without a path:
{data}); library.import {path | data | dataBase64, name?} adds one as a
new library, made current, numbered on a name clash ("Brand (2)").
The panel menu has Import Library... and Export Library..., through the
app's file dialogs (the web downloads, and picks through File > Open,
which now lists Libraries files). Imported and folder files are made
safe: at most 10,000 items of a kind and 1,000 groups, names clipped and
made unique, sizes finite, thumbnails kept only when their PNG header
says at most 1024 px a side, group entries naming missing items or
unknown kinds dropped; a file over 512 MB or that isn't a library is an
error. Paths go through the engine's file helpers (automation roots).
The importer is fuzzed (import_fuzz: garbage and mutated libraries).
Also fixed: the third graphic added to a library got the second's id
(`graphic-2` twice).
Docs: the commands' params docs, docs/mcp.md, ROADMAP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the Video group to the raster effects, after Sharpen:
- De-Interlace…: Eliminate Odd or Even Fields, Create New Fields by
Duplication or Interpolation. The fields are the rows of the document's
raster grid (Document Raster Effects Settings › Resolution, carried to
the filters as PixelSpace::line) counted from the top of the page, so
the result is the same at any zoom and moves with the page. A line
taken out copies the line above (the one below at the top), or takes
the average of the two.
- NTSC Colors (no options, applied at once): colours a composite
television signal can't carry, with luma plus chroma past 110 % of white
or luma minus chroma below -20 % in YIQ, have their chroma cut to fit,
keeping their luma. Saturated yellow, cyan and pure red lose some
saturation; legal colours stay as they are.
Catalogue rows with parameter docs, dialog dropdowns, menu, translations,
tests (effects, canvas, command sweep junk, menu) and ROADMAP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
File › Place and File › Open read PSD and PSB files: the merged image
every such document carries after its layers, which is what a linked PSD
shows. A placed PSD links like any image, so the Links panel updates and
relinks it, and it is sized by its ResolutionInfo resource.
The reader is written from the public file format specification. It
reads Bitmap, Grayscale, Duotone, Indexed, RGB, CMYK (through the colour
settings' CMYK) and Lab, at 1, 8, 16 and 32 bits per channel (32-bit
values are linear), raw, PackBits or zipped with or without prediction.
A document with transparency stores its merged image on white with the
alpha in the first extra channel. It comes back with straight colours,
also for 16- and 32-bit files, which say so in their Lr16/Lr32 block.
Multichannel files are refused with a message. The file is untrusted:
every read is bounds-checked and the image's size is checked against
the decode budget before anything is allocated.
PSB is a read-only format of its own, so exports never write a PSD under
a .psb name. The desktop packaging still doesn't associate either
extension with VectorCraft (UNASSOCIATED_EXTS): they belong to raster
editors. Layers (import options) are left for later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With both axisMin and axisMax set, the value axis now runs exactly from
one to the other, split into the tick count (5 when automatic). Before,
the override went through the same rounding as the calculated axis, so
asking for 0 to 12000 drew 0 to 12500 and the columns came out shorter
than the data. With only one bound set the axis is still calculated
around the data, as before.
Ticks are now counted from the minimum instead of added up, so the last
tick lands on the maximum (an offset range like 1e10..1e10+1 lost it to
rounding) and a step too small for the range can't loop forever. An
override too wide for f64 used to give a NaN axis and an internal error
in graph.setType ("min > max, or either was NaN"); it now falls back to
the calculated axis.
graph.setType {} also reports axisMin and axisMax.
Behaviour: Change graph value axes, Tick Values > Override Calculated
Values (minimum, maximum and divisions):
https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/visualize-data/change-graph-value-axes.html
Four Photoshop-style raster effects on the pixel-filter pipeline
(vectorcraft_effects::pixel::pixelate), under a new Effect › Pixelate
submenu between Blur and Sharpen in the Raster Effects section:
- Color Halftone (maxRadius 4..127, default 8; channel1..channel4 screen
angles -360..360, defaults 108/162/90/45): each colour channel is
screened at its angle in square cells sized so a dot of Max. Radius
fills one; each cell's dot covers the share of the cell that is the
channel's coverage-weighted mean there (white stays white, black
black). RGB documents screen R, G, B (channels 1 to 3); CMYK documents
screen C, M, Y, K inks (channels 1 to 4), also when they are drawn ink
plane by ink plane (the K plane uses channel 4).
- Crystallize (cellSize 3..300, default 10): every pixel takes the
colour at the nearest of one jittered point per cell (exact Voronoi
over a 5 x 5 neighbourhood, pruned by distance).
- Mezzotint (type: Fine/Medium/Grainy/Coarse Dots, Short/Medium/Long
Lines, Short/Medium/Long Strokes; default Fine Dots; a dropdown in the
dialog): each channel is turned fully on or off against its own random
threshold pattern, so the object becomes pure colours.
- Pointillize (cellSize 3..300, default 5): one dot of random size per
cell at a jittered point, coloured by the softened object there and as
opaque as the object at its centre, stacked in random order over a
white canvas where the object is.
Lengths are in points (Scale Strokes & Effects scales them) and every
pattern lies in document space around the object's centre, so the
result is the same at any zoom or raster resolution, moves with the
object, and is identical on every redraw (hash noise of the pattern's
cell indices, no state). Work per pixel is bounded whatever the
parameters, which are clamped to their ranges and finite-checked; the
halftone's cell grid is capped by the raster's size and a screen finer
than 3 pixels leaves the image as it is (its average). PixelSpace gains
`channels` so the renderer tells the filters what the channels hold.
Catalogue rows with parameter docs, outsets and reaches for cropping,
menu entries, dialog labels (Max. Radius, Channel 1-4, Cell Size), the
Mezzotint Type dropdown, translations, and tests: parameters and
clamping, tone and coverage, CMYK inks, polygon crystals, pure-colour
mezzotints with ten distinct patterns, dots on a white canvas, the same
result at twice the resolution and when moved, the canvas, PDF and SVG
through the engine, the command sweep with junk parameters, the dialogs
and the menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>