Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/development/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ Rules for the generated files:
pull request.
- A change to the menu layout needs no code change here: run `./gradlew generateDocsImages` and commit
the menu SVGs.
- The blocks are drawn in Minecraft's colours and patterns, but no Minecraft texture is used. Each material has a
representative colour and an 8x8 pixel tile that the generator computes from the material name (`BlockLooks`);
pixel icons for menu items that are not cubes are shapes written in code (`ItemIcons`). The generator never
downloads, copies, embeds or reads a Minecraft texture, a resource pack or any other third-party texture file,
because the licence of those files does not allow it. Do not add one to `src/docsGen` or to the pictures.
- A new block material needs an entry in `BlockPalette` (the fixture it stands for) and in `BlockLooks`; a new
non-cube menu item needs an icon in `ItemIcons`. Until then the generator fails and names the material.
- Reading the brush pictures: a block the brush changed is drawn at full colour with a white border, a block it did
not change is faded, air is the light blue sky, and the clicked block is outlined. The legend names every material
by its Minecraft name. The paint brush numbers its clicked points in click order and joins them with a line. In the
menu, a numbered badge marks each functional slot and the legend gives the number, the slot and the English text;
filler panes have no badge.
- The generator reads no clock and no network. Fixed terrain comes from the test fixtures
(`MemoryExtent`, `StrokeBuilder`, `Terrain`); the generator contains no copy of a brush algorithm.
- Every builder page needs an image or a link to the screenshot shotlist; `DocsLinkChecker` fails the
Expand Down
1,033 changes: 915 additions & 118 deletions docs/images/brushes/angle-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
961 changes: 825 additions & 136 deletions docs/images/brushes/bucket-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
939 changes: 849 additions & 90 deletions docs/images/brushes/disc-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
939 changes: 849 additions & 90 deletions docs/images/brushes/fracture-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,413 changes: 1,256 additions & 157 deletions docs/images/brushes/gradient-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
993 changes: 848 additions & 145 deletions docs/images/brushes/overlay-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,665 changes: 1,172 additions & 493 deletions docs/images/brushes/paint-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,427 changes: 1,269 additions & 158 deletions docs/images/brushes/sphere-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,157 changes: 1,003 additions & 154 deletions docs/images/brushes/splatter-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,145 changes: 991 additions & 154 deletions docs/images/brushes/spray-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
997 changes: 889 additions & 108 deletions docs/images/brushes/underlay-brush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
980 changes: 717 additions & 263 deletions docs/images/menu/menu-angle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
902 changes: 639 additions & 263 deletions docs/images/menu/menu-sphere-default.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,004 changes: 742 additions & 262 deletions docs/images/menu/menu-spray-disabled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-10-10
56 changes: 56 additions & 0 deletions openspec/changes/minecraft-style-illustrations/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Design

## Tiles as shared definitions

Every block face and every item icon is an 8x8 pixel tile. The SVG writes each tile once, as a `<g>` with
one `<rect>` per pixel in `<defs>`, and draws each cell as one `<use href>` with an integer position. A
picture with a few hundred cells then stays at a few hundred kilobytes, and no pattern tile has to align with
the grid. The tiles are sorted by id, so the output does not depend on the order in which cells are drawn.

## Procedural pattern

The shade of each pixel is the base colour of the material, lightened or darkened by a fixed step. A
`java.util.Random` seeded from the material name chooses the step of each pixel, and the probabilities of
light and dark pixels are fixed per material. The same name always gives the same tile, on every operating
system, and two materials give different tiles. The grass block side is a special case: two rows of grass
green, one jagged row, then dirt.

Base colours are representative averages of the Minecraft block faces, chosen by hand:

| Material | Base colour |
|---|---|
| `GRASS_BLOCK` (top) | `#7CBD6B` |
| `DIRT` (and the grass side below the strip) | `#866043` |
| `STONE` | `#7D7D7D` |
| `ANDESITE` | `#8A8B8C` |
| `COBBLESTONE` | `#6E6E6E` |
| `GOLD_BLOCK` | `#F9D849` |
| `SPONGE` | `#C3C04A` |

No two materials share a base colour; a unit test checks it.

## Block drawing

