main
Alina Marquardt 2025-05-13 14:46:59 +02:00
parent 2fe2a4a267
commit e3692bf536
3 changed files with 63 additions and 122 deletions

View File

@ -3,50 +3,50 @@ window.onload = function() {
// Make sure THREE is available // Make sure THREE is available
if (typeof THREE === 'undefined') { if (typeof THREE === 'undefined') {
console.error('THREE.js is not loaded from CDN'); console.error('THREE.js is not loaded from CDN');
// Provide visual feedfront in the container // Provide visual feedfront in the container
const container = document.getElementById('icosahedron-container'); const container = document.getElementById('icosahedron-container');
container.innerHTML = '<div style="color: red; padding: 10px;">THREE.js not loaded</div>'; container.innerHTML = '<div style="color: red; padding: 10px;">THREE.js not loaded</div>';
return; return;
} }
// Initialize the scene // Initialize the scene
const container = document.getElementById('icosahedron-container'); const container = document.getElementById('icosahedron-container');
const containerWidth = container.clientWidth; const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight; const containerHeight = container.clientHeight;
// Setup renderer // Setup renderer
const renderer = new THREE.WebGLRenderer({ const renderer = new THREE.WebGLRenderer({
antialias: true, antialias: true,
alpha: true alpha: true
}); });
renderer.setSize(containerWidth, containerHeight); renderer.setSize(containerWidth, containerHeight);
renderer.setPixelRatio(window.devicePixelRatio); renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0x000000, 0); // Transparent frontground renderer.setClearColor(0x000000, 0); // Transparent frontground
container.appendChild(renderer.domElement); container.appendChild(renderer.domElement);
// Setup scene // Setup scene
const scene = new THREE.Scene(); const scene = new THREE.Scene();
// Setup camera // Setup camera
const camera = new THREE.PerspectiveCamera(26, containerWidth / containerHeight, 0.1, 100); const camera = new THREE.PerspectiveCamera(26, containerWidth / containerHeight, 0.1, 100);
camera.position.z = 4; camera.position.z = 4;
// Create icosahedron with manually defined vertices // Create icosahedron with manually defined vertices
function createIcosahedron(radius) { function createIcosahedron(radius) {
// Golden ratio for icosahedron vertices // Golden ratio for icosahedron vertices
const t = (1 + Math.sqrt(5)) / 2; const t = (1 + Math.sqrt(5)) / 2;
// Normalize radius // Normalize radius
const normRadius = radius / Math.sqrt(1 + t * t); const normRadius = radius / Math.sqrt(1 + t * t);
// Create vertices // Create vertices
const vertices = [ const vertices = [
[-1, t, 0], [1, t, 0], [-1, -t, 0], [1, -t, 0], [-1, t, 0], [1, t, 0], [-1, -t, 0], [1, -t, 0],
[0, -1, t], [0, 1, t], [0, -1, -t], [0, 1, -t], [0, -1, t], [0, 1, t], [0, -1, -t], [0, 1, -t],
[t, 0, -1], [t, 0, 1], [-t, 0, -1], [-t, 0, 1] [t, 0, -1], [t, 0, 1], [-t, 0, -1], [-t, 0, 1]
].map(v => new THREE.Vector3(v[0] * normRadius, v[1] * normRadius, v[2] * normRadius)); ].map(v => new THREE.Vector3(v[0] * normRadius, v[1] * normRadius, v[2] * normRadius));
// Define edges (pairs of vertex indices) // Define edges (pairs of vertex indices)
const edges = [ const edges = [
[0, 11], [0, 5], [0, 1], [0, 7], [0, 10], [0, 11], [0, 5], [0, 1], [0, 7], [0, 10],
@ -60,34 +60,34 @@ window.onload = function() {
[8, 9], /*[8, 10],*/ [8, 9], /*[8, 10],*/
/*[9, 11],*/ [10, 11] /*[9, 11],*/ [10, 11]
]; ];
return { return {
vertices: vertices, vertices: vertices,
edges: edges edges: edges
}; };
} }
// Convert vertex indices to actual vertices and create a line // Convert vertex indices to actual vertices and create a line
function createLine(icosa, edgeIndex, material) { function createLine(icosa, edgeIndex, material) {
const startIndex = icosa.edges[edgeIndex][0]; const startIndex = icosa.edges[edgeIndex][0];
const endIndex = icosa.edges[edgeIndex][1]; const endIndex = icosa.edges[edgeIndex][1];
const geometry = new THREE.BufferGeometry(); const geometry = new THREE.BufferGeometry();
const positions = new Float32Array([ const positions = new Float32Array([
icosa.vertices[startIndex].x, icosa.vertices[startIndex].y, icosa.vertices[startIndex].z, icosa.vertices[startIndex].x, icosa.vertices[startIndex].y, icosa.vertices[startIndex].z,
icosa.vertices[endIndex].x, icosa.vertices[endIndex].y, icosa.vertices[endIndex].z icosa.vertices[endIndex].x, icosa.vertices[endIndex].y, icosa.vertices[endIndex].z
]); ]);
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
return new THREE.Line(geometry, material); return new THREE.Line(geometry, material);
} }
// Function to create the dual line rendering (thin back lines, thick front lines) // Function to create the dual line rendering (thin back lines, thick front lines)
function createDualLineRendering(radius) { function createDualLineRendering(radius) {
const group = new THREE.Group(); const group = new THREE.Group();
const icosa = createIcosahedron(radius); const icosa = createIcosahedron(radius);
// Create materials // Create materials
const backMaterial = new THREE.MeshBasicMaterial({ const backMaterial = new THREE.MeshBasicMaterial({
color: 0x63a8b8, // green for back lines color: 0x63a8b8, // green for back lines
@ -96,35 +96,35 @@ window.onload = function() {
opacity: 0.4, opacity: 0.4,
side: THREE.DoubleSide side: THREE.DoubleSide
}); });
const frontMaterial = new THREE.MeshBasicMaterial({ const frontMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff, // white for front lines color: 0xffffff, // white for front lines
transparent: true, transparent: true,
depthTest: true, // Use depth test for front lines depthTest: true, // Use depth test for front lines
side: THREE.DoubleSide side: THREE.DoubleSide
}); });
// For each edge, create: // For each edge, create:
// 1. A thin cylindrical tube with back material that ignores depth // 1. A thin cylindrical tube with back material that ignores depth
// 2. A cylindrical tube with front material that uses depth testing // 2. A cylindrical tube with front material that uses depth testing
const backRadius = 0.03; // thin for back lines const backRadius = 0.025; // thin for back lines
const frontRadius = 0.035; // thick for front lines const frontRadius = 0.035; // thick for front lines
icosa.edges.forEach((edge, index) => { icosa.edges.forEach((edge, index) => {
const start = icosa.vertices[edge[0]]; const start = icosa.vertices[edge[0]];
const end = icosa.vertices[edge[1]]; const end = icosa.vertices[edge[1]];
// Create back line (thick) // Create back line (thick)
const backLine = createCylinderBetweenPoints(start, end, backRadius, 1, backMaterial); const backLine = createCylinderBetweenPoints(start, end, backRadius, 1, backMaterial);
backLine.renderOrder = 1; // Render after front lines backLine.renderOrder = 1; // Render after front lines
group.add(backLine); group.add(backLine);
// Create front line (thin) // Create front line (thin)
const frontLine = createCylinderBetweenPoints(start, end, frontRadius, 0.95, frontMaterial); const frontLine = createCylinderBetweenPoints(start, end, frontRadius, 1.022, frontMaterial);
frontLine.renderOrder = 3; // Render before back lines frontLine.renderOrder = 3; // Render before back lines
group.add(frontLine); group.add(frontLine);
}); });
// Create faces for depth testing (invisible) // Create faces for depth testing (invisible)
const faceMaterial = new THREE.MeshBasicMaterial({ const faceMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff, color: 0xffffff,
@ -133,7 +133,7 @@ window.onload = function() {
depthWrite: true, depthWrite: true,
side: THREE.DoubleSide side: THREE.DoubleSide
}); });
// Create faces for internal icosahedron // Create faces for internal icosahedron
const internalMaterial = new THREE.MeshBasicMaterial({ const internalMaterial = new THREE.MeshBasicMaterial({
color: 0x63a8b8, color: 0x63a8b8,
@ -142,7 +142,7 @@ window.onload = function() {
opacity: 0.2, opacity: 0.2,
side: THREE.DoubleSide side: THREE.DoubleSide
}); });
// Define faces of icosahedron (each is a triangle of vertex indices) // Define faces of icosahedron (each is a triangle of vertex indices)
const faces = [ const faces = [
[0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11], [0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11],
@ -150,7 +150,7 @@ window.onload = function() {
[3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9], [3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9],
[4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1] [4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1]
]; ];
// Create invisible faces for depth testing // Create invisible faces for depth testing
faces.forEach(face => { faces.forEach(face => {
const geometry = new THREE.BufferGeometry(); const geometry = new THREE.BufferGeometry();
@ -159,14 +159,14 @@ window.onload = function() {
icosa.vertices[face[1]].x, icosa.vertices[face[1]].y, icosa.vertices[face[1]].z, icosa.vertices[face[1]].x, icosa.vertices[face[1]].y, icosa.vertices[face[1]].z,
icosa.vertices[face[2]].x, icosa.vertices[face[2]].y, icosa.vertices[face[2]].z icosa.vertices[face[2]].x, icosa.vertices[face[2]].y, icosa.vertices[face[2]].z
]); ]);
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const faceMesh = new THREE.Mesh(geometry, faceMaterial); const faceMesh = new THREE.Mesh(geometry, faceMaterial);
faceMesh.scale.set(0.95,0.95,0.95); faceMesh.scale.set(0.95,0.95,0.95);
faceMesh.renderOrder = 0; // Render first for depth buffer faceMesh.renderOrder = 0; // Render first for depth buffer
group.add(faceMesh); group.add(faceMesh);
const internalMesh1 = new THREE.Mesh(geometry, internalMaterial); const internalMesh1 = new THREE.Mesh(geometry, internalMaterial);
const internalMesh2 = new THREE.Mesh(geometry, internalMaterial); const internalMesh2 = new THREE.Mesh(geometry, internalMaterial);
const internalMesh3 = new THREE.Mesh(geometry, internalMaterial); const internalMesh3 = new THREE.Mesh(geometry, internalMaterial);
@ -188,45 +188,45 @@ window.onload = function() {
internalMesh5.renderOrder = 1; // Render between front and back internalMesh5.renderOrder = 1; // Render between front and back
group.add(internalMesh5); group.add(internalMesh5);
}); });
return group; return group;
} }
// Function to create a cylinder between two points // Function to create a cylinder between two points
function createCylinderBetweenPoints(pointX, pointY, radius, lengthmultiplier, material) { function createCylinderBetweenPoints(pointX, pointY, radius, lengthmultiplier, material) {
// Direction from pointX to pointY // Direction from pointX to pointY
const direction = new THREE.Vector3().subVectors(pointY, pointX); const direction = new THREE.Vector3().subVectors(pointY, pointX);
const length = direction.length(); const length = direction.length();
// Create cylinder // Create cylinder
const geometry = new THREE.CylinderGeometry(radius, radius, length*lengthmultiplier, 3, 1); const geometry = new THREE.CylinderGeometry(radius, radius, length*lengthmultiplier, 3, 1);
// By default, cylinder is along Y-axis, so rotate it // By default, cylinder is along Y-axis, so rotate it
geometry.rotateX(Math.PI / 2); geometry.rotateX(Math.PI / 2);
// Create mesh // Create mesh
const cylinder = new THREE.Mesh(geometry, material); const cylinder = new THREE.Mesh(geometry, material);
// Position and orient cylinder // Position and orient cylinder
const midpoint = new THREE.Vector3().addVectors(pointX, pointY).multiplyScalar(0.5); const midpoint = new THREE.Vector3().addVectors(pointX, pointY).multiplyScalar(0.5);
cylinder.position.copy(midpoint); cylinder.position.copy(midpoint);
cylinder.lookAt(pointY); cylinder.lookAt(pointY);
return cylinder; return cylinder;
} }
// Create our icosahedron // Create our icosahedron
const icosahedronGroup = createDualLineRendering(0.85); const icosahedronGroup = createDualLineRendering(0.85);
scene.add(icosahedronGroup); scene.add(icosahedronGroup);
// Animation state // Animation state
let animating = true; let animating = true;
let lastFrameTime = 0; let lastFrameTime = 0;
const targetFPS = 20; const targetFPS = 20;
const animMultiplier = 30/targetFPS; const animMultiplier = 30/targetFPS;
const frameDuration = 1000 / targetFPS; const frameDuration = 1000 / targetFPS;
// Animation function // Animation function
function animate(now) { function animate(now) {
requestAnimationFrame(animate); requestAnimationFrame(animate);
@ -245,37 +245,22 @@ window.onload = function() {
renderer.render(scene, camera); renderer.render(scene, camera);
} }
/*
// Set up controls
const toggleAnimationBtn = document.getElementById('toggle-animation');
const resetRotationBtn = document.getElementById('reset-rotation');
toggleAnimationBtn.addEventListener('click', function() {
animating = !animating;
this.textContent = animating ? 'Pause' : 'Resume';
});
resetRotationBtn.addEventListener('click', function() {
icosahedronGroup.rotation.set(0, 0, 0);
});
*/
// Start animation // Start animation
animate(); animate();
// Handle window resize // Handle window resize
window.addEventListener('resize', function() { window.addEventListener('resize', function() {
// Only update if container dimensions change // Only update if container dimensions change
const newWidth = container.clientWidth; const newWidth = container.clientWidth;
const newHeight = container.clientHeight; const newHeight = container.clientHeight;
if (newWidth !== containerWidth || newHeight !== containerHeight) { if (newWidth !== containerWidth || newHeight !== containerHeight) {
camera.aspect = newWidth / newHeight; camera.aspect = newWidth / newHeight;
camera.updateProjectionMatrix(); camera.updateProjectionMatrix();
renderer.setSize(newWidth, newHeight); renderer.setSize(newWidth, newHeight);
} }
}); });
console.log('Icosahedron wireframe created successfully'); console.log('Icosahedron wireframe created successfully');
}; };

