Published
Edited
Mar 6, 2021
Fork of 3D Labyrinth
7 forks
35 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
brazilian_art = FileAttachment("brazilian_art@1.csv").csv()
Insert cell
modern_art = FileAttachment("modern_art@1.csv").csv()
Insert cell
kinetic_art = FileAttachment("kinetic_art.csv").csv()
Insert cell
louvre = FileAttachment("louvre.csv").csv()
Insert cell
british_museum = FileAttachment("british_museum.csv").csv()
Insert cell
Insert cell
p5showcase = FileAttachment("p5showcase.csv").csv()
Insert cell
Insert cell
//
// Options for the collection input. An array of objects containing fields
// name, info, and size, respectively the title of the collection, an array of paintings
// and the size of the labirynth (either a an integer or an array of integers.)
//
Insert cell
collections = window.defCollections || [
{ name: "Brazilian Art", info: brazilian_art, size: 5 },
{ name: "Miscellanea", info: miscellanea, size: 3 },
{ name: "Louvre", info: louvre, size: [8, 6] },
{ name: "British Museum", info: british_museum, size: [14, 11] },
{ name: "National Gallery of Art", info: national_gallery, size: [10, 7] },
{ name: "Modern art", info: modern_art, size: 4 },
{ name: "Kinetic art", info: kinetic_art, size: 3 },
{ name: "p5js Showcase 2020", info: p5showcase, size: [8, 6] }
]
Insert cell
//
// Ratio of the labyrinth walls to be torn down
// 0 means perfect labyrinth and 1 means a rectangular room with no walls
//
Insert cell
Insert cell
//
// Thickness of the walls w.r.t. cell.
// 1 means walls as thick as cells. 0 means paper-thin walls (not recommended)
//
Insert cell
Insert cell
//
// Camera angle in degrees.
// Useful values range from roughly 60 to 140.
// Small angles mean less distortion, but, since the viewer stays at the center of
// cells, paintings will appear larger. Bigger values result in bigger distortions, but paintings can be put
// closer together and still appear small. Use paintingSizeRatio to control overall painting size.
//
Insert cell
Insert cell
//
// Painting size with respect to each wall cell.
// Values closer to 1 make paintings bigger.
//
Insert cell
Insert cell
//
// Background color of navigation buttons
//
Insert cell
Insert cell
//
// Color of arrows in navigation buttons
//
Insert cell
Insert cell
//
// Ratio between button size and width
//
Insert cell
Insert cell
//
// Ratio between width of button and size of the arrow
//
Insert cell
Insert cell
//
// These configure the button icons used in the interface
//
Insert cell
Insert cell
//
// width of the canvas
//
Insert cell
width = window.defWidth || window.innerWidth * 0.98
Insert cell
//
// Height of the canvas. By default set to 9/16 of the width (wide-screen aspect ratio)
//
Insert cell
Insert cell
//
// If true, removes the blocks that make the ceiling
//
Insert cell
Insert cell
//
// An array of 6 image urls used for the scene cubemap
//
Insert cell
cubemapUrls = window.defCubemapUrls || [
await FileAttachment('px.png').url(),
await FileAttachment('nx.png').url(),
await FileAttachment('py.png').url(),
await FileAttachment('ny.png').url(),
await FileAttachment('pz.png').url(),
await FileAttachment('nz.png').url()
]
Insert cell
Insert cell
canvas = DOM.canvas(width, height)
Insert cell
buttons = {
collection;
let buttons = {};
let size = width * buttonSizeRatio;
for (let prop of Object.getOwnPropertyNames(buttonIcons)) {
buttons[prop] = html`<div style="border-radius:200px;padding:1px 1px;
max-width:${size}px;width:${size}px;max-height:${size}px;height:${size}px;line-height:${size}px;
border:1px solid gray;display:inline-block;position:relative;
background:${buttonColor}">
<span style='font-size:${width * buttonSizeRatio * buttonSymbolSizeRatio}px;
color:${buttonSymbolColor}'>${buttonIcons[prop]}</span></div>`;
}
return buttons;
}
Insert cell
title = {
let title = html`<div></div>`;
title.id = "title";
title.style.background = "white";
title.style.border = "1px solid gray";
title.style["border-radius"] = "15px";
title.style["padding"] = "2px 10px";
title.style.display = "inline-block";
title.style.margin = "0 auto";
title.style["margin-top"] = "20px";
title.style.position = "relative";
title.style["z-index"] = "10";
title.style["box-shadow"] = "4px 4px 5px 0px rgba(0,0,0,.2)";
title.style["pointer-events"] = "auto";
title.style["cursor"] = "pointer";
title.style["user-select"] = "none";
title.append(html`<i>Click to open popup</i>`);
return title;
}
Insert cell
loadBar = {
let bar = html`<div></div>`;
bar.style.width = `70%`;
bar.style.backgroundColor = 'lightgray';
bar.style.border = '2px solid gray';
bar.style.display = "inline-block";
bar.style.margin = "0 auto";
bar.style["margin-top"] = `${height * 0.45}px`;
bar.style.position = "relative";
bar.style["z-index"] = "10";
bar.style["cursor"] = "wait";
bar.style.display = "none";

let progress = html`<div></div>`;
progress.style.width = `10%`;
progress.style.height = `${height * 0.05}px`;
progress.style.backgroundColor = 'darkgray';
bar.append(progress);
bar.setProgress = x => (progress.style.width = `${x}%`);

return bar;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// showLabyrinth(labyrinth)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
cubemapImages = {
return await Promise.all(cubemapUrls.map(url => afterDecode(url)));
}
Insert cell
Insert cell
Insert cell
Insert cell
//
// Returns a group with all 3d elements of the maze, including
// the walls, floor and sprites
//
cubes = {
decimate; // Regenerate after decimation of walls, if any

let materials = [];
let geometries = [[[], []], [[], []]];
let getCubeGeometry = (i, j, k) =>
geometries[i % 2][j % 2][k % 2] ||
(geometries[i % 2][j % 2][k % 2] = new THREE.BoxBufferGeometry(
...boxSize(i, j, k).map(x => x)
));
let getCubeMaterial = i =>
materials[i] ||
(materials[i] = new THREE.MeshStandardMaterial({
color: new THREE.Color().setHSL(Math.random(), 1, 0.5)
}));
let beamMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff });
let floorMaterial = new THREE.MeshStandardMaterial({ color: 0x777777 });
let cubes = new THREE.Group();

