Commit Graph
884 Commits
Author SHA1 Message Date
BFlat e41ef1ea62 Merge pull request #7148 from dotCooCoo/fix/blend-without-spine-widget
Blend: a blend without a spine shows the Appearance widget
2026-11-14 00:51:09 +07:00
BFlat d58452f287 Merge pull request #7167 from dotCooCoo/fix/image-icc-16bit-kept
Images: an image stored again keeps its ICC profile and 16-bit samples
2026-11-14 00:49:03 +07:00
BFlat 6a1b4c42de Merge pull request #7040 from dotCooCoo/fix/nested-link-ids
Links: list and manage linked art in symbols, graph designs, patterns and masks by id
2026-11-13 23:58:20 +07:00
dotCooCoo 9b027bb43a Merge main into fix/image-icc-16bit-kept 2026-11-13 08:25:53 -08:00
BFlat 9395fb9cec Merge pull request #7160 from dotCooCoo/fix/svg-image-orientation
SVG import: raster images follow their EXIF or TIFF orientation
2026-11-13 23:16:49 +07:00
BFlat 43d0b50760 Merge pull request #7157 from dotCooCoo/fix/remove-anchors-one-pass
Paths: Remove Anchor Points removes many anchors of a subpath in one pass
2026-11-13 23:15:10 +07:00
dotCooCoo e13384ebb4 Merge main into fix/blend-without-spine-widget 2026-11-13 08:06:40 -08:00
BFlat e0e97e8ed1 Merge pull request #7153 from dotCooCoo/fix/blend-spine-binary-search
Blend: find the spine segment for an arc length with a binary search
2026-11-13 23:04:50 +07:00
BFlat b17a62be34 Merge pull request #7109 from dotCooCoo/fix/painted-fx-brush-reach
Effects: culling a painted piece leaves room for its own brush art
2026-11-13 23:01:55 +07:00
dotCooCoo d7740ff587 Images: an image stored again keeps its ICC profile and 16-bit samples
Open and Place store some images again instead of keeping their bytes:
a photo turned by its orientation, a TIFF the image decoder reads, and
a CMYK TIFF with one plane per ink. Those images lost the ICC profile
their file embeds, and 16-bit samples were cut to 8 bits.

- raster_image keeps gray and RGB samples at 8 or 16 bits in the
  stored PNG, with the gray or RGB profile in its iCCP chunk. A
  profile of another color space, such as the CMYK profile of a CMYK
  image read as RGB, is left out. A TIFF's profile is read with the
  tiff decoder, which reads it whatever the image's size.
- stored_cmyk_tiff and turned_cmyk write CMYK8 or CMYK16 as the file
  has it, with the CMYK profile as TIFF tag 34675 (UNDEFINED). A CMYK
  JPEG's profile is put together from all its APP2 segments.
- The docs/target-app-parity.md line on EXIF orientation says 8 or 16
  bits, and a line after it describes the kept profile.
2026-11-13 07:19:15 -08:00
dotCooCoo 4fae51023e Paths: Remove Anchor Points removes many anchors of a subpath in one pass
Object > Path > Remove Anchor Points removed each selected anchor with
its own Vec::remove, shifting the rest of the subpath every time, so
removing k anchors from an n-anchor subpath took O(k*n) moves.

- pathops: remove_anchors removes a subpath's anchors the last first,
  reading each anchor's current neighbors, handles, length and closed
  state as removing them one at a time does, and compacts the anchor
  list once. remove_anchor and remove_anchors share one refit, so the
  result is bit for bit the same.
- engine: the command groups the selected anchors by subpath, works out
  the next selected anchor with each subpath reading only its own
  selected anchors, and takes emptied paths out of the selection
  together (Selection::remove_all).
- Tests compare remove_anchors bit for bit with remove_anchor over 600
  generated subpaths and focused cases, count the anchors moved (2001
  anchors, every other one removed: at most one move per anchor left,
  500,500 one at a time), and compare the next-anchor selection and
  the command on many paths and subpaths with removing one at a time.
2026-11-13 06:54:55 -08:00
dotCooCoo 1ce6431a99 SVG import: raster images follow their EXIF or TIFF orientation
An <image> whose EXIF orientation or TIFF Orientation tag turns or flips
its pixels, linked or as a data URL, is turned before usvg reads it: the
pixels are stored as they are shown, the width and height swap, and the
box fits the turned image under preserveAspectRatio, as CSS
image-orientation: from-image describes. A turned CMYK JPEG or TIFF keeps
its inks as a CMYK TIFF, as does a CMYK TIFF that is not turned.

