Commit Graph
4064 Commits
Author SHA1 Message Date
Taha-Mahmoodi a1a3a99104 Fix #3592: a gradient mesh from a file is budgeted at about a million quads
A mesh read from a file can have any number of patches, and the view any zoom: a 150 x 150 mesh from a 3 MB file exported at 20x asked for about 23 million quads (2 GB), and the command's own cap of 50 rows and columns does not apply to a loaded one. One tessellation now makes at most 1,048,576 quads, the subdivision per patch giving way before that, and a mesh with more patches than that is malformed, like a point count that does not fit.
2026-10-31 19:43:16 +07:00
dotCooCoo b13b3260cd Keyboard Shortcuts: a shortcut on the Turkish Q quotation-mark key shows " and runs
Squashed from #4851 onto main.
2026-10-31 19:43:16 +07:00
dotCooCoo f28de75a22 Shortcuts: an exact Shift binding for the typed character wins over a plain binding at Shift+Comma or Shift+Period 2026-10-31 19:43:16 +07:00
dotCooCoo 26173437b9 i18n: Bosnian paint-chip tips name Shift+, and Shift+.
The Bosnian catalog still carried the Color (,) and Gradient (.) rows, which no longer match the toolbar's tips. It now translates Color (, or Shift+,) and Gradient (. or Shift+.) like the other catalogs.
2026-10-31 19:43:16 +07:00
dotCooCoo 64c776af4c Shortcuts: a bound layout candidate for Shift+Period or Shift+Comma wins over the key position 2026-10-31 19:43:16 +07:00
dotCooCoo 55108efef4 Toolbar: the paint-chip tips name Shift+, and Shift+. instead of < and >
The Color and Gradient tips now read "Color (, or Shift+,)" and
"Gradient (. or Shift+.)". The shortcuts follow the key positions, so on
layouts where Shift at those keys types another character the tips name
the keys to press. The 25 catalogs with these rows and the toolbar
accessibility test follow.
2026-10-31 19:43:16 +07:00
dotCooCoo 021fa4a846 Shortcuts: Shift+Comma and Shift+Period follow their key positions only with Shift alone
The key-position match for Shift+Comma and Shift+Period now applies only
when Shift is the only modifier. Cmd/Ctrl+Shift and Alt+Shift presses at
those keys keep their logical key, so the type-size shortcuts on German-like
layouts behave as before. The typing test also checks that no paint command
runs while text is edited.
2026-10-31 19:43:16 +07:00
dotCooCoo 8363c815ec Perspective: a transform's overflow fallback keeps each entry's powers of two apart
When the conjugate a ∘ self ∘ a⁻¹ overflows, conjugated now forms every
entry of the product as a sum of terms whose factors are split into a
mantissa and a power of two, and scales the whole product by one power of
two from its w row at the end. Scaling each partial product to a largest
entry of 1 could round an entry to 0 that the next factor would restore,
so a projection with entries far apart in size, such as a scale by
(1e160, 1e-160) seen through the same scale, was removed and the art drew
flat.
2026-10-31 19:43:16 +07:00
dotCooCoo c711ad42f0 Perspective: a transform's overflow fallback scales each partial product
When the conjugate of a projection overflows, it is now formed one factor
at a time, and each partial product is divided by its largest entry
magnitude. Scaling the affine factors up front let the w row underflow to
0, so a scale by 2 seen through a move by 1e308 returned None and the
projection was removed, though its conjugate is finite.
2026-10-31 19:43:16 +07:00
dotCooCoo 375b413ba6 Perspective: a projection that a transform overflows keeps a w row of 1
When conjugating a projection overflows, the scaled-down result is now divided by the largest entry in its w row. A valid conjugate is no longer dropped by the determinant cutoff at the arbitrary scale, and every point of a kept projection passes the horizon test in apply instead of none.
2026-10-31 19:43:16 +07:00
dotCooCoo db38e2e114 Perspective: a projection that a transform overflows is scaled down or removed
When moving type or a symbol instance in perspective makes the
conjugated projection overflow, Homography::conjugated now forms the
product from factors scaled to a largest entry of 1 and returns it at
that scale, which maps every point to the same place. When even the
scaled result is not invertible, the transform removes the projection
and the art is drawn flat, instead of keeping the old projection while
the flat art moves.
2026-10-31 19:43:16 +07:00
dotCooCoo 3d345ff95b Geometry: a perspective point that overflows maps to nothing instead of infinity
Homography::apply checked that w is in front of the horizon but not the quotients, so a point with w just above 1e-9 and a large x or y, or a NaN w, mapped to an infinite or NaN point. apply now returns None unless the mapped point is finite. inverse, conjugated and affine_at return None when their result has an entry that is not finite.
2026-10-31 19:43:16 +07:00
dotCooCoo ae76455723 Perspective Selection: a move of loose art keeps its plane when 4 clears the active plane
The move drag remembers the plane it places loose art on. Pressing 4
during the drag, or dragging on after it, previews and commits the move
on that plane instead of dropping it. A 1-3 key pressed before the drag
threshold also sets the plane the move uses.
2026-10-31 19:43:16 +07:00
dotCooCoo c15874ba6e Perspective Selection: Shift constrains each object in its own plane
perspective.move takes constrain: true instead of a plane-space
direction. For each object, the command snaps the move to the nearest
axis or 45-degree diagonal of that object's own plane, so a selection on
several planes keeps every object on a line of its plane. The tool sends
the raw pointer with the flag.

