-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathterrain.js
More file actions
74 lines (69 loc) · 2.27 KB
/
Copy pathterrain.js
File metadata and controls
74 lines (69 loc) · 2.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Terrain rasterisation. Paint is stored as vector strokes on each terrain layer;
// the raster is a runtime cache rebuilt from strokes when dirty (or when the theme
// changes). Live painting and rebuild share paintStroke() so they look identical.
const MATERIAL_KEYS = {
grass: 'grass',
forest: 'forest',
water: 'water',
waterDeep: 'waterDeep',
sand: 'sand',
stone: 'stone',
snow: 'snow',
dirt: 'dirt',
road: 'road',
};
export function materialColor(theme, material) {
return theme.terrain[MATERIAL_KEYS[material] || 'grass'] || theme.terrain.grass;
}
// Draw one stroke. `ctx` is in world space (sheet origin = 0,0). When painting
// live, pass `fromIndex` to draw only the newly-added segment for responsiveness.
export function paintStroke(ctx, stroke, theme, fromIndex = 0) {
const pts = stroke.pts;
if (!pts || pts.length === 0) return;
ctx.save();
ctx.globalCompositeOperation = stroke.erase ? 'destination-out' : 'source-over';
ctx.globalAlpha = stroke.erase ? 1 : stroke.opacity ?? 1;
const color = materialColor(theme, stroke.material);
ctx.strokeStyle = color;
ctx.fillStyle = color;
ctx.lineWidth = stroke.size;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
if (pts.length === 1) {
ctx.beginPath();
ctx.arc(pts[0].x, pts[0].y, stroke.size / 2, 0, Math.PI * 2);
ctx.fill();
} else {
const start = Math.max(0, fromIndex - 1);
ctx.beginPath();
ctx.moveTo(pts[start].x, pts[start].y);
for (let i = start + 1; i < pts.length; i++) ctx.lineTo(pts[i].x, pts[i].y);
ctx.stroke();
}
ctx.restore();
}
export function rebuildTerrain(layer, w, h, theme) {
let cv = layer._raster;
if (!cv || cv.width !== w || cv.height !== h) {
cv = document.createElement('canvas');
cv.width = w;
cv.height = h;
layer._raster = cv;
}
const ctx = cv.getContext('2d');
ctx.clearRect(0, 0, w, h);
for (const s of layer.strokes) paintStroke(ctx, s, theme);
layer._dirty = false;
layer._rasterTheme = theme.id;
return cv;
}
export function ensureTerrainRaster(layer, w, h, theme) {
const stale =
!layer._raster ||
layer._raster.width !== w ||
layer._raster.height !== h ||
layer._dirty ||
layer._rasterTheme !== theme.id;
if (stale) return rebuildTerrain(layer, w, h, theme);
return layer._raster;
}