- Data URLs of type image/tiff, and untyped TIFF bytes, are read.
- TIFFs with extra channels, CCITT Group 3 TIFFs and planar CMYK TIFFs
  are read as Open and Place read them.
- Converted images decode within the decode allowance, and a TIFF that
  claims more strips or tiles than the decoder can count is refused
  before any decoder opens it.
- exif_orientation moves to vectorcraft-doc so Open/Place and SVG import
  share it.

Tests: crates/svg/tests/it/image_orientation.rs.
2026-11-13 06:53:42 -08:00
dotCooCoo 666084fb81 Blend: find the spine segment for an arc length with a binary search
Spine::at_length walked the first subpath's segments from the start
until one ended at or past the arc length, so placing every blend step,
spread key, blend widget and Objects on Path attachment cost one
comparison per earlier segment. It now searches the anchors' arc
lengths, which never decrease, and picks the same segment: the first
whose end is at or past the length, so an exact anchor length and
repeated lengths from zero-length segments keep their segment. The
point and tangent evaluation is unchanged.

Tests compare the search with the old walk on open and closed spines,
one segment, curves, zero-length segments, every anchor, both ends,
out-of-range and non-finite lengths, and check that a 4,096-segment
spine needs at most 13 comparisons.
2026-11-13 06:46:06 -08:00
jmvergesandClaude Opus 5.5 e11155c692 Envelopes: a top object's split goes by the longer of handles or bends
The previous commit took the spans from how far each piece bends away
from the split whenever both handles there were within ONE_CURVE of the
anchor. A curve that barely moves at the split but doesn't bend there
(a straight one, say) has short handles and no bend, so that gave 0/0
and the side fell back to the polygon shares. Now the spans come from
whichever pair is the longer: the handles, or the bends (which go with
the squares of the spans). The check of every piece against the
rebuilt curve is unchanged.

Test: a_split_curve_keeps_the_top_object_patch_where_it_barely_moves_and_runs_straight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 14:42:07 +00:00
dotCooCoo fc70b08f71 Blend: a blend without a spine shows the Appearance widget
A blend made with Create Spine set to Never now shows the Appearance
widget with the Selection and Blend tools, so its color Shift can be
dragged on the canvas as on a blend with a spine. The Easing widget
stays hidden, since easing needs a spine.

The widget sits 16 px above the halfway point, by length, of the
straight lines between the key centers that the steps are laid out
along, and a drag follows those lines' direction there. The lines are
not drawn. `blend_layout_line` exposes that line; `blend_spine` still
returns `None` for such a blend.
2026-11-13 06:17:47 -08:00
jmvergesandClaude Opus 5.5 6ed3935e59 Envelopes: a top object's side split at a cusp still follows its curve
Split where the curve stops (a cusp), both handles at the split lie on
the anchor, so their lengths say nothing of how the curve's parameter
divides between the two pieces, and the side fell back to the polygon
shares. When both handles lie within ONE_CURVE of the anchor, the
spans now come from how far each piece bends away from it (the second
derivatives, in proportion to the squares of the spans). The existing
check of every piece against the rebuilt curve is unchanged.

Test: a_split_curve_keeps_the_top_object_patch_when_split_at_a_cusp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 12:43:47 +00:00
dotCooCoo 88c02aa293 Effects: culling a painted piece leaves room for its own brush art
A live-effect plug-in that paints its pieces draws each piece's brushed
stroke along the piece's path at the piece's stroke width. Culling left
room for brush art from the object's own stroke width around the
pieces, so brush art of a moved or wider piece was cut off at the edge
of the view. Culling now leaves 16 x the piece's stroke width + 8 pt
past each brushed piece, and does so for a symbol instance whose art
holds such a path (measured once per symbol) and for a blend, envelope
or repeat that holds one. Object > Rasterize measures the brush art
drawn along each piece and leaves out the object's own brush art, which
is not drawn.