let spriteMap = new THREE.CanvasTexture(
await FileAttachment('whitecheck.png').image()
);
let checkMaterial = new THREE.SpriteMaterial({ map: spriteMap });
spriteMap = new THREE.CanvasTexture(await FileAttachment('exit.png').image());
let exitMaterial = new THREE.SpriteMaterial({ map: spriteMap });

let startCell = labyrinth.longestPath[0];
let pathLen = labyrinth.longestPath.length;
let endCell = labyrinth.longestPath[pathLen - 1];
labyrinth.checkFlags = {};

let [jmin, jmax] = removeCeiling
? [0, labyrinth[0].length - 1]
: [0, labyrinth[0].length];
for (let i = 0; i < labyrinth.length; i++) {
for (let j = jmin; j < jmax; j++) {
for (let k = 0; k < labyrinth[i][j].length; k++) {
let cubetype = labyrinth[i][j][k];
if (cubetype != '.') {
let isFloor = j == 0 && (i & 1) + (k & 1) == 2;
let material = isFloor ? floorMaterial : beamMaterial;
let cube = new THREE.Mesh(getCubeGeometry(i, j, k), material);
cube.isFloor = isFloor;
cube.gridCoords = [i, j, k];
cube.position.set(...boxPos(i, j, k));
cubes.add(cube);
} else if (breadCrumbs && i & j & k & 1) {
let flag = new THREE.Sprite(checkMaterial);
flag.visible = false;
labyrinth.checkFlags[[i, j, k]] = flag;
let [x, y, z] = boxPos(i, j, k);
flag.position.set(x, y - 0.4, z);
flag.scale.set(0.1, 0.1, 0.1);
cubes.add(flag);
}
}
}
}

return cubes;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mutable imgStat = ({ success: 0, fail: 0 })
Insert cell
Insert cell
Insert cell
THREE = require('three@0.119.1')
Insert cell
import {textGeometry} from '@esperanc/3d-fonts'
Insert cell
import {
Button,
Checkbox,
Toggle,
Radio,
Range,
Select,
Text,
Search,
Table
} from "@observablehq/inputs"
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more