View File

@ -2,28 +2,27 @@
<head id="www-lastfuture-de"> <head id="www-lastfuture-de">
<meta charset="utf-8"> <meta charset="utf-8">
<title>lastfuture</title> <title>lastfuture</title>
<meta name="description" content="personal website" /> <meta name="description" content="personal website" />
<meta name="author" content="▧" /> <meta name="author" content="▧" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preload" fetchpriority="high" as="image" href="img/bg_blur.jpg" type="image/jpeg"> <link rel="preload" fetchpriority="high" as="image" href="img/bg_blur.jpg" type="image/jpeg">
<link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="style.css" />
<script src="js/three.min.js"></script> <script src="js/three.min.js"></script>
<script src="functions.js" defer></script> <script src="functions.js" defer></script>
</head> </head>
<body> <body>
<div id="base" class="headlineshadow"> <div id="base" class="headlineshadow">
<header> <header>
<div id="greeting"> <div id="greeting">
<!-- <img class="avatar" src="img/avatar.png" width="90" height="73" alt="alina marquardt" /> -->
<div id="icosahedron-container" class="avatar"></div> <div id="icosahedron-container" class="avatar"></div>
<span class="line1">hello!</span> <span class="line1">hello!</span>
<span class="line2">my name is</span> <span class="line2">my name is</span>
@ -33,36 +32,26 @@
<aside> <aside>
<section class="iam"> <section class="iam">
<span class="line1">i am a senior</span> <span class="line1">i am a senior</span>
<span class="line2"><em>UX/UI designer</em></span> <span class="line2 bigline"><em>UX/UI designer</em></span>
<span class="line3">&amp; electronic musician</span> <span class="line3">&amp; electronic musician</span>
<span class="line4">based in germany</span> <span class="line4">based in germany</span>
</section> </section>
</aside> </aside>
<!--
<nav>
<ul>
<li class="music line1"><a href="#music">i make <em>music</em><img class="icon" src="img/nav-music.png" width="17" height="19" alt></a></li>
<li class="design line2"><a href="#design">i <em>design</em> things<img class="icon" src="img/nav-design.png" width="12" height="20" alt></a><div class="icon"></div></li>
<li class="experiment line3"><a href="#experiment">i <em>experiment<img class="icon" src="img/nav-experiment.png" width="15" height="26" alt></em></a><div class="icon"></div></li>
</ul>
</nav>
-->
<aside> <aside>
<section class="findme"> <section class="findme">
<span class="line1">please also see</span> <span class="line1">please also see</span>
<ul> <ul>
<li class="line2"><a href="https://git.broken.graphics/explore/repos" target="_blank">my <em>git repos</em></a></li> <li class="line2 bigline"><a href="https://git.broken.graphics/explore/repos" target="_blank">my <em>git repos</em></a></li>
<li class="line3"><a href="https://lastfuture.bandcamp.com/" target="_blank">my <em>bandcamp</em></a></li> <li class="line3 bigline"><a href="https://lastfuture.bandcamp.com/" target="_blank">my <em>bandcamp</em></a></li>
</ul> </ul>
<!-- <span class="line4"><a href="">more places »</a></span> -->
</section> </section>
</aside> </aside>
</div> </div>
<div id="maincontent"> <div id="maincontent">
<div class="column"> <div class="column">
<section class="box" id="music"> <section class="box" id="music">
<header class="blurgreen headlineshadow"><h1><em>music</em> releases<img class="icon" src="img/head-music.png" width="69" height="77" alt></h1></header> <header class="blurgreen headlineshadow"><h1><em>music</em> releases<img class="icon" src="img/head-music.png" width="69" height="77" alt></h1></header>
<article class="opaque whitebg album"> <article class="opaque whitebg album">
@ -97,41 +86,8 @@
</a> </a>
</article> </article>
</section> </section>
<!--
<section class="box" id="experiment">
<header class="blurgreen headlineshadow"><h1><em>experiments</em> box<img class="icon" src="img/head-experiment.png" width="63" height="103" alt></h1></header>
<div class="opaque whitebg">
<h2><span class="halfbubble blurgreen"></span>video experiments</h2>
<h2><span class="halfbubble blurgreen"></span>lorem ipsum</h2>
</div>
</section>
-->
</div> </div>
<!--
<div class="column">
<section class="box invertedbox" id="recent">
<header class="headlineshadow whitebg"><h1><em>highlighted</em> tracks</h1></header>
<div class="opaque blurgreen">
<h2 class="headlineshadow"><em>track</em></h2>
</div>
</section>
<section class="box" id="design">
<header class="blurgreen headlineshadow"><h1><em>design</em> work<img class="icon" src="img/head-design.png" width="46" height="75" alt></h1></header>
<div class="opaque whitebg">
<h2><span class="halfbubble blurgreen"></span>web</h2>
<div style="height: 300px;"></div>
<h2><span class="halfbubble blurgreen"></span>apps</h2>
<div style="height: 400px;"></div>
</div>
</section>
</div>
-->
</div> </div>
</body> </body>