Groups and layers now leave room for their members' brush art. The
cull bounds of a group or layer united its members' bounds without the
room each brushed member leaves, so the painted bounds of a group, and
the area redrawn when it changes, left out brush art past its members'
strokes. That applied to plain brushed paths as well as painted pieces.
Each member is now measured as the renderer measures it when it draws
the group, in the members of a group with a fill, stroke or adjustment
of its own and in a group with crop marks or Vector Halftone too.
2026-11-13 04:31:38 -08:00
dotCooCoo 5e33ab2a61 Merge main into fix/nested-link-ids 2026-11-13 04:06:39 -08:00
jmvergesandClaude Opus 5.5 728fd9ef14 Envelopes: a top object's side that is one split curve follows its parameter
A top-object envelope gives each piece of a side a share of the content
in proportion to its control polygon's length. When anchors split a
curve without changing its shape, the pieces' polygons don't divide the
side where the curve's own parameter does, so the envelope bent its
contents differently although its outline was the same: Add Anchor
Points on a top object moved the art, and a top object whose .ai mesh
saves each side's curve split into thirds drew its type up to about
half a point off its drawn art, which follows the whole curve's
parameter.

Coons::from_top_object now checks whether a side's pieces, from corner
to corner, are all pieces of one cubic. The pieces' spans of that
curve's parameter come from the handle lengths either side of each
split, the curve from the outer ends and handles, and every piece must
lie within 0.02 pt of the curve's piece over its span (each control
point). Such a side runs along that curve, by its parameter. Any other
side keeps the polygon shares, including a side only partly made of one
curve and a smooth side drawn in many pieces that isn't one curve as a
whole. Corners found from the outline can still move when anchors are
added, as before. Gradient meshes made from a path are unchanged.

Existing top-object envelopes with such sides redraw.
a_split_curve_moves_the_top_object_share_to_its_new_anchor, which
pinned the earlier behaviour, becomes a_split_curve_keeps_the_top_object_patch.

Tests: a_split_curve_keeps_the_top_object_patch,
a_top_object_joins_a_split_curve_saved_with_rounded_coordinates,
a_top_object_keeps_smoothly_joined_curves_apart,
a_top_object_side_partly_of_one_curve_keeps_the_polygon_shares and
a_top_object_side_of_many_arcs_stays_on_them (vectorcraft-doc),
envelope_top_object_keeps_its_look_when_anchors_are_added
(vectorcraft-engine) and
a_top_object_whose_top_is_one_curve_split_in_thirds_opens_live
(vectorcraft-eps).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 11:18:40 +00:00
BFlat 46be3af7f8 Merge pull request #7064 from dotCooCoo/feat/envelope-warp-points
Envelopes: the Mesh tool and Direct Selection edit a warp envelope's points
2026-11-13 15:04:18 +07:00
BFlat dd8446f3a4 Merge pull request #7054 from dotCooCoo/feat/nine-slice-guides-outside-art
Symbols: 9-slice guides may lie outside the symbol's art
2026-11-13 15:03:59 +07:00
BFlat 8998d94cc8 Merge pull request #7057 from dotCooCoo/feat/paste-remembers-sublayers
Layers: Paste Remembers Layers puts art back into the sublayer it was copied from
2026-11-13 14:18:03 +07:00
dotCooCoo 8914d079f4 Envelopes: a warp grid kept before type's measure is registered is built again
The kept grid now records whether a type measure was registered, so a
grid built before registration is not reused when the registered inputs
number happens to be 0.
2026-11-12 22:36:02 -08:00
BFlat 9b7aa561c0 Merge pull request #7036 from dotCooCoo/fix/pattern-cancel-dangling
Patterns: canceling a new pattern leaves no swatch or paint of it
2026-11-13 13:18:51 +07:00
dotCooCoo ffc21b702f Symbols: same-edge 9-slice art shrinks when the instance is smaller
With both guides of an axis past the same edge, the art keeps its size
from the other edge of the instance, or shrinks to the instance's width
or height when the instance is smaller than the art. The module doc,
AxisMap::new and the docs/target-app-parity.md entry now say so.
2026-11-12 21:44:36 -08:00
dotCooCoo 4316f819a8 Envelopes: the Mesh tool and Direct Selection edit a warp envelope's points
A selected warp envelope now shows the points of its surface on a 4x4
grid (8x8 or 16x16 where 4x4 strays more than 0.1% of the warp's
diagonal from it). object.mesh.movePoint, movePoints, addLine,
deletePoint and object.envelope.setMeshPoint accept a warp: the first
edit turns it into a mesh envelope on that grid and applies the edit as
one undo step, so Undo or Escape gives the warp back with its style and
bend. An edit that moves no point leaves the warp and adds no undo
step. A warp that no such grid follows (some version 0 maps) shows no
points and its edits fail without changing anything.