- Top view: a column shows its highest changed block at full opacity with a highlight border. A column the
brush did not change shows its top block, faded.
- Side cross-section: a cell shows the side tile of its block (the grass side has the green strip on top);
changed blocks are at full opacity, unchanged blocks faded, air in the sky tint.
- Clicked block: a two-pixel dark outline on the cell.
- Paint brush: the control points are numbered in click order, connected by a polyline. The brush size is
the radius of the ball the stroke is swept through, so a size of 1 keeps the stroke thin.

## Scenes

Every scene stands on grass over three dirt blocks over stone. The scenes that need a palette mix three
stone-family blocks. The paint scene has six control points on the top layer, with the last one as the
target.

## Menu

The menu is drawn as the Minecraft 6x9 chest container at twice the inventory scale: 36 px per slot with a
2 px bevel, a 14 px light grey border, and the slot grid of `GUI.layout()` unchanged. Block items are block
tiles, glass panes are tinted translucent tiles, and other items are 8x8 pixel icons written in code.
A numbered badge marks each functional slot, and a legend beside the chest maps numbers to the English
label and the slot number. Filler panes have no badge.

Unknown items fail the generator and name the material, as an unknown block does today.
62 changes: 62 additions & 0 deletions openspec/changes/minecraft-style-illustrations/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Proposal

## Why

Maintainer review of the generated illustrations found that they do not look like Minecraft. Andesite is
teal, stone is nearly black, every block the brush did not change is one flat grey, so builders cannot see
the grass and dirt they are painting on. The paint stroke is a solid blob with no visible path, and the
brush menu is a grid of text cells that does not resemble the inventory a builder sees. The 2D layout (top
view, side cross-section, legend; the 54-slot grid) is liked and stays.

## What Changes

- Colour every block with the representative colour of its Minecraft material (grass block, dirt, stone,
andesite, cobblestone, gold block, sponge). The generator maps each test fixture to its real material name
and no longer colours blocks by object identity, so the legend names the block ("Grass Block", "Gold Block").
- Draw every block face as a deterministic 8x8 pixel tile that the generator computes from the material name
with a fixed seed: shades of the material's base colour. No Minecraft or third-party texture file is
downloaded, copied, embedded or read.
- Draw blocks the brush did not change with their material tile at reduced opacity, blocks the brush changed
at full opacity with a thin highlight border, air in a sky tint (`#dbeafe`), and the clicked block outlined.
- Build the brush scenes on grass over dirt over stone. The sphere and gradient scenes mix stone, andesite and
cobblestone; the paint scene uses a thin brush so the stroke reads as a path through its six clicked points,
which are numbered 1 to 6 in click order.
- Draw the brush menu as a Minecraft 6x9 chest container: a light grey panel, bevelled inset slots, glass panes
as tinted translucent tiles, block items as block tiles, and the other items as small pixel icons defined in
code. Numbered callouts sit on functional slots only, never on filler panes, and a legend gives number to
English label (from `bettergopaint.properties`) with the slot number, because the pages refer to slots.
- Regenerate the eleven brush and three menu SVGs. The existing tasks are unchanged: `./gradlew
generateDocsImages` writes them, `./gradlew checkDocsImages` and `./gradlew docsGenTest` run inside `check`.
- Document the procedural tiles and the rule against third-party or Mojang texture files in
`docs/development/documentation.md`.

Commit type and scope: `build(docs)` for the generator, the tests and the images; `docs(development)` for the
documentation page; `docs(openspec)` for this proposal. Pull request title: `build(docs): draw illustrations
with minecraft style blocks`.

Not breaking: no command, permission, config key, brush, menu layout or supported version changes, so no
server owner has to act. The plugin jar does not change; the generator stays in the `docsGen` source set.

## Capabilities

### Modified Capabilities
- `docs-illustrations`: material colours, procedural block tiles without third-party assets, legend names,
unchanged blocks shown with their material, paint control points, and the chest-style menu with callouts.

## Impact

