← Basics3D assetfogon/canasto-de-mimbre
Canasto de mimbre
Drag to turn it// «Canasto de mimbre» · gpt-6-sol (medium)
// Wicker hamper («Canasto de mimbre»).
const dark = "#74502e";
const shadow = "#8b6035";
const wicker = ["#a8753e", "#b98347", "#c59152", "#d39e5d"];
const light = "#e0ae6c";
function bounds(z) {
const inset = Math.max(0, Math.floor((27 - z) / 9));
return { x0: 8 + inset, x1: 39 - inset, y0: 9 + inset, y1: 37 - inset };
}
// Tapered, solid basket body. The dark top will show between the clothes.
for (let z = 0; z <= 28; z++) {
const b = bounds(z);
box(b.x0, b.y0, z, b.x1, b.y1, z, z > 25 ? "#624c36" : shadow);
}
// Dense, staggered wicker weave on all four walls.
for (let z = 1; z <= 27; z++) {
const b = bounds(z);
const band = Math.floor(z / 5);
for (let x = b.x0; x <= b.x1; x++) {
for (const [y, face] of [[b.y0 - 1, 0], [b.y1 + 1, 1]]) {
const cell = Math.floor((x + (band % 2) * 3) / 6);
const vertical = x % 6 < 2;
let c = z % 5 === 0 ? dark :
z % 5 === 4 ? shadow :
wicker[(cell + face + (rand() < 0.12 ? 1 : 0)) % 4];
if (vertical && z % 5 !== 0) c = (band + x) % 3 ? "#c18b4c" : light;
set(x, y, z, c);
if (z % 5 === 2 && !vertical && x % 6 !== 5)
set(x, y - (face ? -1 : 1), z, "#d39c59");
}
}
for (let y = b.y0; y <= b.y1; y++) {
for (const [x, face] of [[b.x0 - 1, 0], [b.x1 + 1, 1]]) {
const cell = Math.floor((y + (band % 2) * 3) / 6);
const vertical = y % 6 < 2;
let c = z % 5 === 0 ? dark :
z % 5 === 4 ? shadow :
wicker[(cell + face + 1 + (rand() < 0.1 ? 1 : 0)) % 4];
if (vertical && z % 5 !== 0) c = (band + y) % 3 ? "#b17b40" : "#d7a260";
set(x, y, z, c);
}
}
}
// Raised strands and small shadows where the strips cross.
for (let z = 3; z <= 26; z += 5) {
const b = bounds(z);
for (let x = b.x0 + 2; x < b.x1; x += 6) {
box(x, b.y0 - 2, z, x + 2, b.y0 - 2, z + 1, "#e0aa63");
box(x + 3, b.y1 + 2, z, x + 4, b.y1 + 2, z, "#704b2c");
}
for (let y = b.y0 + 2; y < b.y1; y += 6) {
box(b.x1 + 2, y, z, b.x1 + 2, y + 2, z + 1, "#d29a57");
box(b.x0 - 2, y + 2, z, b.x0 - 2, y + 4, z, "#76502c");
}
}
// Substantial square rim, with a lighter upper edge and corner bindings.
for (let z = 28; z <= 30; z++) {
const c = z === 30 ? light : z === 28 ? "#946634" : "#b88042";
box(7, 8, z, 40, 10, z, c);
box(7, 36, z, 40, 38, z, c);
box(7, 11, z, 9, 35, z, c);
box(38, 11, z, 40, 35, z, c);
}
for (let x = 9; x <= 39; x += 6) {
box(x, 8, 29, x + 1, 9, 31, "#d7a05a");
box(x, 37, 29, x + 1, 38, 31, "#cb9250");
}
for (let y = 13; y <= 34; y += 6) {
box(7, y, 29, 8, y + 1, 31, "#d09a57");
box(39, y, 29, 40, y + 1, 31, "#e0a963");
}
for (const x of [7, 38])
for (const y of [8, 36])
box(x, y, 28, x + 2, y + 2, 31, "#b67c3c");
// Dark gaps inside the opening, underneath the laundry.
box(10, 11, 29, 37, 35, 29, "#594d3e");
// Rumpled sea-glass-colored fabric on the left.
for (let x = 12; x <= 29; x++)
for (let y = 13; y <= 34; y++) {
const d = ((x - 20) / 10) ** 2 + ((y - 23) / 11) ** 2;
if (d < 1 && rand() > 0.12) {
const z = 30 + Math.max(0, Math.floor(2.4 * (1 - d))) +
((x + 2 * y) % 7 === 0 ? 1 : 0);
set(x, y, z, ["#93b4a7", "#b4cec1", "#d4ded0", "#789c91"][Math.floor(rand() * 4)]);
if (rand() < 0.44) set(x, y, z - 1, "#9bb8ad");
}
}
line(14, 24, 33, 22, 20, 33, 0, "#e0e5d9");
line(18, 30, 32, 25, 26, 33, 0, "#638b83");
line(15, 18, 32, 21, 15, 32, 0, "#648d84");
// Off-white laundry piled higher on the right, with readable folded edges.
ellipsoid(30, 24, 31, 7, 10, 2, "#e5e5da");
ellipsoid(32, 19, 33, 5, 6, 2, "#f6f5e9");
ellipsoid(27, 30, 33, 6, 4, 2, "#f0f1e8");
box(25, 16, 34, 35, 17, 34, "#ffffff");
box(29, 20, 35, 36, 21, 35, "#f9faf4");
line(24, 29, 35, 32, 27, 35, 0, "#ffffff");
line(27, 24, 34, 34, 23, 34, 0, "#adbcb7");
line(31, 18, 35, 35, 18, 35, 0, "#b7c4bd");
// A pale towel spilling over the right-hand edge of the rim.
box(38, 15, 21, 40, 23, 29, "#d7ded8");
box(39, 15, 25, 41, 22, 32, "#f0f1e9");
box(41, 16, 22, 41, 19, 29, "#a6bab4");
box(41, 21, 23, 41, 23, 30, "#bacbc5");
box(38, 16, 20, 39, 18, 23, "#b8c9c2");
box(38, 20, 22, 39, 23, 25, "#eef0e9");
line(40, 15, 30, 40, 16, 21, 0, "#ffffff");
// Open wooden lid frame above the hamper: one long slat and its crosspiece.
box(7, 33, 37, 43, 35, 39, "#a97038");
box(8, 33, 40, 43, 34, 40, "#e6b46d");
box(12, 34, 39, 41, 35, 39, "#c48b49");
for (let x = 9; x <= 42; x += 5) {
box(x, 33, 40, x + 1, 34, 40, "#f2c57e");
set(x + 2, 33, 40, "#8d5d30");
}
box(26, 11, 35, 28, 36, 36, "#ac753c");
box(26, 12, 37, 27, 35, 37, "#dfaa61");
for (let y = 14; y <= 34; y += 6)
box(26, y, 37, 27, y + 1, 37, "#f0be75");import { draw } from "./assets/voxel";
import { canastoDeMimbre } from "./assets/canasto-de-mimbre";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, canastoDeMimbre, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: its longest side, in px
// angle: turn on the floor, in radians
// t: seconds; if it moves, that animates it# In a terminal, in the game's folder
fogon assets add canasto-de-mimbre
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["canasto-de-mimbre"] })
# It lands in client/assets/canasto-de-mimbre.ts
# https://fogon.page/object/canasto-de-mimbre