movePoints and setPointColor on a gradient mesh record no undo step
when they change no point.
2026-11-12 21:37:21 -08:00
dotCooCoo 7b6b6af7b1 Type: the Mojikumi Set menu offers the predefined spacing sets
The Paragraph panel's Mojikumi Set lists None, YakumonoHankaku,
GyomatsuYakumonoHankaku, GyomatsuYakumonoZenkaku and YakumonoZenkaku,
then Line-end Punctuation Half Width, and text.setFormat {mojikumi}
takes yakumonoHankaku, gyomatsuYakumonoHankaku, gyomatsuYakumonoZenkaku
and yakumonoZenkaku besides none and lineEndHalf.

The spacing of each set is VectorCraft's own rule, built on the help
page's one-line description of each set:
- YakumonoHankaku sets every bracket, comma and full stop half width,
  takes a quarter em off each side of a middle dot, and puts a quarter
  em between Japanese and Latin letters or digits.
- GyomatsuYakumonoHankaku keeps punctuation full width inside a line,
  sets an opening bracket starting a line flush and a closing mark
  ending it half width, and takes the quarter em after a middle dot
  ending a line off.
- GyomatsuYakumonoZenkaku keeps punctuation full width and adds the
  quarter em between Japanese and Latin text.
- YakumonoZenkaku sets characters as None does.

Both composers and vertical type measure each line as it is set. A
right-to-left paragraph, whose line ends are measured untrimmed, also
draws them untrimmed, and a right-to-left line that ends between a
Japanese and a Latin character is measured without the quarter em the
set adds there. With a Desired glyph scaling other than 100%, the
space a set takes off before an opening bracket or middle dot, and the
Japanese-Latin space recorded at a line's end, scale with the glyph.

The sets save with the document and paragraph styles; new type and
Reset Panel keep Line-end Punctuation Half Width. SVG export lays type
with middle dots out again for its positioned spans. A .ai save
reports a paragraph with one of the new sets as left out.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/type-parity.md,
docs/gaps.md.
2026-11-12 21:07:44 -08:00
dotCooCoo d8b6f4cd02 Layers: Paste Remembers Layers puts art back into the sublayer it was copied from
Copy now remembers the whole chain of layers around each object, not only
the name of the nearest one. With Paste Remembers Layers on:

- In the same document, art goes back into the sublayer it came from while
  that sublayer exists, even after it was moved, so two sublayers of the
  same name in different layers stay apart. A deleted sublayer is made
  again inside the layer that held it.
- In another document, the sublayer and the layers around it are matched
  by name from the top level down; of repeated names, the topmost chain
  that matches the most names wins, a shown and unlocked one first. A
  missing sublayer is made inside its layer, and a missing layer on top
  with the sublayer inside it, in the paste's undo step.
- When the matched layer, or a layer around it, is hidden or locked, the
  paste fails with "the target layer is locked or hidden" and nothing is
  pasted, instead of putting art in a hidden layer or making a second
  layer of the same name.
2026-11-12 21:02:59 -08:00
dotCooCoo 26182f6f80 Merge main into fix/nested-link-ids 2026-11-12 20:48:49 -08:00
dotCooCoo 47fbe1c14a Patterns: Delete, Cancel and rename reach a graph's stored paints
The series fill and stroke and the label fill and stroke a graph keeps
for its next redraw are now in the paint traversal, so Delete Pattern
and canceling a new pattern make them no paint and a Pattern Options
rename renames them. Before, a later graph.setData drew the paint of a
missing pattern again. Global swatch edits reach the same paints.

The progress-doc line now says that only canceling a new pattern clears
its paints; canceling the edit of an existing pattern puts the
definition, and after a rename the old name, back.
2026-11-12 20:43:38 -08:00
dotCooCoo 26ab0f2523 Symbols: 9-slice guides may lie outside the symbol's art
A 9-slice guide can now be placed outside the symbol's art, by dragging
it past the art's edge in symbol editing mode, by symbol.options
nineSliceGuides, or by a Symbol Options field below 0% or above 100%.
A scaled instance then scales by the cells its art falls in.

- Each guide is a fraction from its edge in -16..16, and the two guides
  of an axis keep their order (left + right < 1, top + bottom < 1).
- A guide past an edge cuts the art at that edge, so that side's outer
  cell holds no art. With both guides of an axis past opposite edges,
  the art scales as a whole along it. With both past the same edge, the
  art keeps its size in the outer cell at the other edge.
- Symbol Options shows the guides with three decimals, and OK keeps a
  field that was not changed at its exact value.
