Public
Edited
May 18
16 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// return "#B0B7BC";
// } else {
// return "#CE0F3D";
Insert cell
c1 = "#b3bed2"
Insert cell
c2 = "#56679f"
Insert cell
function make_penrose_tiling() {
let svg_width = 800;
let svg_height = (2 * svg_width) / phi ** 2;
let xmin = 0;
let xmax = phi;
let ymin = -1 / phi;
let ymax = 1 / phi;

let svg = d3
.create("svg")
.style("max-width", `${svg_width}px`)
.attr("viewBox", [0, 0, svg_width, svg_height]);
// .attr("width", svg_width)
// .attr("height", svg_height);

let x_scale = d3.scaleLinear().domain([xmin, xmax]).range([0, svg_width]);
let y_scale = d3.scaleLinear().domain([ymin, ymax]).range([svg_height, 0]);
let pts_to_path = d3
.line()
.x(function (d) {
return x_scale(d[0]);
})
.y(function (d) {
return y_scale(d[1]);
});

svg
.selectAll("path")
.data(triangles)
.join("path")
// Piece together the triangles to form either Rhombs or Kites and Darts
// depending whether depth is odd or even
.attr("d", function (d) {
if (depth % 2 == 0) {
return pts_to_path([d.vertices[1], d.vertices[2], d.vertices[0]]);
} else {
if (d.type == "a") {
return pts_to_path([d.vertices[2], d.vertices[0], d.vertices[1]]);
} else {
return pts_to_path([d.vertices[0], d.vertices[1], d.vertices[2]]);
}
}
})
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("fill", function (d) {
if (d.type == "o") {
return c1;
} else {
return c2;
}
});

if (show_diag) {
svg
.selectAll("path.diag")
.data(triangles)
.join("path")
.attr("d", function (d) {
if (depth % 2 == 0) {
return pts_to_path([d.vertices[1], d.vertices[0]]);
} else {
if (d.type == "a") {
return pts_to_path([d.vertices[2], d.vertices[1]]);
} else {
return pts_to_path([d.vertices[0], d.vertices[2]]);
}
}
})
.attr("stroke", "black")
.attr("stroke-width", 0.6)
.attr("stroke-dasharray", "5,5")
.attr("fill", null);
} else {
svg
.selectAll("path.diag")
.data(triangles)
.join("path")
.attr("d", function (d) {
if (depth % 2 == 0) {
return pts_to_path([d.vertices[1], d.vertices[0]]);
} else {
if (d.type == "a") {
return pts_to_path([d.vertices[2], d.vertices[1]]);
} else {
return pts_to_path([d.vertices[0], d.vertices[2]]);
}
}
})
.attr("stroke", function (d) {
if (d.type == "o") {
return c1;
} else {
return c2;
}
})
.attr("stroke-width", 1)
.attr("fill", null);
}
return svg.node();
}
Insert cell
triangles = {
let triangles = [S];
for (let i = 0; i < depth; i++) {
triangles = triangles.map(dissect(i)).reduce(function(a, c) {
return a.concat(c);
}, []);
}
triangles = triangles.concat(
triangles.map(function(o) {
let newo = Object.assign({}, o);
newo.vertices = o.vertices.map(function(xy) {
let x = xy[0];
let y = -xy[1];
return [x, y];
});
return newo;
})
);
return triangles;
}
Insert cell
// When i is even, we dissect the acute triangles but
// when i is odd, we dissect the obtuse triangles.
function dissect(i) {
let i_dissect = function(T) {
let T1, T2;
let [p, q, r] = T.vertices;
if (T.type == 'a') {
if (i % 2 == 0) {
let new_point = [
(phi * q[0] + p[0]) * (2 - phi),
(phi * q[1] + p[1]) * (2 - phi)
];
T1 = {
vertices: [r, new_point, q],
type: 'a'
};
T2 = {
vertices: [r, new_point, p],
type: 'o'
};
return [T1, T2];
} else {
return [T];
}
} else if (T.type == 'o') {
if (i % 2 == 1) {
let new_point = [
(phi * r[0] + p[0]) * (2 - phi),
(phi * r[1] + p[1]) * (2 - phi)
];
T1 = {
vertices: [r, new_point, q],
type: 'o'
};
T2 = {
vertices: [p, q, new_point],
type: 'a'
};
return [T1, T2];
} else {
return [T];
}
}
};

return i_dissect;
}
Insert cell
// Start with a single obtuse triangle
S = ({
vertices: [[phi, 0], [phi / 2, Math.sin(Math.PI / 5)], [0, 0]],
type: 'o'
})
Insert cell
phi = (Math.sqrt(5) + 1) / 2
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