On a diagonal, Snap to Grid now moves along the diagonal by the nearer
per-axis correction, so that edge lands on its gridline.
2026-10-31 19:43:16 +07:00
dotCooCoo 9f64452303 Perspective Selection: Snap to Grid keeps a Shift-constrained move on its axis or diagonal
perspective.move takes an optional constrain: [dx, dy], a plane-space
direction. For an in-plane move, Snap to Grid's correction is projected
onto that direction, so a move the Shift key constrains to a plane axis
or a 45-degree diagonal stays on that line when an object edge lies near
a gridline. The Perspective Selection tool sends the direction for
Shift moves and copies. Moves without the param behave as before.
2026-10-31 19:43:16 +07:00
dotCooCoo f677c60aba Perspective Selection: Shift constrains a move to the plane's axes and diagonals
Holding Shift while dragging objects with the Perspective Selection tool
snaps the move, in the plane's own coordinates, to the nearest plane axis
or 45 degree diagonal. This applies to plain moves and to Alt and
Ctrl+Alt copies. The preview, the final move and the measurement label
use the snapped point. Pressing or releasing Shift, pressing 5 and
pressing 1 to 4 during the drag recompute the preview from the pointer;
a move perpendicular to the plane is not constrained.
2026-10-31 19:43:15 +07:00
dotCooCoo 45b266c805 Appearance: Reduce to Basic Appearance finds the active row in the first selected object
The targets are in paint order, so the first selected object is not always the first target. The active row is now matched against the first selected object wherever it is among them.
2026-10-31 19:43:15 +07:00
dotCooCoo d9fefde061 Appearance: Reduce to Basic Appearance keeps the selected fill or stroke row
Reduce to Basic Appearance kept the topmost visible fill and stroke even
when another fill or stroke row was selected in the Appearance panel.
It now keeps the selected row in place of the topmost one of its kind,
and in each other selected object the row equal to it. With ids, or
with no selected row, it keeps the topmost ones as before.
2026-10-31 19:43:15 +07:00
dotCooCoo 3a7e81e6c3 Appearance: Add New Fill copies the selected fill row right above it
With a fill row selected in the Appearance panel, Add New Fill now
inserts a copy of that row directly above it and selects the new fill.
Rows already above the selected one stay where they are. Before, the
new fill was always a copy of the topmost fill, added at the top.

The selected row belongs to the first selected object. In a mixed
selection, an object that shares that row gets the copy above its own
equal row; an object that does not share it gets a copy of its top
fill on top, as before. With a stroke row selected, or with `ids` or
`target: "contents"`, Add New Fill works as before.

