Commit Graph

246 Commits

Author SHA1 Message Date
bflatastic b2ececc7b4 M3.59: tints of global and spot colours, tint swatches and Global colour balance
A colour linked to a global or spot swatch keeps a tint (Paint::Solid.tint, 1 by default and
left out of files at 100 %): paint.setFill/setStroke {swatch, tint} apply the swatch at that
percentage, swatch edits recolour every linked colour at its own tint, and Color::tinted
resolves it (CMYK and Gray inks scale, RGB mixes toward white). swatch.new saves a tint as a
tint swatch ("Ink 40%") linked to its base. Spot tints separate as that percentage of their
plate (Separations Preview, PDF Separation value) instead of an ink-ratio guess. Adjust Color
Balance's Global mode (tint) shifts the tints of linked colours. The Color panel shows Tint
mode (the swatch chip, its name and a T slider) for linked colours; the Swatches panel
highlights the tint swatch in use. Gradient stops get the same link and tint fields (the
swatch walks cover them), so GradientStop is no longer Copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 02:29:17 +07:00
BFlat e94748dc17
Merge pull request #43 from storytold/pr/m3-targeting-expand-appearance
M3: target circles and full Expand Appearance (M3.69, M3.70)
2026-10-05 02:20:06 +07:00
bflatastic b4ba4ecf0a M3.70: Expand Appearance turns fills, strokes, effects and brushes into objects
effect.expandAppearance now expands the whole appearance: each visible fill becomes a
path of its own and each stroke its outline (a brushed stroke its brush art), grouped
in paint order under the object's id and transparency; geometry effects are baked and
raster effects become an embedded image (shadows below the art, blur/feather/inner glow
replacing it). Type with effects or its own fills is outlined, and a group's or layer's
own fills and strokes are expanded among its members. The command is enabled whenever
an appearance isn't basic. Builds on bake_leaf (effects::expand_leaf / expand_art) and
shares the raster-effect image code with PDF export.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 02:16:02 +07:00
bflatastic b768b4a53b M3.69: target circles target layers, groups and objects; drag them to move appearances
Clicking a Layers panel target circle runs `layer.target {id}`: a layer gets its art
selected and becomes the target (`Selection.target`) of the appearance, effect,
transparency and opacity-mask commands, which then edit the layer itself; the
Appearance panel lists it. Dragging a circle onto another runs
`appearance.transfer {source, target, copy?}` (Alt copies), dropping it on the trash
clears it. Filled dots also mark groups and layers with fills of their own, masked
objects get a dashed underline, and while an opacity mask is edited the panel lists
only an `<Opacity Mask>` entry and the tab says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 02:15:41 +07:00
BFlat a2adf729a7
Merge pull request #42 from storytold/pr/m3-proxies-eyedropper
M3: Fill/Stroke chip popovers, panel shortcuts, Eyedropper Options and modes (M3.51, M3.52)
2026-10-05 02:15:06 +07:00
BFlat 6a9b658443
Merge pull request #41 from storytold/pr/custom-titlebar-dragon-icon
UI: custom title bar on Windows and Linux; the dragon icon as the brand mark
2026-10-05 02:14:15 +07:00
bflatastic 303689c8f4 M3.52: Eyedropper Options with Picks Up / Applies trees, Alt and Shift+Alt modes, image sampling
Double-clicking the Eyedropper opens Eyedropper Options (dialog
eyedropperOptions): raster sample size (point, 3x3, 5x5) and Picks Up /
Applies trees (transparency; focal fill colour, transparency, overprint;
focal stroke colour, transparency, overprint, weight, cap, join, miter,
dash; character and paragraph attributes). The options live in the
preferences (Prefs::eyedropper, eyedropper.setOptions, prefs key
eyedropper) and replace the earlier appearance/transparency switches.

