Aquí Solín discute con ChatGPT 4, hasta obtener lo que estaba buscando.

El siguiente es el último código generado por la IA (la cabecera <head> la modificamos para ajustar el objeto interactivo a este libro):

<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=yes,minimal-ui"> <meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <title>Three.js Dodecahedron</title> <style> body { margin: 0; } #point-container { width: 100vw; height: 100vh; display: block; } #controls { position: absolute; top: 10px; left: 10px; background: rgba(255, 255, 255, 0.8); padding: 10px; border-radius: 5px; } </style> </head> <body> <div id="point-container"></div> <div id="controls"> <label for="distance">Distancia:</label> <input type="range" id="distance" min="0.1" max="5" step="0.1" value="2"> <span id="distance-value">2</span> <br> <label for="pyramid-selection">Pirámides a mostrar (separadas por comas):</label> <p><input type="text" id="pyramid-selection" value="0,1,2,3,4,5,6,7,8,9,10,11"></p> <p><button id="update-pyramids">Actualizar Pirámides</button></p> </div> <script src="three.min.js"></script> <script src="OrbitControls.js"></script> <script> // Configuración de la escena, cámara y renderizador const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.getElementById('point-container').appendChild(renderer.domElement); // Crear controles de órbita const controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableZoom = true; controls.enablePan = true; // Crear dodecaedro const dodecahedronGeometry = new THREE.DodecahedronGeometry(); const edgesGeometry = new THREE.EdgesGeometry(dodecahedronGeometry); const edgesMaterial = new THREE.LineBasicMaterial({ color: 0x0077ff }); const edges = new THREE.LineSegments(edgesGeometry, edgesMaterial); scene.add(edges); // Obtener los vértices únicos del dodecaedro const vertices = dodecahedronGeometry.getAttribute('position').array; const uniqueVertices = []; for (let i = 0; i < vertices.length; i += 3) { const vertex = new THREE.Vector3(vertices[i], vertices[i + 1], vertices[i + 2]); uniqueVertices.push(vertex); } // Remover duplicados de los vértices const deduplicatedVertices = [...new Set(uniqueVertices.map(v => v.toArray().toString()))].map(v => new THREE.Vector3(...v.split(',').map(Number))); // Definición de las caras del dodecaedro const faces = [ [0, 2, 1, 3, 4], // Cara 0 [0, 1, 17, 19, 5], // Cara 1 [1, 3, 5, 6, 7], // Cara 2 [3, 4, 7, 10, 13], // Cara 3 [2, 16, 4, 13, 12], // Cara 4 [0, 17, 2, 16, 15], // Cara 5 [17, 19, 15, 14, 18], // Cara 6 [8, 9, 18, 14, 11], // Cara 7 [5, 6, 19, 18, 8], // Cara 8 [6, 7, 8, 9, 10], // Cara 9 [9, 10, 11, 12, 13],// Cara 10 [11, 12, 14, 15, 16]// Cara 11 ]; // Función para crear un punto function createPoint(x, y, z, color) { const pointGeometry = new THREE.SphereGeometry(0.05, 32, 32); const pointMaterial = new THREE.MeshBasicMaterial({ color: color }); const point = new THREE.Mesh(pointGeometry, pointMaterial); point.position.set(x, y, z); return point; } function createPyramidAndLines(vertexIndices, mode, perpendicularLength) { const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff]; // Colores: Rojo, Verde, Azul, Amarillo, Magenta const points = vertexIndices.map((index, i) => { const vertexPoint = createPoint(deduplicatedVertices[index].x, deduplicatedVertices[index].y, deduplicatedVertices[index].z, colors[i]); scene.add(vertexPoint); return { x: deduplicatedVertices[index].x, y: deduplicatedVertices[index].y, z: deduplicatedVertices[index].z }; }); // Calcular puntos medios function midpoint(p1, p2) { return { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2, z: (p1.z + p2.z) / 2 }; } const midpoint69 = midpoint(points[1], points[2]); const midpoint915 = midpoint(points[2], points[4]); // Función para crear una línea function createLine(p1, p2, color) { const geometry = new THREE.BufferGeometry().setFromPoints([ new THREE.Vector3(p1.x, p1.y, p1.z), new THREE.Vector3(p2.x, p2.y, p2.z) ]); const material = new THREE.LineBasicMaterial({ color: color }); return new THREE.Line(geometry, material); } // Crear las líneas const line1 = createLine(points[0], midpoint69, 0xff0000); // Vértice 3 al punto medio de vértices 6 y 9 (Rojo) const line2 = createLine(points[3], midpoint915, 0x0000ff); // Vértice 12 al punto medio de vértices 9 y 15 (Azul) scene.add(line1); scene.add(line2); // Calcular punto de intersección const intersection = midpoint(midpoint69, midpoint915); const intersectionPoint = createPoint(intersection.x, intersection.y, intersection.z, 0xffff00); // Punto de intersección (Amarillo) scene.add(intersectionPoint); // Calcular la normal del plano determinado por los vértices 3, 6 y 9 const p1 = points[0]; const p2 = points[1]; const p3 = points[2]; const v1 = new THREE.Vector3(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z); const v2 = new THREE.Vector3(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z); const normal = new THREE.Vector3().crossVectors(v1, v2).normalize(); // Crear la línea perpendicular en ambos sentidos let perpendicularLine2, pointOnLine; if (mode === 1) { perpendicularLine2 = createLine({ x: intersection.x, y: intersection.y, z: intersection.z }, { x: intersection.x + normal.x * perpendicularLength, y: intersection.y + normal.y * perpendicularLength, z: intersection.z + normal.z * perpendicularLength }, 0xff00ff // Color magenta ); pointOnLine = createPoint( intersection.x + normal.x * perpendicularLength, intersection.y + normal.y * perpendicularLength, intersection.z + normal.z * perpendicularLength, 0xff00ff // Color magenta ); } else if (mode === 2) { perpendicularLine2 = createLine({ x: intersection.x, y: intersection.y, z: intersection.z }, { x: intersection.x - normal.x * perpendicularLength, y: intersection.y - normal.y * perpendicularLength, z: intersection.z - normal.z * perpendicularLength }, 0xff00ff // Color magenta ); pointOnLine = createPoint( intersection.x - normal.x * perpendicularLength, intersection.y - normal.y * perpendicularLength, intersection.z - normal.z * perpendicularLength, 0xff00ff // Color magenta ); } scene.add(perpendicularLine2); scene.add(pointOnLine); // Función para trazar líneas desde el nuevo punto a los vértices function createLinesToVertices(point, vertices, color) { vertices.forEach(vertex => { const line = createLine(point, vertex, color); scene.add(line); }); } // Crear y añadir las líneas desde el nuevo punto a los cinco vértices createLinesToVertices({ x: pointOnLine.position.x, y: pointOnLine.position.y, z: pointOnLine.position.z }, points, 0xffffff // Color blanco ); } // Posicionar la cámara camera.position.z = 5; // Bucle de animación function animate() { requestAnimationFrame(animate); controls.update(); renderer.render(scene, camera); } animate(); // Ajustar el tamaño del canvas cuando se redimensiona la ventana window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // Manejar el cambio en el input de distancia const distanceInput = document.getElementById('distance'); const distanceValue = document.getElementById('distance-value'); distanceInput.addEventListener('input', () => { const distance = parseFloat(distanceInput.value); distanceValue.innerText = distance; updatePyramids(); }); // Manejar el cambio en el input de selección de pirámides document.getElementById('update-pyramids').addEventListener('click', updatePyramids); function updatePyramids() { const distance = parseFloat(distanceInput.value); const selectedFaces = document.getElementById('pyramid-selection').value.split(',').map(Number); scene.clear(); // Limpiar la escena scene.add(edges); // Volver a agregar el dodecaedro selectedFaces.forEach(faceIndex => { if (faces[faceIndex]) { createPyramidAndLines(faces[faceIndex], 2, distance); } }); } // Inicializar con la distancia y pirámides por defecto updatePyramids(); </script> </body> </html>