View File

@ -169,7 +169,7 @@ body, select, input, textarea {color: var(--text-color);}
/* Custom text-selection colors (remove any text shadows: http://twitter.com/miketaylr/status/12228805301) */ /* Custom text-selection colors (remove any text shadows: http://twitter.com/miketaylr/status/12228805301) */
::-moz-selection{background: var(--highlight-color); text-shadow: none;} ::-moz-selection{background: var(--highlight-color); text-shadow: none;}
::selection {background: var(--highlight-color); text-shadow: none;} ::selection {background: var(--highlight-color); text-shadow: none;}
/* j.mp/webkit-tap-highlight-color */ /* j.mp/webkit-tap-highlight-color */
a:link {-webkit-tap-highlight-color: var(--highlight-color);} a:link {-webkit-tap-highlight-color: var(--highlight-color);}
@ -385,7 +385,7 @@ article.album a:hover {
margin-left: 3rem; margin-left: 3rem;
} }
#base a, #base section.iam .line2 { #base a, #base .bigline {
font-size: 1.5rem; font-size: 1.5rem;
display: block; display: block;
line-height: 1em; line-height: 1em;
@ -457,9 +457,9 @@ article.album a:hover {
-------------------------------------------------------------------------------*/ -------------------------------------------------------------------------------*/
@media screen and (max-width: 576px) { @media screen and (max-width: 576px) {
} }
@media print { @media print {
} }