Files
cadSet/text-to-cad/models/implicits/pythagoras-tree-solid.implicit.js
T
2026-07-22 19:38:36 +08:00

78 lines
3.4 KiB
JavaScript

const GLSL = `
float sdBox(vec3 p, vec3 b) {
vec3 q = abs(p) - b;
return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
}
float branchBox(vec3 p, vec2 center, vec2 halfSize, float zCenter) {
return sdBox(p - vec3(center, zCenter), vec3(halfSize, thickness * 0.5));
}
float sdf(vec3 p) {
float d = branchBox(p, vec2(0.0, -size * 0.25), vec2(size * 0.55, size * 0.32), 0.0);
d = min(d, branchBox(p, vec2(-size * 0.36, size * 0.34), vec2(size * 0.36, size * 0.24), thickness * 0.04));
d = min(d, branchBox(p, vec2(size * 0.36, size * 0.34), vec2(size * 0.36, size * 0.24), thickness * 0.04));
if (levels > 1.5) {
d = min(d, branchBox(p, vec2(-size * 0.72, size * 0.86), vec2(size * 0.24, size * 0.18), thickness * 0.1));
d = min(d, branchBox(p, vec2(-size * 0.16, size * 0.92), vec2(size * 0.22, size * 0.17), thickness * 0.1));
d = min(d, branchBox(p, vec2(size * 0.16, size * 0.92), vec2(size * 0.22, size * 0.17), thickness * 0.1));
d = min(d, branchBox(p, vec2(size * 0.72, size * 0.86), vec2(size * 0.24, size * 0.18), thickness * 0.1));
}
if (levels > 2.5) {
d = min(d, branchBox(p, vec2(-size * 0.95, size * 1.22), vec2(size * 0.16, size * 0.12), thickness * 0.16));
d = min(d, branchBox(p, vec2(-size * 0.52, size * 1.32), vec2(size * 0.14, size * 0.11), thickness * 0.16));
d = min(d, branchBox(p, vec2(0.0, size * 1.38), vec2(size * 0.14, size * 0.11), thickness * 0.16));
d = min(d, branchBox(p, vec2(size * 0.52, size * 1.32), vec2(size * 0.14, size * 0.11), thickness * 0.16));
d = min(d, branchBox(p, vec2(size * 0.95, size * 1.22), vec2(size * 0.16, size * 0.12), thickness * 0.16));
}
return d - cornerRound;
}
vec3 color(vec3 p, vec3 normal) {
float leaf = smoothstep(size * 0.35, size * 1.35, p.y);
vec3 c = mix(baseColor, accentColor, leaf);
return c * (0.58 + 0.42 * smoothstep(-0.1, 0.9, normal.z));
}
`;
export default {
schema: "implicit.js/0.1.0",
name: "Pythagoras tree solid",
description: "A stable 3D Pythagoras tree made from recursively arranged softened blocks.",
units: "mm",
params: {
size: { type: "number", label: "Base size", min: 8, max: 22, step: 0.5, default: 14, unit: "mm" },
levels: { type: "number", label: "Levels", min: 1, max: 3, step: 1, default: 3 },
thickness: { type: "number", label: "Thickness", min: 1.5, max: 7, step: 0.1, default: 3.0, unit: "mm" },
cornerRound: { type: "number", label: "Corner round", min: 0.05, max: 1.2, step: 0.05, default: 0.35, unit: "mm" },
baseColor: { type: "color", label: "Trunk color", default: "#ff9f1c" },
accentColor: { type: "color", label: "Canopy color", default: "#65f58a" }
},
animations: {
canopySway: {
label: "Canopy sway",
duration: 5.8,
loop: true,
update({ progress, set }) {
const wave = Math.sin(progress * Math.PI * 2);
set("cornerRound", 0.35 + (wave * 0.5 + 0.5) * 0.55);
set("thickness", 3.0 + wave * 0.7);
}
}
},
bounds: ({ params }) => {
const x = params.size * 1.25 + 6;
const y = params.size * 1.65 + 6;
const z = params.thickness + params.cornerRound + 4;
return [[-x, -params.size - 4, -z], [x, y, z]];
},
render: ({ params }) => ({
steps: 180,
stepScale: 0.72,
maxStep: Math.max(params.cornerRound * 5, 1.0),
epsilon: Math.max(params.cornerRound * 0.012, 0.004),
normalEpsilon: Math.max(params.cornerRound * 0.12, 0.035)
}),
glsl: GLSL
};