|
@@ -129,6 +129,7 @@ function pieceMontee(): Canvas {
|
|
|
// Nougatine base.
|
|
// Nougatine base.
|
|
|
c.rect(470, 905, 1578, 960, CARAMEL);
|
|
c.rect(470, 905, 1578, 960, CARAMEL);
|
|
|
const R = 40;
|
|
const R = 40;
|
|
|
|
|
+ const FLAT_TOP = 0.8; // the top is cut flat at this fraction of the radius above the center
|
|
|
const STEP_X = 82; // 2 px gap between neighbours: they never touch sideways
|
|
const STEP_X = 82; // 2 px gap between neighbours: they never touch sideways
|
|
|
const STEP_Y = 82;
|
|
const STEP_Y = 82;
|
|
|
const base = 13;
|
|
const base = 13;
|
|
@@ -138,10 +139,18 @@ function pieceMontee(): Canvas {
|
|
|
const cy = 905 - R - 2 - row * STEP_Y;
|
|
const cy = 905 - R - 2 - row * STEP_Y;
|
|
|
for (let i = 0; i < count; i++) {
|
|
for (let i = 0; i < count; i++) {
|
|
|
const cx = cx0 + (i - (count - 1) / 2) * STEP_X;
|
|
const cx = cx0 + (i - (count - 1) / 2) * STEP_X;
|
|
|
- c.disc(cx, cy, R, SPONGE);
|
|
|
|
|
- c.disc(cx, cy, R - 14, CREAM);
|
|
|
|
|
- // Caramel glaze on top and the glue joint with what is below.
|
|
|
|
|
- for (let a = -2.4; a <= -0.7; a += 0.02) c.disc(cx + Math.cos(a) * (R - 4), cy + Math.sin(a) * (R - 4), 5, CARAMEL);
|
|
|
|
|
|
|
+ // The top of each chou is flat (a disc of glaze): somewhere to stand on every step.
|
|
|
|
|
+ const flat = Math.round(cy - R * FLAT_TOP);
|
|
|
|
|
+ for (let y = flat; y <= cy + R; y++) {
|
|
|
|
|
+ for (let x = Math.floor(cx - R); x <= cx + R; x++) {
|
|
|
|
|
+ const d2 = (x - cx) ** 2 + (y - cy) ** 2;
|
|
|
|
|
+ if (d2 > R * R) continue;
|
|
|
|
|
+ c.put(x, y, d2 <= (R - 14) ** 2 ? CREAM : SPONGE);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // Caramel glaze on the flat top, and the glue joint with what is below.
|
|
|
|
|
+ const half = Math.sqrt(R * R - (flat - cy) ** 2);
|
|
|
|
|
+ c.rect(Math.round(cx - half + 3), flat, Math.round(cx + half - 2), flat + 5, CARAMEL);
|
|
|
c.disc(cx, cy + R + 1, 11, CARAMEL);
|
|
c.disc(cx, cy + R + 1, 11, CARAMEL);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -162,6 +171,7 @@ function roule(): Canvas {
|
|
|
c.rect(cx - 180, 900, cx + 181, 960, BISCUIT);
|
|
c.rect(cx - 180, 900, cx + 181, 960, BISCUIT);
|
|
|
const SPACING = 64; // distance between two turns of the spiral
|
|
const SPACING = 64; // distance between two turns of the spiral
|
|
|
const JAM = 12;
|
|
const JAM = 12;
|
|
|
|
|
+ const WAFER = 16;
|
|
|
for (let y = cy - R; y <= cy + R; y++) {
|
|
for (let y = cy - R; y <= cy + R; y++) {
|
|
|
for (let x = cx - R; x <= cx + R; x++) {
|
|
for (let x = cx - R; x <= cx + R; x++) {
|
|
|
const dx = x - cx;
|
|
const dx = x - cx;
|
|
@@ -178,6 +188,21 @@ function roule(): Canvas {
|
|
|
c.put(x, y, phase < JAM && r > 24 ? JELLY : SPONGE);
|
|
c.put(x, y, phase < JAM && r > 24 ? JELLY : SPONGE);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ // Wafer sticks planted in the roll: flat ledges to stand on around the round sides.
|
|
|
|
|
+ for (const [side, dy] of [
|
|
|
|
|
+ [-1, -170],
|
|
|
|
|
+ [1, -90],
|
|
|
|
|
+ [-1, 60],
|
|
|
|
|
+ [1, 160],
|
|
|
|
|
+ [-1, 230],
|
|
|
|
|
+ ]) {
|
|
|
|
|
+ const y = cy + dy;
|
|
|
|
|
+ const edge = cx + side * Math.sqrt(R * R - dy * dy);
|
|
|
|
|
+ const inner = Math.round(edge - side * 50);
|
|
|
|
|
+ const outer = Math.round(edge + side * 120);
|
|
|
|
|
+ c.rect(Math.min(inner, outer), y, Math.max(inner, outer), y + WAFER, BISCUIT);
|
|
|
|
|
+ c.disc(outer, y + WAFER / 2 - 0.5, WAFER / 2, BISCUIT);
|
|
|
|
|
+ }
|
|
|
return c;
|
|
return c;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -216,31 +241,40 @@ function tarteFruits(): Canvas {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * La Pile de Crêpes: dozens of thin crêpes with spreads in between (chocolate, jam, caramel),
|
|
|
|
|
- * stacked into a pile that leans. Side wind (modifier).
|
|
|
|
|
|
|
+ * La Pile de Crêpes: thick crêpes with soft spreads in between (jam and whipped cream, never
|
|
|
|
|
+ * chocolate: every layer can be dug through), stacked into a big pile that leans a little, and
|
|
|
|
|
+ * a small pile beside it. Side wind (modifier).
|
|
|
*/
|
|
*/
|
|
|
function crepes(): Canvas {
|
|
function crepes(): Canvas {
|
|
|
const c = new Canvas();
|
|
const c = new Canvas();
|
|
|
c.plate();
|
|
c.plate();
|
|
|
- const SPREADS = [CHOCOLATE, JELLY, CARAMEL];
|
|
|
|
|
- const CREPE = 9;
|
|
|
|
|
- const SPREAD = 3;
|
|
|
|
|
- const lean = 0.22; // horizontal shift per pixel of height
|
|
|
|
|
- let layer = 0;
|
|
|
|
|
- for (let top = 960; top > 340; top -= CREPE + SPREAD, layer++) {
|
|
|
|
|
- const shift = Math.round((960 - top) * lean);
|
|
|
|
|
- const halfWidth = 330 - Math.round(layer * 1.5) + Math.round(6 * Math.sin(layer * 1.7));
|
|
|
|
|
- const left = 860 - halfWidth + shift;
|
|
|
|
|
- const right = 860 + halfWidth + shift;
|
|
|
|
|
- // Rounded crêpe ends: the sides read as a stack of pancakes, not a staircase.
|
|
|
|
|
- const r = CREPE / 2;
|
|
|
|
|
- c.rect(left + Math.floor(r), top - CREPE, right - Math.floor(r), top, SPONGE);
|
|
|
|
|
- c.disc(left + r - 0.5, top - r - 0.5, r, SPONGE);
|
|
|
|
|
- c.disc(right - r - 0.5, top - r - 0.5, r, SPONGE);
|
|
|
|
|
- if (top - CREPE - SPREAD > 340) c.rect(left + 12, top - CREPE - SPREAD, right - 12, top - CREPE, SPREADS[layer % 3]);
|
|
|
|
|
- }
|
|
|
|
|
- // Whipped cream on top.
|
|
|
|
|
- c.disc(860 + Math.round(620 * lean), 345, 40, CREAM);
|
|
|
|
|
|
|
+ const SPREADS = [JELLY, CREAM];
|
|
|
|
|
+ const CREPE = 20;
|
|
|
|
|
+ const SPREAD = 6;
|
|
|
|
|
+ const pile = (cx: number, bottom: number, top: number, halfWidth: number, lean: number): [number, number] => {
|
|
|
|
|
+ let layer = 0;
|
|
|
|
|
+ let y = bottom;
|
|
|
|
|
+ for (; y - CREPE > top; y -= CREPE + SPREAD, layer++) {
|
|
|
|
|
+ const shift = Math.round((bottom - y) * lean);
|
|
|
|
|
+ const half = halfWidth - layer * 3 + Math.round(8 * Math.sin(layer * 1.7));
|
|
|
|
|
+ const left = cx - half + shift;
|
|
|
|
|
+ const right = cx + half + shift;
|
|
|
|
|
+ // Rounded crêpe ends: the sides read as a stack of pancakes, not a staircase.
|
|
|
|
|
+ const r = CREPE / 2;
|
|
|
|
|
+ c.rect(left + r, y - CREPE, right - r, y, SPONGE);
|
|
|
|
|
+ c.disc(left + r - 0.5, y - r - 0.5, r, SPONGE);
|
|
|
|
|
+ c.disc(right - r - 0.5, y - r - 0.5, r, SPONGE);
|
|
|
|
|
+ // The spread oozes almost to the edge.
|
|
|
|
|
+ c.rect(left + 10, y - CREPE - SPREAD, right - 10, y - CREPE, SPREADS[layer % SPREADS.length]);
|
|
|
|
|
+ for (const x of [left + 10, right - 11]) c.disc(x, y - CREPE - SPREAD / 2 - 0.5, SPREAD / 2, SPREADS[layer % SPREADS.length]);
|
|
|
|
|
+ }
|
|
|
|
|
+ return [cx + Math.round((bottom - y) * lean), y - CREPE];
|
|
|
|
|
+ };
|
|
|
|
|
+ const [topX, topY] = pile(820, 960, 380, 330, 0.1);
|
|
|
|
|
+ // Whipped cream and a jam strawberry on top.
|
|
|
|
|
+ c.disc(topX, topY + 4, 46, CREAM);
|
|
|
|
|
+ c.disc(topX + 10, topY - 36, 20, JELLY);
|
|
|
|
|
+ pile(1520, 960, 720, 190, -0.06);
|
|
|
return c;
|
|
return c;
|
|
|
}
|
|
}
|
|
|
|
|
|