The panel drops rows that were Cmd- or Shift-clicked together when the
active row changes outside the panel, so after Add New Fill (button,
menu or shortcut) only the new fill is selected.
2026-10-31 19:43:15 +07:00
dotCooCoo 65b6606d90 Part of #3229: setting type in a font or style that isn't available warns
Squashed from #4866 onto main.
2026-10-31 19:43:15 +07:00
dotCooCoo 1f31b6ed4a Find Font: Change and Change All leave hidden and locked text alone
Squashed from #4852 onto main.
2026-10-31 19:43:15 +07:00
dotCooCoo 875d3301f5 Transparency: Make Opacity Mask over several layers masks their art
transparency.makeOpacityMask with two or more layers below the mask
object (or a layer and objects of other layers) wrapped the layers
themselves in a new top-level group, so a layer ended up inside a
group. A layer among several masked items now stands for its art: the
masked group takes the layer's visible, unlocked objects, a hidden,
locked or template layer gives none, and the layers stay where they
are. One layer under a mask object, or one layer alone, is masked as
before.
2026-10-31 19:43:15 +07:00
dotCooCoo dd83e063a4 Render: pattern tiles draw undecoded-image marks when the view does
Pattern tiles were drawn with the default render options, so an image
whose pixels cannot be decoded drew nothing inside a pattern fill even
when the render marks undecoded images, as the canvas does. Tiles now
follow the caller's mark_undecoded_images setting, and the tile cache
keys on it.

An opacity mask with such a pattern now treats the mark as part of the
mask, as it does for an undecodable image outside a pattern.
2026-10-31 19:43:15 +07:00
dotCooCoo 8aa8bb4d9d Render: a mask art root, symbol art root or thumbnail keeps its own opacity mask
The renderer drew the root node of an opacity mask's art, of placed symbol
art, of an art brush piece, of an Image Trace source image and of a node
thumbnail without its own opacity mask. Make Opacity Mask uses the top
object as the mask art, so a mask made from an object that already had an
opacity mask ignored that inner mask. These roots now draw through the
same masked group as any other node, still drawn whatever their visibility
and bounds, and an incomplete inner mask still leaves out the outer object.
2026-10-31 19:43:15 +07:00
dotCooCoo b1ed0a512b Brushes: opacity masks on art brush pieces bend with the art
An art, scatter or pattern brush piece whose art carries a linked opacity
mask was bent along the path while the mask's art kept its place in the
brush's own coordinates, so the mask no longer covered the piece. The
mask's art now bends and has its strokes scaled with the object it
masks, on bent runs, on straight runs and where the brush narrows along
the path. A mask whose art cannot bend becomes an empty group. The
anchor budget for a brushed stroke counts the mask's art as well.
2026-10-31 19:43:15 +07:00
dotCooCoo 2a5e2e8588 Blob Brush: the tip preview draws 0% Roundness as thin as the brush
The Tool Options tip preview floored the short axis at 1% of the long one, while the brush draws 0% Roundness at 0.1%. The preview now uses the same 0.1% minimum, with a test that compares it with the tip the brush draws.
2026-10-31 19:43:15 +07:00
dotCooCoo baaffd63e7 Blob Brush: Tool Options set the brush's Angle and Roundness
Blob Brush Tool Options gain Angle (-180 to 180 degrees, 0 by default)
and Roundness (0 to 100 percent, 100 by default), with a preview of the
tip and three dabs; a drag in the preview square sets the Angle. The
elliptical tip keeps its angle on the page along a stroke, a 0 percent
tip is drawn 0.1 percent as wide as it is long, and Size set to
Pressure still varies its size. path.blob takes angle and roundness,
and both values are kept with the tool's options.

While a non-round tip drags, the Blob Brush shows the outline of the
area it covers with its length label, and an Alt drag that smooths
shows the Smooth tool's thin line.
2026-10-31 19:43:15 +07:00
dotCooCoo 958566468a DXF export: dashed polylines continue their linetype around vertices
A polyline with a linetype now sets group 70 bit 128, so the dash pattern runs on through its vertices instead of restarting at each corner. This applies to LWPOLYLINE and the R12 POLYLINE.
2026-10-31 19:43:15 +07:00
dotCooCoo d318a77560 DXF export: Alter Paths for Appearance outlines only strokes a CAD line can't draw
Alter Paths for Appearance wrote every stroke as its filled outline, so a
1 pt circle became two circles and a hatch, and an open arc became a
closed outline and a hatch: two cut lines where a CAM program expects
one. The option now outlines only the strokes a line can't draw: the ones
Preserve Appearance outlines, strokes wider than the widest lineweight
(2.11 mm, to the hundredth), and every stroke in R13 and R14, which have
no lineweights. Other strokes stay CIRCLE, ARC, SPLINE and LWPOLYLINE
entities with their lineweights; R12 keeps them as POLYLINEs with their
width.