- Native files keep the guides exactly. SVG and .ai write such an
  instance as its placed art, without guides.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/gaps.md.
2026-11-12 20:40:05 -08:00
BFlat f062dd7ed2 Merge pull request #6932 from dotCooCoo/feat/dcs-linked-spot-plates
Place: DCS files link with their spot plates as spot swatches and separate on their own inks
2026-11-13 11:27:13 +07:00
dotCooCoo 21dbcf6307 Links: list and manage linked art in symbols, graph designs, patterns and masks by id
Linked images and placed documents kept only in symbols, graph designs,
pattern swatches or opacity masks now have ids of their own. links.list
lists them after the layers' own, one row per stored object, with an
owner naming what holds them; links.check, the missing, modified and
updated files reported on open, the link watch and links.findFiles
report them by id; relink (All Instances included), update, embed,
placementOptions, info and image.info act on them. links.goTo on one
clears the selection and returns onCanvas: false with its owner, and the
Links panel says the link has no place on the canvas.

- Document::visit_stored, stored_node and stored_node_mut find objects
  in every stored art. fix_next_id counts their ids and gives an object
  there whose id is 0, past the id range or already used a new one,
  updating Objects on Path references to renamed children.
- Copies of masked objects give the mask art new ids, and symbols and
  patterns that placed art brings from another document take ids of the
  document they join.
- The Links panel picks such rows in the panel itself, drops the pick
  when the canvas selection changes, and draws their thumbnails from a
  lookup made once per list refresh. Select by Link Status leaves them
  out, as it cannot select them.
2026-11-12 20:16:54 -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
BFlat efc3c9491a Merge pull request #7018 from dotCooCoo/fix/followup-6991
Recolor Artwork: the Color Theme Picker keeps symbol art and clipped image reads within their limits
2026-11-13 10:26:37 +07:00
BFlat 703fb03d65 Merge pull request #7006 from dotCooCoo/fix/mesh-locate-winding
Mesh: a point on a mesh's outline locates on its edge on every side
2026-11-13 10:26:18 +07:00
dotCooCoo f6d8af3ed6 Merge main into feat/dcs-linked-spot-plates 2026-11-12 19:09:51 -08:00
dotCooCoo 99f896f9b8 Place: DCS links follow their plate files, clips and document swatches
- The link watch and links.updateChanged look at a DCS file's plate files
  of their own, so a changed plate file is read again.
- links.update and links.relink apply the file's clipping path to the
  images in symbols, graph designs, patterns and masks too. When the new
  file has no clipping path, a clip group holding the old file's clipping
  path loses that path and stays as a plain group of the image, keeping
  its id, name, visibility and transparency. The image keeps that path with its plates to tell it from a
  clipping mask made in the document; a clipping path given another fill
  rule in the document stays. An even-odd clipping path in the file is
  kept as the same area under the nonzero rule.
- links.update and links.relink return the swatch warnings file.place
  gives, including two files that add one ink in two colors.
- links.relink with copy fails for a DCS file with plate files of its own.
- file.place answers the image id of a clipped DCS placement as image,
  and checks page and crop for a DCS file as it does for an EPS file.
- Print separations and Separations Preview take a spot plate's color
  from the document's swatch of its ink, and a reduced image's plates
  keep their coverage.
- Show DCS Transparency Interactions counts a transparent DCS image and
  characters filled or stroked with a transparent gradient.
- %%PlateFile names keep their spaces, and header bytes that are not
  UTF-8 are read as Latin-1.
- A plate program that draws more than one image, art besides its image,
  or its image through a clipping path is an error naming the plate
  instead of printing the wrong ink.
- Nested clipping paths around the composite clip the image together.
- A kept image with more plates than are read, or more TIFF directories
  than its composite and plates, is the CMYK image of its composite
  only, not a set of plates cut short.
- swatch.unused counts a spot or process swatch as used only while an
  image the art shows has a spot plate of its name.
2026-11-12 18:43:04 -08:00
dotCooCoo 25c1a56755 Patterns: canceling a new pattern leaves no swatch or paint of it
Cancel on a pattern that Make created for the edit now removes every
swatch of it, a color group's included, and turns every paint of it
left in the document into no paint: art outside the editing layer,
opacity mask art, type runs, symbol definitions, graph designs, other
patterns' tiles (a copy Save a Copy made included), graphic and
character styles, brush art, and the fill, stroke and appearance new
art takes. Delete Pattern clears its paints in the same places, and a
Pattern Options rename renames them there; both reached only the
layers before. Delete's `unpainted` counts the document paints it
cleared.

