| CARVIEW |
Select Language
HTTP/1.1 200 OK
Date: Fri, 16 Jan 2026 09:23:46 GMT
Server: Apache/2.4.65 (Debian) OpenSSL/1.1.1w mod_perl/2.0.11 Perl/v5.32.1
Last-Modified: Fri, 24 Oct 2025 21:13:35 GMT
ETag: "fba-641ee05c5a44e-gzip"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 1405
Content-Type: application/javascript
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
let group, renderer, scene, camera;
function animate() {
group.rotation.x += 0.002;
group.rotation.y += 0.004;
group.rotation.z += 0.006;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
init();
animate();
window.addEventListener('resize', () => {
renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
});
function init() {
let size = 100;
renderer = new THREE.WebGLRenderer({
antialias: true,
canvas: canvas,
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);
scene = new THREE.Scene();
scene.add(new THREE.AmbientLight(0x666666));
let zmax = size * 9;
let zcam = size * 3;
let zmin = size * 2;
camera = new THREE.PerspectiveCamera(
40, // fov
canvas.width / canvas.height,
1, zmax + zmin,
);
camera.position.set(zcam, zcam, zcam);
camera.add(new THREE.PointLight(0xffffff, 3, 0, 0));
scene.add(camera);
// XXX this obnoxiously disables the context menu
let controls = new OrbitControls(camera, renderer.domElement);
controls.minDistance = zmin;
controls.maxDistance = zmax;
let vertices = [];
for(let x of [+size, -size]) {
for(let y of [+size, -size]) {
for(let z of [+size, -size]) {
vertices.push(x,y,z);
}
}
}
vertices.push(0,0,0);
vertices = new THREE.Float32BufferAttribute(vertices, 3);
let indices = new Uint16Array([
8,0,1,0,2,0,3,0,4,0,5,0,6,0,
8,3,0,3,1,3,2,3,5,3,6,3,7,3,
8,5,0,5,1,5,3,5,4,5,6,5,7,5,
8,6,0,6,2,6,3,6,4,6,5,6,7,6,
]);
let box = new THREE.Line(
new THREE.BufferGeometry()
.setAttribute('position', vertices)
.setIndex(new THREE.BufferAttribute(indices, 1))
);
box.material.color = new THREE.Color(0x888888);
let segments = size/2;
let open_ended = true;
let z = new THREE.Vector3(0,0,1);
let xy = new THREE.Vector3(1,1,0);
let xyz = new THREE.Vector3(1,1,1);
let apex_d = xyz.length() * size;
let face_taper = Math.tan(xyz.angleTo(xy));
let axis_taper = Math.tan(xyz.angleTo(z));
let thigh_r = size/2;
let thigh_d = thigh_r / axis_taper;
let ankle_r = size/3;
let ankle_d = apex_d - ankle_r / face_taper;
let sole_d = thigh_d + size;
let sole_r = (apex_d - sole_d) * face_taper;
let sole = new THREE.CircleGeometry(sole_r, segments*2)
.translate(0, 0, sole_d);
let foot_h = sole_d - ankle_d;
let foot = new THREE.CylinderGeometry(
sole_r, ankle_r, foot_h, segments, 1, open_ended
).rotateX(Math.PI/2).translate(0, 0, ankle_d + foot_h/2);
let leg_taper = (thigh_r - ankle_r) / (ankle_d - thigh_d)
let waist_r = thigh_r + thigh_d * leg_taper;
let thigh = new THREE.CylinderGeometry(
ankle_r, waist_r, ankle_d, segments, 1, open_ended
).rotateX(Math.PI/2).translate(0, 0, ankle_d/2);
let leg = BufferGeometryUtils.mergeGeometries(
[ sole, foot, thigh ]
).applyQuaternion(
new THREE.Quaternion()
.setFromUnitVectors(z, xyz.normalize())
);
group = new THREE.Group().add(box);
scene.add(group);
group.add(new THREE.Mesh(
leg.clone(),
new THREE.MeshStandardMaterial({
color: 0xcc0000
})).rotateOnWorldAxis(
new THREE.Vector3(1,0,0), Math.PI
));
group.add(new THREE.Mesh(
leg.clone(),
new THREE.MeshStandardMaterial({
color: 0x00aa00
})).rotateOnWorldAxis(
new THREE.Vector3(0,1,0), Math.PI
));
group.add(new THREE.Mesh(
leg.clone(),
new THREE.MeshStandardMaterial({
color: 0x2222ff
})).rotateOnWorldAxis(
new THREE.Vector3(0,0,1), Math.PI
));
group.add(new THREE.Mesh(
leg.clone(),
new THREE.MeshStandardMaterial({
color: 0xbbbb00
})));
}