Document combining vector shapes (#187)
- Vector Art: new section Combining shapes with a table, Ctrl+click, Delete on several shapes, the COMBINE block and a known limit. - Architecture: Clipper2 in the stack and the vectorBoolean module. - Development: check sum 1222. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent
396314f38d
commit
b07676cb23
3 files changed
+47
-8
No files matched your search
+5
-1
@@ -10,6 +10,10 @@
|
||||
`runtime/physics/engine.ts` loads once in the background after the editor has opened its project,
|
||||
so the first picture never waits for it; a failed load is reported in the console and not retried.
|
||||
Outside the engine everything stays in pixels, inside 100 px are one metre (`toMetres`/`toPixels`).
|
||||
- **Clipper2** (`clipper2-ts`, Boost licence, no dependencies) — polygon clipping for the boolean
|
||||
operations on vector art (#187). Only its engine is imported (`dist/Engine.js`, `dist/Core.js`),
|
||||
not the package's `Clipper` namespace, which would bring offsetting, triangulation and Minkowski
|
||||
sums along: about 50 KB of the bundle instead of 101 KB, minified.
|
||||
- **Own canvas widget toolkit** (`src/ui/`) — panels, buttons, lists, text/number/color fields,
|
||||
popups. The only lasting DOM element is a hidden `<input>` used as a keyboard bridge (IME,
|
||||
mobile keyboard, clipboard) while a canvas text field is focused; *Upload…* adds a short-lived
|
||||
@@ -68,7 +72,7 @@
|
||||
|
||||
```
|
||||
web/index.html Vite source page (dev) index.html built page (generated, served)
|
||||
src/core/model/ Project (object holders), Asset + AssetTypes, SceneObject, Properties (value types), hierarchy, walk (generic parent/child walks), instance (container content resolved from its template), usages (where an asset is used), container (the cycle guard), placement (what may be dropped where), texture (frames, the slice grid, a frame's collision outlines), frameOutline (#193: a frame's outlines as a collider wears them, centred, a pixel to a unit, cut into pieces once per drawn list), traceCollision (#161: a frame's outlines read off its picture — the pixels from an alpha threshold on are solid, each 4-connected island is walked round along the pixel edges and cut down to a few corners without ever crossing itself; crumbs below 1 % of the frame drop out), generated (pictures that are drawn rather than uploaded, #185: the key `gen:<assetId>@<revision>` and the register of readers that tell the picture path an asset's canvas size and revision), vectorart (#129: the canvas as a property, which ring is worth drawing, and the revision as a fingerprint of the drawing rather than a counter) + vectorPath (what a shape draws, as plain commands: closed rings fill, every ring strokes, a segment is a line while neither of its ends carries a handle), polygon (an outline checked, cut into convex pieces and clipped to a rectangle), tile (the three kinds and their block shapes, reading a tile's pictures, the animation step), autotile (which quarter tile a neighbourhood asks for, the 47/16 tiles a block produces, 1 for a plain tile), tilemap (the cells of a layer, which neighbours a cell is joined to, the cells a stroke or a rectangle covers), tilemapCollision (the solid cells of a map, drawn in or out by their tile's collision scale and merged into rectangles, one group per one-way angle and per surface (`tileMaterials`, #193), the outlined cells as polygons — an autotile's outlines cut into its quarters and pieced together per cell like its picture —, where one sits in the world, and which cells of a placed map a world rectangle can reach, in cells (`cellsWithin`, #184)), animation (which frame at time t, a row's value there, what a row may aim at, the players on an object; KeyRef and its lookups) + animationPreview (the root at a frame, written into copies), graphLookup (what a graph sees of the project: behaviors to run, state machines to read), Scope (where a state machine's current state lives: per instance or one for the run — variables are shared by living on a Variables asset instead), collisionLayers (which colliders may meet: the project's named layers, a collider's layer and mask, and the one rule `layersMeet` the engine, Solids and the event pair search all answer by)
|
||||
src/core/model/ Project (object holders), Asset + AssetTypes, SceneObject, Properties (value types), hierarchy, walk (generic parent/child walks), instance (container content resolved from its template), usages (where an asset is used), container (the cycle guard), placement (what may be dropped where), texture (frames, the slice grid, a frame's collision outlines), frameOutline (#193: a frame's outlines as a collider wears them, centred, a pixel to a unit, cut into pieces once per drawn list), traceCollision (#161: a frame's outlines read off its picture — the pixels from an alpha threshold on are solid, each 4-connected island is walked round along the pixel edges and cut down to a few corners without ever crossing itself; crumbs below 1 % of the frame drop out), generated (pictures that are drawn rather than uploaded, #185: the key `gen:<assetId>@<revision>` and the register of readers that tell the picture path an asset's canvas size and revision), vectorart (#129: the canvas as a property, which ring is worth drawing, and the revision as a fingerprint of the drawing rather than a counter) + vectorPath (what a shape draws, as plain commands: closed rings fill, every ring strokes, a segment is a line while neither of its ends carries a handle) + vectorBoolean (#187: union, subtract, intersect and exclude on the sampled outlines, each shape read by the even-odd rule; the result rings in the order Pixi's hole detection needs, every outline followed by its holes and preceded by the islands inside them), polygon (an outline checked, cut into convex pieces and clipped to a rectangle), tile (the three kinds and their block shapes, reading a tile's pictures, the animation step), autotile (which quarter tile a neighbourhood asks for, the 47/16 tiles a block produces, 1 for a plain tile), tilemap (the cells of a layer, which neighbours a cell is joined to, the cells a stroke or a rectangle covers), tilemapCollision (the solid cells of a map, drawn in or out by their tile's collision scale and merged into rectangles, one group per one-way angle and per surface (`tileMaterials`, #193), the outlined cells as polygons — an autotile's outlines cut into its quarters and pieced together per cell like its picture —, where one sits in the world, and which cells of a placed map a world rectangle can reach, in cells (`cellsWithin`, #184)), animation (which frame at time t, a row's value there, what a row may aim at, the players on an object; KeyRef and its lookups) + animationPreview (the root at a frame, written into copies), graphLookup (what a graph sees of the project: behaviors to run, state machines to read), Scope (where a state machine's current state lives: per instance or one for the run — variables are shared by living on a Variables asset instead), collisionLayers (which colliders may meet: the project's named layers, a collider's layer and mask, and the one rule `layersMeet` the engine, Solids and the event pair search all answer by)
|
||||
src/core/properties/ PropertySchema, kinds (one row per value kind), listItems (an item's kind, read off its value), resolve (the only override merge), deviation (what is gold), collider/camera/bounds geometry (with the side a one-way collider blocks from, and the rectangle around a shape a broad phase looks at first, #184), convex (capsules and polygons: rounded convex pieces against each other), ray (where a ray meets a shape, in double precision), rigidbody (what the engine reads off a rigidbody, a collider's material and the scene), aspect, enabled, editorRun, assetRef/textureRef (how a reference reads), declaredRows (the row a Property node declares in a shader or particle graph, and the params that node has in both)
|
||||
src/core/util/ list (take out, put back and move an item — what a command's undo is made of), vec (dot, the closest point on a segment — shared by the collider maths, the outline hit area and the polygon grips)
|
||||
src/core/commands/ undoable commands, CommandStack, SetBagValueCommand base, RenameCommand base (undone by renaming back: variable, struct field, port), one import command shape for texture/font/sound, require.ts (validated lookups, and what an instance may not change), naming.ts
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs
|
||||
npm run build # writes dist/ and regenerates index.html
|
||||
./build.sh # the same under `set -o pipefail`, for callers that pipe the output
|
||||
php tools/make-user.php <name> <password> # an account; every tool signs in (see Accounts)
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1217 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1222 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
|
||||
./e2e.sh start [GM_STEPS] # the same run detached, see below
|
||||
./e2e.sh status # live progress, or the last run's result
|
||||
|
||||
+41
-6
@@ -69,7 +69,10 @@ like the Texture tab, and the checkerboard behind the canvas is what is transpar
|
||||
cut: both halves together draw exactly what was drawn before.
|
||||
- A **double click** on a point takes it away.
|
||||
- **Alt** on a straight point makes it symmetric, and a point with handles straight again.
|
||||
- **Delete** removes the selected shape.
|
||||
- **Ctrl+click** (Cmd on a Mac) adds a shape to the selection or takes it out again. The shapes
|
||||
selected alongside show their outline; only the one clicked last shows its points and handles.
|
||||
A plain click selects that one shape alone.
|
||||
- **Delete** removes the selected shapes.
|
||||
|
||||
The point pressed last — or put down last with the Pen — is **picked** and drawn filled. The
|
||||
**Point** menu in the toolbar shows its type and turns it into another one, as one undo step:
|
||||
@@ -99,15 +102,48 @@ Selecting a shape — in the tab or in the list — gives it its own rows:
|
||||
- **SHAPES** lists every shape of the asset, each row painted with the shape itself, the whole
|
||||
canvas scaled into the swatch, so a row also says *where* on the canvas its shape sits. The
|
||||
**topmost row is drawn last**, the way a layer list reads.
|
||||
Ctrl or Shift on a row adds its shape to the selection, as Ctrl+click does in the tab.
|
||||
- **Forward / To front / Backward / To back** move the selected shape through that order, and a row
|
||||
can be **dragged** to another place in the list.
|
||||
- **COMBINE** holds the four boolean operations, see [below](#combining-shapes).
|
||||
- **VECTOR SHAPE** carries Name, Fill, Opacity and an **Outline** switch; with the switch on, Line
|
||||
colour and Line width appear. The switch *gives and takes away* the outline rather than hiding it,
|
||||
and a width of zero is no outline, so the row stops at 0.1.
|
||||
- **Delete shape** and **Add shape** sit below.
|
||||
- **Delete shape** and **Add shape** sit below; with several shapes selected it reads **Delete
|
||||
shapes** and removes them all.
|
||||
|
||||
A dragged colour or opacity slider is one undo step, not one per pixel of the drag. Selecting the
|
||||
**asset** instead of a shape shows the same list under the asset's own rows, with nothing selected.
|
||||
With several shapes selected, the order buttons and the style rows are those of the one clicked
|
||||
last. A dragged colour or opacity slider is one undo step, not one per pixel of the drag. Selecting
|
||||
the **asset** instead of a shape shows the same list under the asset's own rows, with nothing
|
||||
selected.
|
||||
|
||||
## Combining shapes
|
||||
|
||||
Select two or more shapes (Ctrl+click) and press one of the four buttons under **COMBINE**:
|
||||
|
||||
| Button | What is left |
|
||||
|---|---|
|
||||
| **Union** | Everything any of the shapes covers. |
|
||||
| **Subtract** | The lowest shape, with every shape above it cut out. |
|
||||
| **Intersect** | Only what all of the shapes cover. |
|
||||
| **Exclude** | What an odd number of them covers: the overlaps are cut out. |
|
||||
|
||||
- **The result replaces the lowest shape** of the selection, the one drawn first. It keeps that
|
||||
shape's name, fill, opacity, outline and place in the drawing order; the other shapes are gone.
|
||||
The note under the buttons names it. Which shape was clicked first does not matter.
|
||||
- It is an **ordinary shape** again, not a compound that remembers its parts. To change the parts,
|
||||
undo: the whole operation is **one undo step**.
|
||||
- **Curves become corners.** The shapes are sampled first, each curve cut into straight pieces that
|
||||
stay within 0.2 canvas pixels of it, and the result has no handles.
|
||||
- Every shape is read the way it is painted, by the even-odd rule: a ring inside a ring is a hole
|
||||
and stays one. A result can have holes, and islands inside those holes.
|
||||
- **An open line cannot be combined** — it has no inside. The buttons stay off while one of the
|
||||
selected shapes holds an open line, and with a single shape selected.
|
||||
- A result of **nothing** is refused with a message, for example Intersect on shapes that do not
|
||||
overlap. The drawing stays as it was.
|
||||
|
||||
The clipping is done by [Clipper2](https://www.angusj.com/clipper2/) (the `clipper2-ts` port,
|
||||
Boost licence), the project's first geometry library.
|
||||
|
||||
## Wearing it
|
||||
|
||||
@@ -122,8 +158,7 @@ image — the project file holds the shapes, and the picture is rasterised again
|
||||
## Known limits
|
||||
|
||||
- **A fill is one colour.** Gradients and texture fills are #186.
|
||||
- **No boolean operations** — no union, no subtraction. A hole is a ring inside a ring; anything
|
||||
beyond that is #187.
|
||||
- **Combining keeps no curves.** The result of a boolean operation is made of corners only (#187).
|
||||
- **A collider does not follow the drawing.** A sprite wearing vector art collides with its box or
|
||||
with a shape you give it by hand, as with any picture; taking the outline as a collider shape is
|
||||
#188.
|
||||
|
||||
Reference in new issue
Block a user