Cancel after a Pattern Options rename during the edit gives the
pattern its old name back with its swatches and paints, unless another
pattern, swatch or color group has taken that name; before, the
definition went back to the old name while its swatches and paints
kept the new one.

Undo and Redo restore new art's paints with the document, including
the appearance inherited from the selection with New Art Has Basic
Appearance off. A rename preview that is replaced or canceled starts
again from the paints the interaction began with, and a canceled
preview takes the selection's appearance again.
2026-11-12 17:42:59 -08:00
dotCooCoo 93155b0e3f Recolor Artwork: the Color Theme Picker keeps symbol art and clipped image reads within their limits
The symbol instances the picker reads now share one limit of 16,000,000
placed objects, charged through charge_symbol_art and counted as the
renderer counts them, so deeply nested or heavily repeated instances
stop there. An instance whose art would pass the limit is left out with
its own paints, as the canvas draws none of it. Pattern tiles carry the
count in and out, and their art is read one level deeper than the
object the pattern fills, so a chain of patterns shares the depth limit
with the art around it; a pattern cut short there is read again where
it is used less deep.

The pixels read inside clip groups are now tested against at most
4,000,000 clipping-path elements in all. The clipped reads of one image
share the work, a read outside clip groups keeps its own pixel budget,
and when the work left can't test a pixel of every clipped read, the
reads that fit are kept in order and the rest are left out.
ImageBlob::sample_pixels takes a pixel budget for each pick.
2026-11-12 17:40:51 -08:00
dotCooCoo df155042d4 Merge main into feat/image-color-mask 2026-11-12 17:40:13 -08:00
BFlat f7a03f7899 Merge pull request #6991 from dotCooCoo/feat/recolor-theme-picker
Recolor Artwork: the Color Theme Picker picks new colors from the art on the canvas
2026-11-13 08:31:13 +07:00
dotCooCoo 13a4d271d4 Mesh: locating a point searches only the edges near it
locate_within measured every boundary edge of the mesh at full accuracy
for each lookup, including points well inside the mesh. It now skips an
edge whose control points all lie farther than the distance it is
looking for: a millionth of a point for the outline, or the tolerance
for a point outside.
2026-11-12 17:24:18 -08:00
dotCooCoo 61f170a761 Mesh: a point on a mesh's outline locates on its edge on every side
A point exactly on a gradient mesh's outline located on the top and left
edges but not on the bottom and right ones, where the nonzero winding
number is 0. object.mesh.addLine and Create Gradient Mesh at a point there
reported the point outside the mesh, and the Eyedropper did not sample it,
while the hit test selects the mesh there.

locate_within now takes the nearest point of the edge for a point within
a millionth of a point of the outline, with any tolerance, and returns
nothing for a point that is not finite. A point just outside still needs
a tolerance.
2026-11-12 16:56:22 -08:00
dotCooCoo 632830947a Merge main into feat/recolor-theme-picker
Xf::new is test-only on main now: the Color Theme Picker's canvas layer builds its view transform with Xf::at and the canvas's interface zoom.
2026-11-12 15:54:28 -08:00
dotCooCoo 31b669a0a1 Recolor Artwork: the Color Theme Picker picks new colors from the art on the canvas
The Color Theme Picker button beside the Assign and Edit tabs turns on
picking from the canvas. A click takes the color palette of the object a
Selection tool click takes there, a drag takes the palette of the art
inside its marquee, and Shift adds more picks. The rows that are not
excluded take the palette as their new colors and the art previews them;
OK keeps the recolor as one undo step.

- recolor.pickTheme {picks, colors?} returns the palette of the art
  picked: visible fills and strokes, gradient stops, text, mesh points,
  pattern tiles, symbol art, colorize colors and image pixels. Clip
  groups, clipping opacity masks and isolation mode limit it to what
  shows, and it changes nothing.
- Each object read weighs the same; similar colors merge (k-means in Lab)
  into at most `colors`, most used first. Image reads are bounded (65,536
  pixels a pick, 1,048,576 pixels and 64 images in all), and past 4,096
  distinct colors, close colors merge before clustering.
- The dialog's themePicker and themePicks fields drive it from
  ui.dialog.set. A new Colors count keeps the picks.
2026-11-12 15:16:23 -08:00
dotCooCoo 3c10f9bf71 Merge main into feat/cjk-insert-aki 2026-11-12 14:28:45 -08: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
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