canvas2 = {
const aspect = 0.58125;
const height = width * aspect;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);
camera.position.set(0, 0, 40);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearColor("#f6f6f6");
renderer.setSize(width, height);
yield renderer.domElement;
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableZoom = true;
false;
controls.enablePan = false;
controls.enableRotate = true;
controls.autoRotate = true;
controls.autoRotateSpeed = 1;
const material = new THREE.MeshLambertMaterial({
color: 0xcccccc,
side: THREE.DoubleSide,
transparent: true,
flatShading: true
});
// SHAPE ------------------------------------
const geometry = new THREE.TorusKnotGeometry(5, 2, 60, 6);
geometry.computeFlatVertexNormals(); // Needed to us material parameter {flatShading: true}
const shape = new THREE.Mesh(geometry, material.clone());
shape.material.color.setHex(0x9022dd);
//sprite
function makeLabelCanvas(baseWidth, size, name) {
const borderSize = 10;
const ctx = document.createElement("canvas").getContext("2d");
const font = `bold ${size}px calibri`;
ctx.font = font;
// measure how long the name will be
const textWidth = ctx.measureText(name).width;
const spriteWidth = baseWidth + borderSize * 2;
const spriteHeight = size + borderSize * 2;
ctx.canvas.width = spriteWidth;
ctx.canvas.height = spriteHeight;
// need to set font again after resizing canvas
ctx.font = font;
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "#66ee88";
ctx.fillRect(0, 0, spriteWidth, spriteHeight);
// scale to fit but don't stretch
const scaleFactor = Math.min(1, baseWidth / textWidth);
ctx.translate(spriteWidth / 2, spriteHeight / 1.85);
ctx.scale(scaleFactor, 1);
ctx.fillStyle = "white";
ctx.fillText(name, 0, 0);
return ctx.canvas;
}
let n = 150;
let goldenRatio = 1.61803398875;
let pi = Math.PI;
let radius = 13;
for (let i = 0; i < n; i++) {
let theta = (2 * pi * i) / goldenRatio;
let phi = Math.acos(1 - (2 * (i + 0.5)) / n);
const canvas = makeLabelCanvas(
Math.max(1, Math.ceil(Math.log10(i) + 0.01)) * 50 + 50,
100,
i
);
const texture = new THREE.CanvasTexture(canvas);
texture.minFilter = THREE.LinearFilter;
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
const labelMaterial = new THREE.SpriteMaterial({
map: texture,
transparent: true
});
const labelBaseScale = 0.01;
const label = new THREE.Sprite(labelMaterial);
label.position.set(
radius * (Math.cos(theta) * Math.sin(phi)),
radius * (Math.sin(theta) * Math.sin(phi)),
radius * Math.cos(phi) * 2
);
label.scale.x = canvas.width * labelBaseScale;
label.scale.y = canvas.height * labelBaseScale;
scene.add(label);
/*const sprite = new THREE.Sprite( new THREE.SpriteMaterial( { color: '#69f' } ) );
sprite.scale.set(2, 1, 1);
sprite.material.color.setHex(0x66ee88)
sprite.position.set(
radius*(Math.cos(theta) * Math.sin(phi)),
radius*(Math.sin(theta) * Math.sin(phi)),
radius*(Math.cos(phi))
)
scene.add(sprite)*/
}
// Light ---------------------------------------
const hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6);
hemiLight.color.setHSL(0.6, 0.6, 0.6);
hemiLight.groundColor.setHSL(0.095, 0.5, 0.75);
hemiLight.position.set(0, 50, 0);
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.color.setHSL(0.1, 1, 0.95);
dirLight.position.set(-1, 1.75, 1);
dirLight.position.multiplyScalar(30);
scene.add(dirLight);
// SCENE --------------------------------------
const pickable = new THREE.Group();
scene.add(pickable.add(shape));
scene.add(hemiLight);
scene.add(dirLight);
// RAYCASTER-----------------------------------
const mouse = new THREE.Vector2(-0.5, 0.25); // starts mouse position off screen so no objects are highlighted at start
let intersected;
const raycaster = new THREE.Raycaster();
let x;
let y;
renderer.domElement.onmousemove = (event) => {
event.preventDefault();
console.log(event);
x = event.layerX;
y = event.layerY;
(mouse.x = (x / width) * 2 - 1), (mouse.y = -(y / height) * 2 + 1);
};
//////////////////////////////////////////////////////////////////////////////////////////////
// RENDER LOOP ///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
let intersects;
let INTERSECTED;
let t = 0;
const render = function () {
requestAnimationFrame(render); // adjust scale on resize and stops animation when window is closed to save memory
// mouse actions
raycaster.setFromCamera(mouse, camera); //raycaster runs each animation frame
intersects = raycaster.intersectObjects(pickable.children); // checks intersection
// intersects = raycaster.intersectObjects(scene.children); // checks intersection
// highlight object
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
if (INTERSECTED)
INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
INTERSECTED = intersects[0].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex(0x444400);
}
} else {
if (INTERSECTED)
INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
INTERSECTED = null;
}
// animation
t += 0.005;
shape.quaternion.copy(camera.quaternion);
controls.update();
renderer.render(scene, camera); // renders scene
};
// Input -------------------------
window.addEventListener("click", () => {
INTERSECTED.material.color.setHex(Math.random() * 0xffffff);
});
render();
}