appearance.copyFrom takes pickUp/apply overrides, reverse (Alt-click:
the selection's attributes onto the clicked object) and append
(Shift+Alt-click: add the source's fills and strokes). Type copies font,
size and paragraph attributes to type. Clicking an image samples its
pixels; sampled colours keep their colour model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 02:08:10 +07:00
bflatastic 2e6662c93b M3.51: Fill/Stroke chips open swatch and mixer popovers; panel shortcuts; Recolor quick action
The Control bar and Properties Fill/Stroke chips show the proxies' paints
("?" for mixed selections) and open a popover with the Swatches panel body
(Shift-click or the toggle: the Color panel's mixer); a click brings the
chip's proxy forward through paint.toggleActive {fill}. The Properties
panel offers Recolor for multi-colour selections. Window panel shortcuts
(Color F6, Color Guide Shift+F3, Appearance Shift+F6, Graphic Styles
Shift+F5, Stroke Cmd+F10, Gradient Cmd+F9, Transparency Shift+Cmd+F10)
run window.panel {panel}, show in the Window menu and are editable.

paint.invert / paint.complement and the "?" state honour the Appearance
panel's active item; objects without bounds still show their paints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 02:05:33 +07:00
bflatastic 512e96e7c8 M14.3: the dragon app icon is the brand mark
The mark left of Home (and in the About box) is now the VectorCraft app icon,
the engraved dragon from assets/app-icon/ (128 px PNG, include_bytes!), decoded
once per context into a mipmapped texture and drawn at 22 pt with the icon's own
rounded corners. The old code-drawn gradient square with a pen nib is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 02:00:05 +07:00
bflatastic 3c856caa65 M14.2: custom title bar on Windows and Linux
The window is undecorated off macOS and the app bar is the title bar: Minimize,
Maximize/Restore and Close caption buttons (46 pt, drawn in code, Close hover red)
at the far right, empty bar space drags the window and double-click toggles
maximize, and 5 pt invisible edge/corner zones resize it (off while maximized or
fullscreen). Close runs app.quit, so modified documents are asked about first.
The right-side group now fits the space after the menus: Discord drops out and
the search box becomes an icon at narrow widths, so nothing overlaps at 800 pt.
macOS and the web build are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 02:00:04 +07:00
BFlat 8dc6396971
Merge pull request #38 from storytold/pr/m3-graphic-styles-panel
M3: Graphic Styles panel depth, Tile Edge Color, Show Swatch Bounds (M3.71, M3.72)
2026-10-05 01:59:14 +07:00
bflatastic b36fd67bf4 M3.72: Tile Edge Color dialog and Show Swatch Bounds in pattern editing
Object > Pattern > Tile Edge Color... picks the colour of the tile edge
(a layer-colour preset or a custom colour), stored in the preference
patternTileEdgeColor and passed to the renderer. Pattern Options gains
Show Swatch Bounds: a dashed outline of the part of the tiling the swatch
repeats (the period of brick and hex tilings).

Commands: prefs.set {patternTileEdgeColor}, pattern.options
{showSwatchBounds}, ui.tileEdgeColor. Model: PatternDef.show_swatch_bounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:50:25 +07:00
bflatastic d8b942c43d M3.71: Graphic Styles panel views, previews, merge, reorder and drag and drop
Thumbnail, Small List and Large List views; thumbnails rendered on a
square or on type and cached by a hash of each style's look. Merge
Graphic Styles (named in Graphic Style Options), drag to reorder, delete
asks first (Alt-click does not), the right button held on a style shows
it large on the selected object. Styles dragged onto art apply to the
object hit; art dragged off the canvas or the Appearance thumbnail dropped
on the panel becomes a new style. Override Character Color (on by
default) clears type's character fill and stroke when a style with fills
or strokes is applied. Control bar Style picker.

Commands: graphicStyle.merge, graphicStyle.move, graphicStyle.setOptions,
ui.mergeGraphicStyles. Prefs: overrideCharColor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:50:13 +07:00
BFlat 1c18851728
Merge pull request #35 from storytold/pr/m3-flatten-transparency
M3: Flatten Transparency engine (M3.91)
2026-10-05 01:39:03 +07:00
bflatastic f75d7628c7 M3.91: Object > Flatten Transparency turns transparent art into opaque art that looks the same
object.flattenTransparency {preset?|options, ids?} splits the targets into groups of overlapping
objects. Groups with transparency become atomic regions (faces of the arrangement of every fill,
outlined stroke and clipping path) filled with the colour the renderer composites there: blend
modes, group opacity, isolated and non-isolated groups, knockout groups and knockout shapes, over
white or (Preserve Alpha) over nothing. Regions reached by gradients, patterns, images, opacity
masks or raster effects are rendered into one image per group, under the regions, clipped to
them with Clip Complex Regions. The raster/vector balance rasterizes whole groups past its region
budget (all at 0). Options and presets live in FlattenOptions; single visible paints keep their
colour and overprint; text and strokes can be outlined everywhere. One undo step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:36:18 +07:00
BFlat cc514957f5
Merge pull request #32 from storytold/pr/m3-freeform-gradients
M3: freeform gradients — model, rendering, commands, tool and panel (M3.81, M3.82)
2026-10-05 01:29:29 +07:00
bflatastic ed308d61f7 M3.82: freeform point and line commands, Gradient tool and Gradient panel freeform section
paint.freeform.addPoint/setPoint/deletePoint/addLine/splitLine/selectPoint
and the paint.freeform.get query edit the freeform gradient behind the active
proxy (one undo step each; errors on other paints). The session keeps the
selected point. The Gradient tool on a freeform gradient selects and drags
points, adds points on the art (joined in Lines mode) and on lines, and
deletes the selected point. The Gradient panel shows the Draw toggle and the
selected point's colour, opacity, spread and Delete Point; the Color panel
recolours the selected point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:21:39 +07:00
bflatastic 2877407111 M3.81: freeform gradients: colour points with spread, smooth lines, first points placed inside the shape
Freeform gradients carry their own points (colour, opacity, spread) and lines
(Catmull-Rom curves through points), blended by inverse-distance weighting
from each disc of pure colour. paint.editGradient {kind: "freeform", mode?}
places 4+ points inside the shape, coloured along the stops (which follow the
points' colours). The renderer samples the field on a cached colour grid
drawn as an image paint clipped by the fill or stroke. Moves, transforms,
box rebases and warps move the points; old files load unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:21:38 +07:00
BFlat 0d145986e8
Merge pull request #28 from storytold/pr/m3-stroke-on-type
M3: stroke on type, Stroke panel units, mixed values and Control bar (M3.40, M3.41)
2026-10-05 01:21:10 +07:00
BFlat 09778f236c
Merge pull request #27 from storytold/pr/m3-swatch-libraries
M3: swatch libraries — library panel, generated libraries, save and load, gradient libraries (M3.56–M3.58)
2026-10-05 01:11:53 +07:00
BFlat 1ea13b54d3
Merge pull request #26 from storytold/pr/m3-container-appearance
M3: group, layer and type appearance with Contents and Characters rows (M3.68)
2026-10-05 01:10:47 +07:00
bflatastic 64d366e2ae M3.41: Stroke panel follows Units > Stroke, shows mixed values blank and reaches the Control bar
- Weights and dash lengths show in Units > Stroke in the Stroke panel,
  the Control bar and the Properties panel (Session::stroke_unit).
- Values the selected objects don't share show blank (weight, miter
  limit, dashes) or light no button (cap, join, align), from a
  per-revision cached DocState::stroke_mixed.
- Align Stroke Inside/Outside are disabled for open paths and type.
- The Control bar weight is a preset spinner with a width profile
  dropdown, and its Stroke link (and the Properties panel's) opens the
  Stroke panel as an anchored popover instead of docking it.
- The panels show a group's first painted object's stroke, as the
  Stroke proxy does (Session::shown_stroke).
- stroke.set on a group skips images and symbol instances.
- document.inspect reports each object's strokeOptions (cap, join,
  miterLimit, align, dash, dashOffset, alignDashes, arrows, arrowAlign,
  profile) in stroke.set terms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:10:14 +07:00
bflatastic 323b178ea7 M3.40: stroke on type — the Stroke panel edits the characters' stroke
With type selected, stroke.set (without an item) gives every run the
weight, cap, join, miter limit and dashes instead of adding an object
stroke; while the Type tool edits text, the Stroke panel styles the
selected characters through text.setRangeStyle `strokeOptions`, and the
panel reads the first run (or the selection's style).

CharStyle gains stroke_cap, stroke_join, stroke_miter_limit and
stroke_dash (defaulted, skipped when default). Character strokes and
object strokes on type now draw through the shared stroke geometry on
the canvas, in PDF and in SVG (live text writes cap/join/miter/dash and
tspans reset what the first run sets; object strokes are drawn on the
glyph outlines), so every stroke option, opacity and blend applies.
Outlines, the Eyedropper and graphic styles keep the options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:09:55 +07:00
bflatastic 3a79e6d922 M3.58: gradient libraries; Swatch Options renames gradient swatches; Alt-drop replaces a swatch
Window > Swatch Libraries > Gradients lists five generated gradient
libraries (Spectrum, Sky, Neutral incl. fades to transparent, Duotone,
Radial Glows); the library panel's menu lists them too.
Swatch Options on a gradient swatch shows its name and the gradient; OK
renames it. Alt-dropping an edited gradient (or a colour) on a swatch of its
kind in the Swatches panel replaces it, via swatch.edit {paint}.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:00:00 +07:00
bflatastic 786f2c05d3 M3.57: save swatch libraries as .vcswatches, .gpl or CSS and load them back
Save Swatch Library… (Swatches panel menu, libraries menu, Window menu)
opens a dialog: name, format and destination. .vcswatches is lossless JSON
(colour models, global, spot, gradients, groups), .gpl an RGB palette with
groups as comment headers, CSS custom properties with escaped names.
Libraries saved to the user library folder show under User Defined.
Other Library… (and opening a .vcswatches/.gpl file) loads a library or
another document's swatches into the library panel.

Commands: swatch.library.save {path?, format?, names?, name?, user?}
(no path returns {data}), swatch.library.load {path?|data?|dataBase64?,
name?}; UI: window.swatchLibrary.other, ui.saveSwatchLibrary (dialog
saveSwatchLibrary), window.userSwatchLibrary1..10.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:59:59 +07:00
bflatastic e7f6328188 M3.56: swatch library panel with code-generated swatch libraries
Window > Swatch Libraries and the Swatches panel's library button open a
read-only, floating library panel (thumbnail and list views, find field,
libraries menu, previous/next arrows). Clicking a swatch adds it to the
document and applies it as one undo step; Shift/Cmd-clicks select swatches
and groups for Add to Swatches. Default Swatches restores missing defaults.

Libraries are computed in code (OKLCH, rounded to 8-bit sRGB): Web Safe 216,
Grays and Neutrals, Earth Tones, Skin Tone Ramps, Pastels, Brights,
Metallic Gradients, Perceptual Scales, Harmony Sets.

Commands: swatch.library.list, swatch.library.get, swatch.library.add
{library, names?, apply?, focus?}, swatch.resetDefaults {replace?};
UI command window.swatchLibrary {library}.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:59:59 +07:00
bflatastic 8a837fd4b9 M3.68: groups, layers and type have their own fills, strokes and effects, with Contents and Characters rows
A group's or layer's own fills and strokes paint every member's geometry, below or above the
members as the new contents slot (Appearance.contents_index) says; its geometry effects reshape
the members as one piece and its raster effects apply to the composite (one combined shadow),
respecting knockout groups. Type's own fills and strokes paint under or over its characters.
Render, SVG/PDF (via bake) and Expand Appearance evaluate them alike.

Commands: appearance.* / effect.* take target: "object"|"contents" and layer ids;
appearance.moveItem moves the Contents slot (from: "contents") or an item across it
(contents); appearance.targetContents selects a group's members. Graphic styles applied to a
group now style the group itself. The Appearance panel shows Contents/Characters rows that
drag, and double-click to target the members or edit the characters' paint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:59:34 +07:00
BFlat 25c8f32fdd
Merge pull request #23 from storytold/pr/ui-dialog-menu-sizing
UI: dialogs and menus size to their content
2026-10-05 00:58:01 +07:00
bflatastic d3df1bcea2 M14.1: dialogs and menus size to their content
The shared dialog frame's button row took all the height left in the window
(a right-to-left layout fills the available height), and every dialog shared
one window id, so a dialog never shrank and inherited the size of the one
before it: the save prompt showed a tall empty band around its buttons. The
row is now as tall as the buttons and each dialog kind has its own window.

Menu items forced themselves to the popup's full available width, so every
menu opened at egui's maximum menu width. The menu layout already justifies
items, so popups are now as wide as their widest label and shortcut (at least
230 pt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:54:03 +07:00
BFlat e21ff60b6a
Merge pull request #22 from storytold/pr/m3-blend-isolation
M3: blend accuracy, non-isolated groups, layer clipping masks (M3.73–M3.75)
2026-10-05 00:34:36 +07:00
bflatastic 45ff33e144 M3.75: layer clipping masks from the Layers panel button
layer.clippingMask.toggle {id?} -> {clip}: the top object of the current
layer (or of the one selected group, or of `id`) becomes the clipping path,
unpainted and moved to the bottom so art added later is clipped too; pressing
the button again releases the mask and leaves the path unpainted. One undo
step. The Layers panel button runs it and underlines clipping-path names.

Model: NodeKind::Layer.clip (skipped when false). Clipped layers render, hit
test, bound, and export to SVG and PDF like clip groups (Node::clips /
set_clips); the clip-path helpers are shared with Make/Release Clipping Mask.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:27:31 +07:00
bflatastic 54427d7a92 M3.74: blending inside a group reaches the art below unless the group isolates it
On screen, a group (or object) with opacity, a blend mode, an opacity mask or
knockout whose content blends is drawn offscreen over a copy of its backdrop
and mixed back in by its opacity, mask and clip (a group blend mode then
applies to the backdrop-free result, as PDF defines it). Isolate Blending keeps
the content starting from transparency. Clip groups no longer isolate their
children (blending content is clipped per drawing), and non-isolated knockout
groups composite their elements against the art below the group. Groups
without blending inside still render as plain layers.

In PDF, such groups are written as non-isolated transparency groups (a
constant-alpha soft mask instead of the writer's isolated opacity group),
knockout groups included.

doc: Node::blends_through / children_blend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:27:31 +07:00
bflatastic a1a5c79b03 M3.73: blend modes follow the reference formulas on screen, in SVG and in PDF
color::blend gains blend_rgb (the 16 blend functions, with the 0.3/0.59/0.11
luminance of the non-separable modes), composite (source-over with blending)
and the shared opacity-mask luminance weights. Exported SVG masks take
luminance from sRGB values (color-interpolation="sRGB"), as the canvas does.

Tests: a per-mode table on the canvas (object, fill and group blend modes,
within 1/255; Exclusion 2/255 in the rasteriser's 8-bit pipeline), the same
table through SVG export rendered by resvg, clip and invert masks against the
canvas, and every mode's PDF name read back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:27:31 +07:00
BFlat bcc30ded5a
Merge pull request #21 from storytold/pr/m3-color-guide
M3: Color Guide rules and options, Edit Colors dialogs, recolouring meshes, images and patterns (M3.53–M3.55)
2026-10-05 00:21:57 +07:00
bflatastic 2edac7e317 M3.55: Edit Colors and Recolor Artwork also recolour gradient meshes, embedded images and pattern fills
One shared colour visitor (`colorcmds::Recolor`, used by Edit Colors, Recolor Artwork, the Blend
commands, the proxy Invert/Complement and Document Color Mode) maps solid colours, gradient stops
and text runs as before, and now also:
- gradient-mesh points (with fills); Blend grades meshes by their average and keeps their shading;
- embedded images' pixels (`includeImages?`, default on): the image becomes a recoloured copy under
  a content key, the original stays for undo and other objects; linked images are left alone;
- the tiles of pattern fills and strokes (`includePatterns?`, default on): one recoloured copy per
  pattern saved as a new pattern swatch ("Dots 2"), the original untouched.
Everything stays one undo step; only nodes that change are copied, in one walk of the tree.

- doc: `ImageBlob::map_rgb` (decode, map each distinct colour once, keep alpha, re-encode PNG),
  `ImageBlob::content_key` (now also used by image import) and `ImageBlob::png`.
- `recolor.colors` lists mesh points and pattern tile colours too.
- patterncmds: `add_pattern` adds a pattern and its swatch (shared by Make Pattern and Save a Copy).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:16:08 +07:00
bflatastic 1de1c6446f M3.54: Adjust Color Balance and Saturate open live-preview dialogs, committed as one undo step
- Edit > Edit Colors > Adjust Color Balance... opens Adjust Colors (`ui.colorBalanceDialog`, kind
  `colorBalance`): colour mode Grayscale, RGB, CMYK or Global, a slider and field per channel
  (-100..100 %), Convert, Fill and Stroke, and Preview.
- Saturate... opens Saturate (`ui.saturateDialog`, kind `saturate`): Intensity -100..100 % and
  Preview.
- Both preview through the session interaction (fields changed by `ui.dialog.set` preview on the
  next frame too); OK keeps the result as one undo step, Cancel rolls it back.
- `edit.colors.adjustBalance` takes `mode` (rgb|cmyk|gray|global; default from the channels
  given). Global mode needs tints of global and spot colours and answers with a clear error until
  they exist; the dialog stays open and the colours keep their links.
- Shared dialog helpers: `form::slider`, `form::preview`, `form::commit_preview`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:14:46 +07:00
bflatastic bfebf66404 M3.53: Color Guide has 21 harmony rules, Steps and Variation options, a fixed base colour and Save Colors as Swatches
The harmony rules (Complementary 1/2, Split Complementary, Left/Right Complement, Analogous 1/2,
Monochromatic, Shades, Triad 1-3, Tetrad 1-3, Compound 1/2, High Contrast 1-3, Pentagram) live in
the color crate with our own math, base colour first. The variation grid moves there too
(`harmony::Guide`): a row per harmony colour with the colour in the centre column, shades/cool/muted
to the left and tints/warm/vivid to the right, 1-20 steps and a variation amount.

- `color.harmony {color, rule, steps?, variation?, amount?}` query returns {rule, colors, grid}.
- The panel keeps its base colour until "Set base color" takes the current one; Shift/Cmd-click
  selects several cells and Save Colors as Swatches saves them (`swatch.new {colors}`, one step).
- Color Guide Options dialog (`ui.colorGuideOptions`, kind `colorGuideOptions`) with a preview row;
  the options persist in `UiState::color_guide`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:13:36 +07:00
BFlat 6c25e54914
Merge pull request #20 from storytold/pr/m3-overprint
M3: overprint per fill and stroke, Overprint Black options (M3.49, M3.50)
2026-10-05 00:07:34 +07:00
bflatastic cb881b082f M3.50: Overprint Black dialog with fill/stroke, rich and spot black options
Edit > Edit Colors > Overprint Black... opens a parameter dialog (Remove,
Percentage, Fill, Stroke, Include Blacks with CMY, Include Spot Blacks).
edit.colors.overprintBlack takes fill?, stroke?, includeCmyBlacks? and
includeSpotBlacks?; it covers type characters and gradients whose every stop
is black, and records no undo step when nothing changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:01:02 +07:00
bflatastic 0c82a3f5c5 M3.49: each fill and stroke can overprint
Fill and stroke layers (and type characters' fill and stroke) carry an
overprint flag. object.setOverprint {fill?, stroke?, item?, ids?} sets it
(groups: their contents; type: its characters too; one undo step, none when
nothing changes) and attributes.info reads it back (null where mixed).
Overprint Preview and Separations Preview draw overprinting fills and strokes
with Multiply. The old Document.unknown["overprintBlack"] id list migrates to
the new flags on load; Overprint Black now sets the flags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:01:02 +07:00
BFlat 6808564486
Merge pull request #19 from storytold/m3-paint-stage2
M3 stage 2: swatch and gradient panel depth, stroke export parity, dash alignment, appearance editing, knockout, graphic style links (M3.30–M3.48)
2026-10-04 23:55:10 +07:00
bflatastic edeb1cb548 Roadmap: M3 stage 2 (M3.30–M3.39, M3.42–M3.48)
The M3 row, the shipped list and the colour, stroke and appearance parity rows
cover Swatches panel depth, Gradient panel depth and rebased gradients, stroke
export parity and Outline Stroke fidelity, corner-fitted dashes, appearance
editing depth and effects on every object kind, knockout and page groups, and
graphic style links. Remaining estimates are recalibrated on the measured
throughput of the first two M3 waves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:54:21 +07:00
bflatastic ca709871f2 Merge main into m3-paint-appearance (PR #15, #16) 2026-10-04 23:51:50 +07:00
bflatastic 3981acdb06 Merge wp/C3: dash alignment and variable-width dashes (M3.38, M3.39)
# Conflicts:
#	crates/pdf/src/lib.rs
#	crates/ui-egui/src/panels/mod.rs
2026-10-04 23:48:47 +07:00
bflatastic 43d7ac420d Merge wp/D2
# Conflicts:
#	crates/pdf/src/export.rs
#	crates/render/src/fx.rs
#	crates/render/src/lib.rs
#	crates/svg/src/export.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/dialogs/mod.rs
#	crates/ui-egui/src/panels/appearance.rs
#	docs/control-protocol.md
#	docs/mcp.md
2026-10-04 23:46:38 +07:00
BFlat deaeef047c
Merge pull request #16 from storytold/docs/name-reference-app
Docs: name the reference app again in ROADMAP and docs/mcp.md
2026-10-04 23:41:42 +07:00
bflatastic 454c5f8620 M3.39: width profiles follow the path through dashes, and their corners take the join
Each dash and dot of a profile stroke is as wide as the profile where it sits
along the whole path (before, dashes dropped the profile). Corners of profile
strokes take the stroke's miter (within the miter limit), round or bevel join on
the outer side and stay clean on the inner side, instead of a miter clamped at
4x; smooth points of curves bend round. Canvas, SVG/PDF outlines and Outline
Stroke share the geometry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:40:52 +07:00
bflatastic 42ad4540b7 M3.38: dashes fitted to corners and path ends, a flag of their own that keeps the offset
With alignDashes every run between corners and path ends holds a whole number of
periods, scaled to fit, with a dash (or dot) centred on each corner and on both
ends of an open path. It lives in the shared dash generator, so the canvas, SVG
and PDF export (as outlines) and Outline Stroke all draw it.

stroke.set: alignDashes and dashOffset edit the current pattern on their own, and
a new dash array keeps the offset and alignment it replaces. The Stroke panel
turns dashes on fitted to corners (or as last turned off), and its Exact/Fit
buttons send only the flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:37:58 +07:00
bflatastic 0f169b5c5d Merge wp/B2
# Conflicts:
#	crates/doc/src/lib.rs
#	crates/engine/src/cmd/style.rs
#	crates/engine/src/lib.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/panels/swatches.rs
#	docs/control-protocol.md
2026-10-04 23:37:27 +07:00
bflatastic 818980a789 Merge wp/A2
# Conflicts:
#	crates/engine/src/lib.rs
2026-10-04 23:20:10 +07:00