- `src/docsGen/java/net/onelitefeather/bettergopaint/docsgen/**`: block looks and tiles, item icons, scene
terrain and paint stroke, brush and menu rendering, SVG model with definitions, opacity, paths and circles.
- `src/docsGenTest/java/net/onelitefeather/bettergopaint/docsgen/**`: unit tests for each of the above.
- `src/test/java/net/onelitefeather/bettergopaint/support/Terrain.java`: one more block fixture
(`cobblestone`), used only by the generator.
- `docs/images/brushes/*.svg` (11 files) and `docs/images/menu/*.svg` (3 files), regenerated.
- `docs/development/documentation.md`: the Illustrations section.

## Out of scope

- Any Minecraft texture, resource pack or third-party texture, in any form. The tiles are computed.
- Changing brush behaviour or the menu layout. `GUI.layout()` and the brush code are read as they are.
- Rewriting the user pages. Their captions and alt texts stay; only a caption that would become wrong changes.
- PNG or other raster output, animation and interactive pictures.
- Archiving this change. It is archived after the pull request is reviewed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
## ADDED Requirements

### Requirement: Blocks are drawn in their Minecraft material colours
The generator SHALL draw every block with the representative colour of its Minecraft material: grass block
top `#7CBD6B`, dirt `#866043`, stone `#7D7D7D`, andesite `#8A8B8C`, cobblestone `#6E6E6E`, gold block
`#F9D849`, sponge `#C3C04A`, and any other material the scenes or menus use in the same way. Two different
materials SHALL NOT share a base colour. The generator SHALL map each test fixture block to its real material
name and SHALL NOT colour blocks by object identity.

#### Scenario: Two materials share a colour
- **WHEN** a material is added to the palette with the base colour of another material
- **THEN** `./gradlew docsGenTest` fails and names both materials

#### Scenario: Unchanged grass
- **WHEN** `./gradlew generateDocsImages` renders the sphere brush
- **THEN** a grass block the brush did not change is drawn with the grass block tile, not with a grey fill

### Requirement: Block tiles are computed without third-party assets
Every block face and item icon SHALL be an 8x8 pixel tile that the generator computes from the material name
with a fixed seed, using shades of the material's base colour. The generator SHALL NOT download, copy, embed or
read any Minecraft texture, resource pack or other third-party texture file. The same material name SHALL give
the same tile on every run, and two materials SHALL give different tiles.

#### Scenario: Tile of the same material twice
- **WHEN** the tile of `STONE` is computed twice in one run or in two runs
- **THEN** both tiles have the same 64 pixel colours

#### Scenario: Texture files in the sources
- **WHEN** the sources of `src/docsGen` are searched for image or texture file references
- **THEN** none is found, because the tiles are computed in code

### Requirement: Unchanged blocks show their material and changed blocks stand out
Blocks the brush did not change SHALL be drawn with their material tile at reduced opacity, so builders see
the grass, dirt and stone they paint on. Blocks the brush changed SHALL be drawn at full opacity with a thin
highlight border. Air SHALL be drawn in the sky tint `#dbeafe`. The clicked block SHALL be outlined.

#### Scenario: Changed block next to unchanged block
- **WHEN** the sphere brush changes a block next to an unchanged block in the top view
- **THEN** the changed block is drawn at full opacity with a highlight border and the unchanged block is drawn faded

#### Scenario: Air
- **WHEN** a cell of the picture is air
- **THEN** the cell is filled with `#dbeafe`

### Requirement: Legend names the materials of the picture
The legend of a brush illustration SHALL list every material shown in the picture by its Minecraft name, for
example "Grass Block", "Dirt", "Gold Block", and SHALL list the markers "changed by the brush", "unchanged
(faded)", "air" and "clicked block". The paint brush legend SHALL also list the numbered control points.

#### Scenario: Sphere legend
- **WHEN** `./gradlew generateDocsImages` renders the sphere brush
- **THEN** the legend of `docs/images/brushes/sphere-brush.svg` contains the text "Grass Block"

### Requirement: Paint brush shows its control points and path
The paint brush illustration SHALL number each clicked control point in click order, starting at 1, SHALL
connect the points with the path of the stroke, and SHALL paint blocks along a thin path rather than a solid
area, so that the curve is visible.