A dashed stroke whose offset starts part way into its pattern is now
outlined with Preserve Appearance and with Alter Paths: a linetype always
starts at the start of its pattern, so the dashes were written in the
wrong place.
2026-10-31 19:43:15 +07:00
dotCooCoo d0a903d5d7 DXF export: ellipses and unevenly scaled circles are written as ELLIPSE
A closed path of four cubics that is the four-cubic circle under an
affine map (an ellipse, or a circle scaled out of proportion) is written
as a native ELLIPSE in R13 and later, with its center, major axis and
minor to major ratio. The check maps the path back onto the unit circle
and runs the existing circle recognition on it. R12 keeps the existing
fallback, and ellipses with a ratio under 1e-6 stay splines. The ratio
and end parameter are written in full so that a thin ellipse keeps its
size and a whole turn reads back closed.

Part of #1299.
2026-10-31 19:43:15 +07:00
dotCooCoo 06e8d711ae DXF export: an open path of one straight segment is written as a LINE
An open path with a single straight segment was written as a two-vertex
LWPOLYLINE (POLYLINE before R14). It is now a LINE entity from its start
to its end point. A stroke with a weight in R12, R13 and R14 keeps its
polyline, since a LINE has no width. Closed paths and paths with more
than one segment are unchanged.

Part of #1299.
2026-10-31 19:43:15 +07:00
dotCooCoo 9571ae0a46 Outline Stroke, Expand: hidden and locked objects stay hidden and locked
The art that replaces an outlined or expanded path did not take the
path's hidden, locked or Knockout Shape state. Outline Stroke on a
hidden path, or Expand on a group with a hidden member, made the new art
visible (a filled and stroked path became half visible: the outline
shown, the fill copy hidden), and a locked member's art was unlocked.

