Skip to content

Commit a6ddd76

Browse files
authored
feat: implement pixel-draw-renderer v1.0 (#237)
1 parent 3380d96 commit a6ddd76

31 files changed

+3381
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Click on one of the links to access the documentation of the package:
3838
| engine | [@jolly-pixel/engine](./packages/engine) | ECS framework on top of Three.js |
3939
| runtime | [@jolly-pixel/runtime](./packages/runtime) | Runtime for the engine / ECS |
4040
| voxel-renderer | [@jolly-pixel/voxel.renderer](./packages/voxel-renderer) | Voxel Engine and Renderer |
41+
| pixel-draw-renderer | [@jolly-pixel/pixel-draw.renderer](./packages/pixel-draw-renderer) | Pixel Art draw renderer |
4142
| fs-tree | [@jolly-pixel/fs-tree](./packages/fs-tree) | Robust, stylable tree view widget for HTML5 apps with drag'n'drop support |
4243

4344
These packages are available in the Node Package Repository and can be easily installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or [yarn](https://yarnpkg.com).

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"packages/fs-tree-backend",
2424
"packages/editors/voxel-map",
2525
"packages/editors/texture",
26-
"packages/voxel-renderer"
26+
"packages/voxel-renderer",
27+
"packages/pixel-draw-renderer"
2728
],
2829
"repository": {
2930
"type": "git",
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<h1 align="center">
2+
pixel-draw.renderer
3+
</h1>
4+
5+
<p align="center">
6+
JollyPixel Pixel Art canvas renderer
7+
</p>
8+
9+
## About
10+
11+
`@jolly-pixel/pixel-draw.renderer` is a browser-based library for editing pixel-art textures. It provides zoom, pan, brush painting, right-click color picking, and an SVG cursor overlay, built around a SOLID-structured class architecture.
12+
13+
## Features
14+
15+
- **Zoom & pan** — smooth mouse-wheel zoom with configurable sensitivity and range; middle-click pan in any mode
16+
- **Brush painting** — configurable square brush with adjustable size, color, and opacity
17+
- **Color picking** — right-click eyedropper that reads the master canvas pixel
18+
- **Transparency support** — configurable checkerboard background renders beneath transparent pixels
19+
- **SVG brush highlight** — grid-aligned SVG overlay tracks the cursor in real time
20+
- **Dual-canvas architecture** — a master canvas (full resolution, off-screen) and a working canvas (viewport-cropped, on-screen) maintain pixel-perfect fidelity at any zoom level
21+
- **Mode switching**`"paint"` and `"move"` modes control how mouse events are interpreted
22+
23+
## Getting Started
24+
25+
This package is available in the Node Package Repository and can be easily installed with [npm][npm] or [yarn][yarn].
26+
27+
```bash
28+
$ npm i @jolly-pixel/pixel-draw.renderer
29+
# or
30+
$ yarn add @jolly-pixel/pixel-draw.renderer
31+
```
32+
33+
## Usage Examples
34+
35+
### Minimal setup
36+
37+
```ts
38+
import { CanvasManager } from "@jolly-pixel/pixel-draw.renderer";
39+
40+
const manager = new CanvasManager({
41+
texture: { size: 64 },
42+
zoom: {
43+
range: [0.5, 40],
44+
sensitivity: 0.002
45+
},
46+
});
47+
48+
const container = document.getElementById("editor-container")!;
49+
manager.reparentCanvasTo(container);
50+
manager.resize();
51+
manager.centerTexture();
52+
```
53+
54+
### Drawing pixels programmatically
55+
56+
```ts
57+
import { CanvasManager } from "@jolly-pixel/pixel-draw.renderer";
58+
59+
const manager = new CanvasManager({
60+
texture: { size: 32 }
61+
});
62+
manager.reparentCanvasTo(document.body);
63+
64+
// Draw a red pixel at texture position (10, 10)
65+
manager.textureBuffer.drawPixels(
66+
[{ x: 10, y: 10 }],
67+
{ r: 255, g: 0, b: 0, a: 255 }
68+
);
69+
manager.render();
70+
```
71+
72+
### Loading an existing texture
73+
74+
```ts
75+
const img = new Image();
76+
img.src = "/assets/sprite.png";
77+
await img.decode();
78+
79+
manager.setTexture(img);
80+
```
81+
82+
### Configuring the brush
83+
84+
```ts
85+
manager.brush.setColor("#FF6600");
86+
manager.brush.setOpacity(0.8);
87+
manager.brush.setSize(3);
88+
```
89+
90+
### Switching modes
91+
92+
```ts
93+
manager.setMode("move"); // left-click pans
94+
manager.setMode("paint"); // left-click draws
95+
```
96+
97+
## Running the Examples
98+
99+
```bash
100+
npm run dev -w @jolly-pixel/pixel-draw.renderer
101+
```
102+
103+
Open `http://localhost:5173` to see the interactive demo.
104+
105+
## API
106+
107+
| Class | Description |
108+
|---|---|
109+
| [`CanvasManager`](./docs/CanvasManager.md) | Top-level coordinator — the primary public API |
110+
| [`Viewport`](./docs/Viewport.md) | Camera position, zoom level, and coordinate transforms |
111+
| [`BrushManager`](./docs/BrushManager.md) | Brush size, color, opacity, and affected-pixel computation |
112+
| `TextureBuffer` | Dual-canvas pixel storage and image-data access |
113+
| `CanvasRenderer` | Visible canvas drawing and checkerboard background |
114+
| `InputController` | Mouse event routing to drawing and pan actions |
115+
| `SvgManager` | SVG brush-highlight overlay |
116+
117+
## Troubleshooting
118+
119+
**Canvas is blank after mounting**
120+
Call `manager.resize()` after `reparentCanvasTo()` to let the renderer read the parent element's dimensions, then call `manager.centerTexture()`.
121+
122+
**Pixels appear at the wrong position**
123+
Pass `{ bounds: canvas.getBoundingClientRect() }` when calling `viewport.getMouseTexturePosition()`. Stale bounding rects cause offset errors.
124+
125+
**Master canvas is slow to initialize**
126+
`TextureBuffer` pre-allocates a canvas at `maxSize` (default `2048`). In test environments or when large textures are unnecessary, set `texture.maxSize` to a smaller value such as `64`.
127+
128+
## Contributors Guide
129+
130+
If you are a developer **looking to contribute** to the project, you must first read the [CONTRIBUTING][contributing] guide.
131+
132+
Once you have finished your development, check that the tests (and linter) are still good by running the following script:
133+
134+
```bash
135+
$ npm run test
136+
$ npm run lint
137+
```
138+
139+
> [!CAUTION]
140+
> In case you introduce a new feature or fix a bug, make sure to include tests for it as well.
141+
142+
## License
143+
144+
MIT
145+
146+
<!-- Reference-style links for DRYness -->
147+
148+
[npm]: https://docs.npmjs.com/getting-started/what-is-npm
149+
[yarn]: https://yarnpkg.com
150+
[contributing]: ../../CONTRIBUTING.md
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# BrushManager
2+
3+
`BrushManager` manages the current brush color, opacity, size, and highlight colors, and computes the list of texture-space pixels a brush stroke covers.
4+
5+
## Import
6+
7+
```ts
8+
import { BrushManager } from "@jolly-pixel/pixel-draw.renderer";
9+
```
10+
11+
## Constructor
12+
13+
```ts
14+
new BrushManager(options: BrushManagerOptions)
15+
```
16+
17+
### `BrushManagerOptions`
18+
19+
| Property | Type | Default | Description |
20+
|---|---|---|---|
21+
| `color` | `string` | `"#000000"` | Initial brush color (CSS hex) |
22+
| `size` | `number` | `1` | Initial brush size in pixels |
23+
| `maxSize` | `number` | `32` | Upper bound for brush size |
24+
| `highlightColorInline` | `string` | `"#FFFFFF"` | SVG overlay inner stroke color |
25+
| `highlightColorOutline` | `string` | `"#000000"` | SVG overlay outer stroke color |
26+
27+
## Properties
28+
29+
| Property | Type | Description |
30+
|---|---|---|
31+
| `color` | `string` | Current brush color as a CSS hex string |
32+
| `opacity` | `number` | Current opacity in the range `[0, 1]` |
33+
| `size` | `number` | Current brush size in pixels |
34+
| `maxSize` | `number` | Maximum allowed brush size |
35+
| `r` / `g` / `b` | `number` | Current color channels in the range `[0, 255]` |
36+
37+
## Methods
38+
39+
### `setColor`
40+
41+
```ts
42+
setColor(color: string): void
43+
```
44+
45+
Sets the brush color from a CSS hex string (e.g. `"#FF0000"`). Updates the internal `r`, `g`, `b` components accordingly.
46+
47+
---
48+
49+
### `setOpacity`
50+
51+
```ts
52+
setOpacity(opacity: number): void
53+
```
54+
55+
Sets the brush opacity. Values are clamped to `[0, 1]`.
56+
57+
---
58+
59+
### `setSize`
60+
61+
```ts
62+
setSize(size: number): void
63+
```
64+
65+
Sets the brush size in pixels. Values are clamped to `[1, maxSize]`.
66+
67+
---
68+
69+
### `getHighlightColorInline` / `setHighlightColorInline`
70+
71+
```ts
72+
getHighlightColorInline(): string
73+
setHighlightColorInline(color: string): void
74+
```
75+
76+
Gets or sets the inner stroke color of the SVG brush cursor overlay.
77+
78+
---
79+
80+
### `getHighlightColorOutline` / `setHighlightColorOutline`
81+
82+
```ts
83+
getHighlightColorOutline(): string
84+
setHighlightColorOutline(color: string): void
85+
```
86+
87+
Gets or sets the outer stroke color of the SVG brush cursor overlay.
88+
89+
---
90+
91+
### `getAffectedPixels`
92+
93+
```ts
94+
getAffectedPixels(cx: number, cy: number): Vec2[]
95+
```
96+
97+
Returns an array of texture-space `{ x, y }` coordinates for every pixel within the current brush square centered at `(cx, cy)`.
98+
99+
- For **odd** brush sizes the center pixel is exactly `(cx, cy)`.
100+
- For **even** brush sizes the brush is offset by `−0.5` to remain grid-aligned.
101+
102+
**Example**
103+
104+
```ts
105+
// size = 3 → 9 pixels around (10, 10)
106+
const pixels = brush.getAffectedPixels(10, 10);
107+
textureBuffer.drawPixels(pixels, { r: 255, g: 0, b: 0, a: 255 });
108+
```

0 commit comments

Comments
 (0)