#### Scenario: Control points
- **WHEN** `./gradlew generateDocsImages` renders the paint brush
- **THEN** `docs/images/brushes/paint-brush.svg` contains the numbers 1 to 6, one for each control point

#### Scenario: Stroke is a path
- **WHEN** `./gradlew docsGenTest` renders the paint scene
- **THEN** the painted blocks fill less than half of their bounding box in the top view; a stroke that fills more fails the test

## MODIFIED Requirements

### Requirement: Menu map matches the layout
The generator SHALL render the 54 slots of `GUI.layout()` for three brush states into `docs/images/menu/`, as a
Minecraft 6x9 chest container: a light grey panel, bevelled inset slots, glass panes as tinted translucent
tiles, block items as block tiles and other items as pixel icons defined in code. A numbered callout SHALL mark
each functional slot and SHALL NOT mark a filler pane. A legend SHALL map each callout number to the English
text of the slot's translation key from `bettergopaint.properties` and to the slot number.

#### Scenario: Slot moved
- **WHEN** a pull request moves the mask toggle from slot 6 to another slot
- **THEN** `./gradlew checkDocsImages` fails on the menu images until they are regenerated

#### Scenario: Missing translation
- **WHEN** a menu slot uses a key that `bettergopaint.properties` does not define
- **THEN** the generator fails and names the key instead of writing the raw key into the image

#### Scenario: Filler panes have no callout
- **WHEN** the menu of the sphere brush is rendered
- **THEN** the callouts are on the functional slots only, one per slot, and no callout is on a filler pane

#### Scenario: Item without an icon
- **WHEN** a menu slot holds a non-block item that has no pixel icon in the generator
- **THEN** the generator fails and names the material
36 changes: 36 additions & 0 deletions openspec/changes/minecraft-style-illustrations/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## 1. Proposal (docs(openspec))

- [x] 1.1 Write the proposal, design and delta spec of `docs-illustrations`; `openspec validate minecraft-style-illustrations --strict` passes

## 2. Red tests (build(docs))

- [x] 2.1 Add failing tests in `src/docsGenTest` for: distinct base colours and real material names in the legend, tile determinism and the grass side, faded unchanged blocks and the sky tint, paint control points and a thin stroke, menu callouts on functional slots only, unknown items naming their material, and the SVG model elements (definitions, use, opacity, polyline, circle, centred text)
- [x] 2.2 Run `./gradlew docsGenTest` and confirm the new tests fail for the stated reasons before any production code changes

## 3. Block looks and tiles (build(docs))

- [x] 3.1 Green: `BlockLooks` with the base colours and procedural 8x8 tiles, `BlockPalette` mapping each fixture to its material name, `Terrain.cobblestone` fixture
- [x] 3.2 Green: SVG model with definitions, `use`, opacity, polyline and circle elements, and the writer for them

## 4. Scenes (build(docs))

- [x] 4.1 Green: grass over dirt over stone ground for every scene, sphere and gradient with stone, andesite and cobblestone, paint scene with six numbered control points and a thin size

## 5. Brush and menu pictures (build(docs))

- [x] 5.1 Green: brush picture with faded unchanged blocks, highlight border on changed blocks, sky tint, named legend, numbered control points and path
- [x] 5.2 Green: menu picture as a chest container with glass panes, block tiles, pixel icons, callouts on functional slots and a legend

## 6. Images (build(docs))

- [x] 6.1 Run `./gradlew generateDocsImages`, rasterise the 14 SVGs at 800 px into the scratch folder outside the repository, inspect each picture and iterate until each is readable and looks like Minecraft blocks
- [x] 6.2 Run `./gradlew generateDocsImages` twice and confirm `git status --porcelain docs/images` changes only with the sources

## 7. Documentation of the tooling (docs(development))

- [x] 7.1 Update the Illustrations section of `docs/development/documentation.md`: the material colours, the computed tiles, the rule against Minecraft or third-party texture files, the legend, the callouts

## 8. Delivery

- [x] 8.1 Run `./gradlew spotlessApply` and `./gradlew check`; both pass with no sleeps, clock reads or shared static state in the new tests
- [x] 8.2 Push the branch and open the pull request titled `build(docs): draw illustrations with minecraft style blocks`; the body ends with the session link
Loading
Loading