The replacement now takes the path's visibility, lock and Knockout Shape
state, and the fill copies and clipping paths inside it are shown and
unlocked, so showing or unlocking the new art shows or unlocks all of it.
This covers Outline Stroke, and Expand's strokes, gradient fills and
pattern fills.
2026-10-31 19:43:15 +07:00
dotCooCoo 9e8e091c86 Find Font: Find leaves out hidden and locked text
select.font (the Find Font dialog's Find button) selected every text
object using the font, including hidden or locked text and text in a
hidden or locked group or layer. It now selects only text that is
visible and unlocked through every group and layer, and not on a locked
artboard. Text on a visible, unlocked template layer is still found. A
font used only by hidden or locked text selects nothing.
2026-10-31 19:43:15 +07:00
dotCooCoo 9dcbe3688e Swatches: the French one-item delete prompt keeps the no-break space before ? 2026-10-31 19:43:15 +07:00
dotCooCoo 541a6c33f0 Swatches: the Ukrainian, Russian and Bosnian delete prompt for 21 or 31 items names swatches and groups
In these languages a count ending in 1 (other than 11) takes the singular form, which read as one swatch or one group. That form now names swatches and groups with the count in parentheses.
2026-10-31 19:43:15 +07:00
dotCooCoo ab2426ca15 Swatches: the delete prompt for several swatches uses each language's plural forms
The confirmation for deleting several swatches and groups now goes
through i18n::tn, as the Graphic Styles prompt does, and each catalog
gives the count-dependent forms in an @plural row.
2026-10-31 19:43:15 +07:00
dotCooCoo 37211078c2 Fix #4837: the saved color scheme, accent and canvas color show at launch
The first UI pass installed the default theme over the one the saved
preferences had applied earlier in the same frame, and later frames saw
the preferences as already applied. Classic, a non-default accent and a
Canvas Color showed as the defaults until a preference changed, and again
after the app took a new graphics context. The first pass now applies the
saved preferences.
2026-10-31 19:43:14 +07:00
dotCooCoo 7860931a3b Select Same: Fill Color from a Live Paint face stays in its Live Paint group
Select > Same > Fill Color and Fill & Stroke from a face of a Live Paint
group now select the matching faces of that group only, and from any
other object they select no Live Paint face. Before, they matched faces
of every Live Paint group and paths outside them alike.
2026-10-31 19:43:14 +07:00
dotCooCoo 9163c22396 Fix #4819: Convert to Smooth and Corner convert a selected blend's spine
With a blend selected, the Contextual Task Bar's Corner and Smooth
buttons failed with "select paths or anchor points", since
`path.convertAnchors` only read paths. They now convert every point of
the blend's spine, as they convert every anchor of a path selected
whole, in one undo step. The key objects stay where they are.

With Direct Selection, the Control bar and the Properties panel now
show their Convert buttons for a selected blend too.
2026-10-31 19:43:14 +07:00
dotCooCoo 4e785b40ce Part of #4516: Windows renders start workers only for the cores the process may use
The renderer sized its worker pool from available_parallelism, which on
Windows counts every processor in the machine, also when the process's
affinity mask allows fewer. A process allowed one core started 8 workers,
and the renderer's flush spun on that core while the workers got no time,
so renders crawled or stalled.

The desktop app and vectorcraft-cli now read the affinity mask at startup
(src/windows_cores.rs, shared) and pass the count to the renderer with
vectorcraft_render::set_usable_cores. With one usable core the renderer
renders on the calling thread.
2026-10-31 18:44:55 +07:00
dotCooCoo 663e691908 Fix #4864: a search pick of an Asset Export command opens the panel
The command palette runs a picked command with no params. Export Assets
then failed with "no assets yet" (or, with assets collected, exported
to no folder), and Remove, Rename and Asset Export Settings failed or
did nothing. Run with no params from the palette, a menu or a shortcut,
these four commands now show the Asset Export panel, and leave it open
when it already shows, docked or floating. Calls with params are
unchanged.
2026-10-31 18:44:53 +07:00
dotCooCoo 2d9ceb8137 Shortcuts: rewrap the typing_presses doc comment 2026-10-31 18:44:50 +07:00
dotCooCoo d3ea958f8f Shortcuts: a modifier key press is never the key that typed a text
In the web build, pressing Shift and then a letter in one frame sends the Shift press, the
letter's text and then the letter's key press. The text paired with the Shift press as the press
before it, so a key-position shortcut looked up Shift instead of the letter, and a held Shift's
repeated presses dropped the text as a repeat. Modifier key presses now never pair with a text.
2026-10-31 18:44:50 +07:00
dotCooCoo 0127739597 Shortcuts: a text with no key press of its own leaves the next keystroke's press to that keystroke's text
A text-only event right before a desktop keystroke in the same frame ([text, key, text]) took the key press as the press after it, which left the keystroke's own text with no key. A text now takes the press after it only when every later text in the same run of alternating texts and presses has a press after it too, as in the web build's [text, key] order; otherwise the press goes to the text after it.
2026-10-31 18:44:50 +07:00
dotCooCoo 8579aa07e3 Shortcuts: in the web build a key-position shortcut uses the key of the same press
eframe's web build sends a keystroke's text before its key press. On a
layout without Latin letters, the one-key shortcuts paired each text with
the last key press before it, so in the web build a lone keystroke had no
key and ran no shortcut, and a second keystroke in the same frame used
the first one's key.

Each text now takes the press right before it, unless an earlier text in
the frame took that press, and otherwise the press right after it. The
one-key shortcuts and the shortcut names a tool sees while it is busy use
the same pairing. The desktop order (key, then text) pairs as before.
2026-10-31 18:44:50 +07:00
Dylan M. Taylor cf0d4d4a3f Merge pull request #4807 from aiongg/docs-tidy
docs: tidy agent, contributor and asset docs
2026-10-31 01:06:55 -04:00
aiongg 91e97c92dd updates: show check results with craft-update's CheckResultDialog 2026-10-31 04:42:43 +00:00
aiongg 8ae1f7dae6 docs: tidy agent, contributor and asset docs 2026-10-31 04:17:23 +00:00
bflatastic 41508e7b24 Path Precision Tools: the Shared Edges fields read like the others in the panel 2026-10-31 07:47:55 +07:00
dotCooCoo 44bac0d7cf Render: leave out an object whose opacity mask lost an image
With undecoded-image marks off, an image in an opacity mask's art that
failed to draw left a hole in the mask. Without Clip, the area outside
the mask art counts as white, so the masked object showed in full.

The renderer now notes an image that fails to draw while mask art is
drawn and leaves the masked object out, as PDF export does since #4765.
This covers images in pattern tiles and in cached effect and shadow
rasters, which remember the failure and report it again when they are
reused. Nested masks pass the failure outward, and View Opacity Mask
shows no coverage for such a mask. An image still waiting to be decoded,
or drawn with the undecoded-image mark, keeps today's behavior.
2026-10-31 07:33:49 +07:00