From dfecee87c0cc27b94b838ebf02220e667f866b09 Mon Sep 17 00:00:00 2001 From: MelekTaus Date: Sun, 20 Sep 2026 20:09:46 +0200 Subject: [PATCH] A picture may be drawn instead of uploaded (#185): Textures gains the section on the contract - a drawn asset resolves through the same picture path as a texture, with the two differences that it has no frames and is not stored, being redrawn from the asset; Architecture lists core/model/generated and viewport/generatedPictures Co-Authored-By: Claude Opus 5 --- Architecture.md | 6 +++++- Textures.md | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Architecture.md b/Architecture.md index b11f9e9..36280d0 100644 --- a/Architecture.md +++ b/Architecture.md @@ -68,7 +68,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), 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, 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), generated (pictures that are drawn rather than uploaded, #185: the key `gen:@` and the register of readers that tell the picture path an asset's canvas size and revision), 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, 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 @@ -104,6 +104,10 @@ src/ui/ widget toolkit (generic, knows nothing about assets) src/viewport/ view (transforms), items (what to draw), symbols (how to draw it), symbolIcons (the SVG files in icons/, parsed once, in the theme's colours, #175), FileLibrary (load once, never retry a failure, tell the listeners) ← TextureLibrary, FontLibrary; + generatedPictures (the drawn half of the picture path, #185: one picture per asset kept + under its revision key, the older one freed when the newer is drawn; who draws which asset + type, and the factory that looks an asset up in the open project) — no GPU, so it is measured + without a browser, SoundLibrary, ShaderLibrary, ParticleLibrary, particles (one ParticleContainer per source), tilemap (one container per layer), drawOrder (objects and tilemap layers by z, #174), activeCamera — no editor concepts, diff --git a/Textures.md b/Textures.md index a0dde31..b5f416e 100644 --- a/Textures.md +++ b/Textures.md @@ -171,6 +171,20 @@ looked like before textures existed. So there is no placeholder, no flicker and Deleting a texture warns how many sprites reference it (a texture is never *placed*, so counting placed instances would report "unused" for a picture a dozen sprites are showing). +**A picture can be drawn instead of uploaded.** Not every picture is a file: an asset may draw +its own, and the first one that does is Vector Art (#129). Such an asset is worn exactly like a +texture — it stands in the same picker, a sprite names it in its *Texture* property, a tile can be +built from it, and a new sprite is measured by its canvas. What the picture path asks of it is two +things: the size of its canvas, and a **revision** that changes whenever the drawing changes. The +revision is what makes a changed drawing a new picture, so nothing anywhere has to ask whether a +picture is still current; the editor draws the new revision and lets the old one go. + +Two differences to an uploaded picture. It has **no frames** — frames are rectangles cut out of a +sheet, and a drawn picture has none to cut from. And it is **not stored**: nothing of it lies in +`data/textures`, the project file holds the drawing, and the picture is drawn again after a +reload. The whole path is the same one an uploaded file takes, which is why a drawn picture works +in shaders, particles, tiles and tilemaps without any of them knowing about it. + **Limits.** nginx caps request bodies at 1 MB, so the editor shrinks images over 2048 px per side and re-encodes to WebP when they are still too big — but an image that already fits is uploaded byte for byte, because re-encoding pixel art would destroy it. Anything still too