test face
by mcoirad
HTML
<div id="controls"></div>
<div id="stage"></div>
CSS
body {
font-family: system-ui, sans-serif;
display: grid;
grid-template-columns: 260px 1fr;
gap: 20px;
padding: 20px;
}
label {
display: block;
margin-bottom: 12px;
}
input {
width: 100%;
}
svg {
width: 500px;
height: 500px;
border: 1px solid #ccc;
background: #fafafa;
}
JavaScript
const params = {
yaw: 0,
pitch: 0,
faceWidth: 170,
faceHeight: 230,
eyeSpacing: 46,
eyeSize: 18,
eyeTilt: 0,
noseLength: 48,
mouthWidth: 70,
smile: 0
};
const sliders = {
yaw: [-0.8, 0.8, 0.01],
pitch: [-0.5, 0.5, 0.01],
faceWidth: [120, 220, 1],
faceHeight: [170, 290, 1],
eyeSpacing: [25, 70, 1],
eyeSize: [8, 30, 1],
eyeTilt: [-0.6, 0.6, 0.01],
noseLength: [20, 80, 1],
mouthWidth: [30, 110, 1],
smile: [-35, 35, 1]
};
const controls = document.getElementById("controls");
const stage = document.getElementById("stage");
for (const key in sliders) {
const [min, max, step] = sliders[key];
const label = document.createElement("label");
label.innerHTML = `
${key}: <span id="${key}-value">${params[key]}</span>
<input type="range" min="${min}" max="${max}" step="${step}" value="${params[key]}" id="${key}">
`;
controls.appendChild(label);
label.querySelector("input").addEventListener("input", e => {
params[key] = Number(e.target.value);
document.getElementById(`${key}-value`).textContent = params[key];
render();
});
}
function rotateAndProject(point) {
let [x, y, z] = point;
const cy = Math.cos(params.yaw);
const sy = Math.sin(params.yaw);
const cp = Math.cos(params.pitch);
const sp = Math.sin(params.pitch);
// yaw
let x1 = x * cy - z * sy;
let z1 = x * sy + z * cy;
// pitch
let y1 = y * cp - z1 * sp;
let z2 = y * sp + z1 * cp;
const perspective = 500 / (500 + z2);
return {
x: 250 + x1 * perspective,
y: 250 + y1 * perspective,
scale: perspective,
depth: z2
};
}
function P(x, y, z = 0) {
return rotateAndProject([x, y, z]);
}
function ellipse(p, rx, ry, extra = "") {
return `
<ellipse
cx="${p.x}"
cy="${p.y}"
rx="${rx * p.scale}"
ry="${ry * p.scale}"
${extra}
/>
`;
}
function path(points, extra = "") {
const d...