diff --git a/threejs/dodeahedron-fractal/extra.js b/threejs/dodeahedron-fractal/extra.js new file mode 100644 index 00000000..30a13842 --- /dev/null +++ b/threejs/dodeahedron-fractal/extra.js @@ -0,0 +1,67 @@ +import * as THREE from 'three'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; + + +// Setup Scene, Camera, Renderer +const scene = new THREE.Scene(); +const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); +const renderer = new THREE.WebGLRenderer(); +renderer.setSize(window.innerWidth, window.innerHeight); +document.body.appendChild(renderer.domElement); + +// Postprocessing with Bloom effect +const composer = new EffectComposer(renderer); +const renderPass = new RenderPass(scene, camera); +composer.addPass(renderPass); + +const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); +composer.addPass(bloomPass); + +// Create Material and Line Geometry +const material = new THREE.LineBasicMaterial({ color: 0xffd700 }); // Golden color + +// Set up the Golden Ratio Dragon Curve parameters +const goldenRatio = (1 + Math.sqrt(5)) / 2; +const r1 = Math.pow(1 / goldenRatio, 1 / goldenRatio); +const r2 = Math.pow(r1, 2); +const angle1 = Math.acos((1 + r1**2 - r1**4) / (2 * r1)); +const angle2 = Math.acos((1 + r1**4 - r1**2) / (2 * r1**2)); + +function goldenDragon(x1, y1, x2, y2, turn, n, vertices) { + const dist = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); + if (dist < 1 || n <= 0) { + vertices.push(new THREE.Vector3(x2, y2, 0)); + return; + } + + const angle = Math.atan2(y2 - y1, x2 - x1); + let px, py; + if (turn) { + px = x1 + dist * r1 * Math.cos(angle + angle1); + py = y1 + dist * r1 * Math.sin(angle + angle1); + } else { + px = x1 + dist * r2 * Math.cos(angle - angle2); + py = y1 + dist * r2 * Math.sin(angle - angle2); + } + + goldenDragon(x1, y1, px, py, true, n - 1, vertices); + goldenDragon(px, py, x2, y2, false, n - 1, vertices); +} + +// Setup the geometry to hold the vertices +const points = []; +goldenDragon(-5, -2, 15, -2, true, 10, points); // Adjust the recursion level as needed +const geometry = new THREE.BufferGeometry().setFromPoints(points); +const line = new THREE.Line(geometry, material); +scene.add(line); + +// Position the camera and animate +camera.position.z = 10; + +function animate() { + requestAnimationFrame(animate); + composer.render(); +} +animate(); \ No newline at end of file diff --git a/threejs/dodeahedron-fractal/getLayer.js b/threejs/dodeahedron-fractal/getLayer.js new file mode 100644 index 00000000..9233ed8b --- /dev/null +++ b/threejs/dodeahedron-fractal/getLayer.js @@ -0,0 +1,55 @@ +/** + * Based upon Bobby Roe's Simple Particle System + * Watch the tutorial on YouTube: (https://youtu.be/h1UQdbuF204) + */ + +import * as THREE from "three"; + +const loader = new THREE.TextureLoader(); + +function getSprite({ hasFog, color, opacity, path, pos, size }) { + const spriteMat = new THREE.SpriteMaterial({ + color, + fog: hasFog, + map: loader.load(path), + transparent: true, + opacity, + }); + spriteMat.color.offsetHSL(0, 0, Math.random() * 0.2 - 0.1); + const sprite = new THREE.Sprite(spriteMat); + sprite.position.set(pos.x, -pos.y, pos.z); + size += Math.random() - 0.5; + sprite.scale.set(size, size, size); + sprite.material.rotation = 0; + return sprite; +} + +function getLayer({ + hasFog = true, + hue = 0.0, + numSprites = 10, + opacity = 1, + path = "./img/rad-grad.png", + radius = 1, + sat = 0.5, + size = 1, + z = 0, +}) { + const layerGroup = new THREE.Group(); + for (let i = 0; i < numSprites; i += 1) { + let angle = (i / numSprites) * Math.PI * 2; + const pos = new THREE.Vector3( + Math.cos(angle) * Math.random() * radius, + Math.sin(angle) * Math.random() * radius, + z + Math.random() + ); + const length = new THREE.Vector3(pos.x, pos.y, 0).length(); + // const hue = 0.0; // (0.9 - (radius - length) / radius) * 1; + + let color = new THREE.Color().setHSL(hue, 1, sat); + const sprite = getSprite({ hasFog, color, opacity, path, pos, size }); + layerGroup.add(sprite); + } + return layerGroup; +} +export default getLayer; \ No newline at end of file diff --git a/threejs/dodeahedron-fractal/getParticleSystem.js b/threejs/dodeahedron-fractal/getParticleSystem.js new file mode 100644 index 00000000..179d2cd0 --- /dev/null +++ b/threejs/dodeahedron-fractal/getParticleSystem.js @@ -0,0 +1,236 @@ +/** + * Based upon Bobby Roe's Simple Particle System + * Watch the tutorial on YouTube: (https://youtu.be/h1UQdbuF204) + */ + +import * as THREE from 'three'; + +const _VS = ` +uniform float pointMultiplier; + +attribute float size; +attribute float angle; +attribute vec4 aColor; + +varying vec4 vColor; +varying vec2 vAngle; + +void main() { + vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); + + gl_Position = projectionMatrix * mvPosition; + gl_PointSize = size * pointMultiplier / gl_Position.w; + + vAngle = vec2(cos(angle), sin(angle)); + vColor = aColor; +}`; + +const _FS = ` +uniform sampler2D diffuseTexture; + +varying vec4 vColor; +varying vec2 vAngle; + +void main() { + vec2 coords = (gl_PointCoord - 0.5) * mat2(vAngle.x, vAngle.y, -vAngle.y, vAngle.x) + 0.5; + gl_FragColor = texture2D(diffuseTexture, coords) * vColor; +}`; + + +function getLinearSpline(lerp) { + + const points = []; + const _lerp = lerp; + + function addPoint(t, d) { + points.push([t, d]); + } + + function getValueAt(t) { + let p1 = 0; + + for (let i = 0; i < points.length; i++) { + if (points[i][0] >= t) { + break; + } + p1 = i; + } + + const p2 = Math.min(points.length - 1, p1 + 1); + + if (p1 == p2) { + return points[p1][1]; + } + + return _lerp( + (t - points[p1][0]) / ( + points[p2][0] - points[p1][0]), + points[p1][1], points[p2][1]); + } + return { addPoint, getValueAt }; +} + +function getParticleSystem(params) { + const { camera, emitter, parent, rate, texture } = params; + const uniforms = { + diffuseTexture: { + value: new THREE.TextureLoader().load(texture) + }, + pointMultiplier: { + value: window.innerHeight / (2.0 * Math.tan(30.0 * Math.PI / 180.0)) + } + }; + const _material = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: _VS, + fragmentShader: _FS, + //blending: THREE.AdditiveBlending, + depthTest: true, + depthWrite: false, + transparent: true, + vertexColors: true + }); + + let _particles = []; + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.Float32BufferAttribute([], 3)); + geometry.setAttribute('size', new THREE.Float32BufferAttribute([], 1)); + geometry.setAttribute('aColor', new THREE.Float32BufferAttribute([], 4)); + geometry.setAttribute('angle', new THREE.Float32BufferAttribute([], 1)); + + const _points = new THREE.Points(geometry, _material); + + parent.add(_points); + + const alphaSpline = getLinearSpline((t, a, b) => { + return a + t * (b - a); + }); + alphaSpline.addPoint(0.0, 0.0); + alphaSpline.addPoint(0.6, 1.0); + alphaSpline.addPoint(1.0, 0.0); + + const colorSpline = getLinearSpline((t, a, b) => { + const c = a.clone(); + return c.lerp(b, t); + }); + colorSpline.addPoint(0.0, new THREE.Color(0xffff00)); + colorSpline.addPoint(0.0, new THREE.Color(0x00ff00)); + colorSpline.addPoint(0.0, new THREE.Color(0x00ffff)); + colorSpline.addPoint(1.0, new THREE.Color(0xffffff)); + + const sizeSpline = getLinearSpline((t, a, b) => { + return a + t * (b - a); + }); + sizeSpline.addPoint(0.0, 0.0); + sizeSpline.addPoint(1.0, 1.0); + // max point size = 512; => console.log(ctx.getParameter(ctx.ALIASED_POINT_SIZE_RANGE)); + const maxVelocity = 1.5; + const radius = 0.5; + const maxLife = 9.5; + const maxSize = 0.15; + let gdfsghk = 0.0; + function _AddParticles(timeElapsed) { + gdfsghk += timeElapsed; + const n = Math.floor(gdfsghk * rate); + gdfsghk -= n / rate; + for (let i = 0; i < n; i += 1) { + const life = (Math.random() * 0.75 + 0.25) * maxLife; + _particles.push({ + position: new THREE.Vector3( + (Math.random() * 2 - 1) * radius, + (Math.random() * 2 - 1) * radius, + (Math.random() * 2 - 1) * radius).add(emitter.position), + size: (Math.random() * 0.5 + 0.5) * maxSize, + colour: new THREE.Color(), + alpha: 1.0, + life: life, + maxLife: life, + rotation: Math.random() * 2.0 * Math.PI, + rotationRate: Math.random() * 0.01 - 0.005, + velocity: new THREE.Vector3( + (Math.random() * 2 - 1) * radius * maxVelocity, + (Math.random() * 2 - 1) * radius * maxVelocity, + (Math.random() * 2 - 1) * radius * maxVelocity), + }); + } + } + + function _UpdateGeometry() { + const positions = []; + const sizes = []; + const colours = []; + const angles = []; + + for (let p of _particles) { + positions.push(p.position.x, p.position.y, p.position.z); + colours.push(p.colour.r, p.colour.g, p.colour.b, p.alpha); + sizes.push(p.currentSize); + angles.push(p.rotation); + } + + geometry.setAttribute( + 'position', new THREE.Float32BufferAttribute(positions, 3)); + geometry.setAttribute( + 'size', new THREE.Float32BufferAttribute(sizes, 1)); + geometry.setAttribute( + 'aColor', new THREE.Float32BufferAttribute(colours, 4)); + geometry.setAttribute( + 'angle', new THREE.Float32BufferAttribute(angles, 1)); + + geometry.attributes.position.needsUpdate = true; + geometry.attributes.size.needsUpdate = true; + geometry.attributes.aColor.needsUpdate = true; + geometry.attributes.angle.needsUpdate = true; + } + _UpdateGeometry(); + + function _UpdateParticles(timeElapsed) { + for (let p of _particles) { + p.life -= timeElapsed; + } + + _particles = _particles.filter(p => { + return p.life > 0.0; + }); + + for (let p of _particles) { + const t = 1.0 - p.life / p.maxLife; + p.rotation += p.rotationRate; + p.alpha = alphaSpline.getValueAt(t); + p.currentSize = p.size * sizeSpline.getValueAt(t); + p.colour.copy(colorSpline.getValueAt(t)); + + p.position.add(p.velocity.clone().multiplyScalar(timeElapsed)); + + const drag = p.velocity.clone(); + drag.multiplyScalar(timeElapsed * 0.1); + drag.x = Math.sign(p.velocity.x) * Math.min(Math.abs(drag.x), Math.abs(p.velocity.x)); + drag.y = Math.sign(p.velocity.y) * Math.min(Math.abs(drag.y), Math.abs(p.velocity.y)); + drag.z = Math.sign(p.velocity.z) * Math.min(Math.abs(drag.z), Math.abs(p.velocity.z)); + p.velocity.sub(drag); + } + + _particles.sort((a, b) => { + const d1 = camera.position.distanceTo(a.position); + const d2 = camera.position.distanceTo(b.position); + + if (d1 > d2) { + return -1; + } + if (d1 < d2) { + return 1; + } + return 0; + }); + } + + function update(timeElapsed) { + _AddParticles(timeElapsed); + _UpdateParticles(timeElapsed); + _UpdateGeometry(); + } + return { update }; +} + +export { getParticleSystem }; \ No newline at end of file diff --git a/threejs/dodeahedron-fractal/img/circle.png b/threejs/dodeahedron-fractal/img/circle.png new file mode 100644 index 00000000..89c7d9ac Binary files /dev/null and b/threejs/dodeahedron-fractal/img/circle.png differ diff --git a/threejs/dodeahedron-fractal/img/fire.png b/threejs/dodeahedron-fractal/img/fire.png new file mode 100644 index 00000000..cd17c701 Binary files /dev/null and b/threejs/dodeahedron-fractal/img/fire.png differ diff --git a/threejs/dodeahedron-fractal/img/rad-grad.png b/threejs/dodeahedron-fractal/img/rad-grad.png new file mode 100644 index 00000000..ab95e3e0 Binary files /dev/null and b/threejs/dodeahedron-fractal/img/rad-grad.png differ diff --git a/threejs/dodeahedron-fractal/img/smoke.png b/threejs/dodeahedron-fractal/img/smoke.png new file mode 100644 index 00000000..6ab10205 Binary files /dev/null and b/threejs/dodeahedron-fractal/img/smoke.png differ diff --git a/threejs/dodeahedron-fractal/index.html b/threejs/dodeahedron-fractal/index.html new file mode 100644 index 00000000..240973db --- /dev/null +++ b/threejs/dodeahedron-fractal/index.html @@ -0,0 +1,35 @@ + + + + + + + Icosahedron - Fractal + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/threejs/dodeahedron-fractal/script.js b/threejs/dodeahedron-fractal/script.js new file mode 100644 index 00000000..3818c609 --- /dev/null +++ b/threejs/dodeahedron-fractal/script.js @@ -0,0 +1,447 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; +import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; +import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; +import getLayer from "./getLayer.js"; +import { getParticleSystem } from "./getParticleSystem.js"; + + +let scene, camera, renderer, composer, fractalLine; +let n = 1; +let increasing = true; +const maxN = 25; +const speed = 0.03; // Adjust this value to control the speed of n's change +let fireEffect; + +init(); +animate(); + + +function init() { + // Scene setup + scene = new THREE.Scene(); + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); + camera.position.z = 5; + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + // Controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + + // Icosahedron Geometry + //const geometry = new THREE.IcosahedronGeometry(1); + const geometry = new THREE.DodecahedronGeometry(1); + const lineMaterial = new THREE.LineBasicMaterial({ color: 0x00ffff }); + + const edges = new THREE.EdgesGeometry(geometry); + const line = new THREE.LineSegments(edges, lineMaterial); + scene.add(line); + + fireEffect = getParticleSystem({ + camera, + emitter: line, + parent: scene, + rate: 50.0, + texture: 'img/circle.png', + }); + + // Glow Effect (Bloom) + composer = new EffectComposer(renderer); + const renderPass = new RenderPass(scene, camera); + composer.addPass(renderPass); + + const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); + bloomPass.threshold = 0; + bloomPass.strength = 2; // Intensity of the bloom + bloomPass.radius = 0.9; + composer.addPass(bloomPass); + + // Handle window resize + window.addEventListener('resize', onWindowResize); +} + +function animate() { + requestAnimationFrame(animate); + + // Rotate the icosahedron + scene.children[0].rotation.y += 0.001; + scene.children[0].rotation.x -= 0.002; + scene.children[0].rotation.z += 0.002; + + // Update fractal + updateFractal(); + fireEffect.update(0.016); + composer.render(); +} + +function updateFractal() { + // Remove previous fractal + const fractal = scene.getObjectByName('fractal'); + if (fractal) { + scene.remove(fractal); + } + + // Generate new fractal with updated n + const vertices = []; + goldenDragon(-5, 0, 5, 0, true, Math.floor(n), vertices); + + const fractalGeometry = new THREE.BufferGeometry().setFromPoints(vertices); + const lineMaterial = new THREE.LineBasicMaterial({ color: 0x00ffff }); + const fractalLine = new THREE.Line(fractalGeometry, lineMaterial); + fractalLine.position.z = -5; // Position behind the icosahedron + fractalLine.position.y = -1; + fractalLine.position.x = 2; + const s = 1.2; + fractalLine.scale.set(s,s,s); // Adjust the scale of the fractal + fractalLine.name = 'fractal'; + scene.add(fractalLine); + + // Update n + if (increasing) { + n += speed; + if (n >= maxN) { + increasing = false; + } + } else { + n -= speed; + if (n <= 1) { + increasing = true; + } + } +} + +function goldenDragon(x1, y1, x2, y2, turn, n, vertices) { + const goldenRatio = (1 + Math.sqrt(5)) / 2; + const r1 = Math.pow(1 / goldenRatio, 1 / goldenRatio); + const r2 = Math.pow(r1, 2); + const angle1 = Math.acos((1 + r1**2 - r1**4) / (2 * r1)); + const angle2 = Math.acos((1 + r1**4 - r1**2) / (2 * r2)); + + const dist = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); + if (dist < 0.05 || n <= 0) { + vertices.push(new THREE.Vector3(x2, y2, 0)); + return; + } + + const angle = Math.atan2(y2 - y1, x2 - x1); + let px, py; + if (turn) { + px = x1 + dist * r1 * Math.cos(angle + angle1); + py = y1 + dist * r1 * Math.sin(angle + angle1); + } else { + px = x1 + dist * r2 * Math.cos(angle - angle2); + py = y1 + dist * r2 * Math.sin(angle - angle2); + } + + vertices.push(new THREE.Vector3(x1, y1, 0)); + goldenDragon(x1, y1, px, py, true, n - 1, vertices); + goldenDragon(px, py, x2, y2, false, n - 1, vertices); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} +/* +let scene, camera, renderer, composer; + + +init(); +animate(); + +function init() { + // Scene setup + scene = new THREE.Scene(); + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); + camera.position.z = 5; + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + // Controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + + // Icosahedron Geometry + const geometry = new THREE.IcosahedronGeometry(1); + const lineMaterial = new THREE.LineBasicMaterial({ color: 0x00ffff }); + + const edges = new THREE.EdgesGeometry(geometry); + const line = new THREE.LineSegments(edges, lineMaterial); + scene.add(line); + + // Generate Golden Ratio Dragon fractal + const points = []; + goldenDragon(-5, -2, 7, -2, true, 20, points); + const fractalGeometry = new THREE.BufferGeometry().setFromPoints(points); + const fractalLine = new THREE.Line(fractalGeometry, lineMaterial); + fractalLine.position.z = -5; // Position behind the icosahedron + fractalLine.position.x = 1.5; // Position behind the icosahedron + fractalLine.position.y = 0.5; // Position behind the icosahedron + let s = 1.5; + fractalLine.scale.set(s, s, s); + scene.add(fractalLine); + + // Glow Effect (Bloom) + composer = new EffectComposer(renderer); + const renderPass = new RenderPass(scene, camera); + composer.addPass(renderPass); + + const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); + bloomPass.threshold = 0; + bloomPass.strength = 2; // Intensity of the bloom + bloomPass.radius = 1; + composer.addPass(bloomPass); + + // Handle window resize + window.addEventListener('resize', onWindowResize); +} + +function animate() { + requestAnimationFrame(animate); + + // Rotate the icosahedron + scene.children[0].rotation.y += 0.01; + + composer.render(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} + +function goldenDragon(x1, y1, x2, y2, turn, n, vertices) { + const goldenRatio = (1 + Math.sqrt(5)) / 2; + const r1 = Math.pow(1 / goldenRatio, 1 / goldenRatio); + const r2 = Math.pow(r1, 2); + const angle1 = Math.acos((1 + r1**2 - r1**4) / (2 * r1)); + const angle2 = Math.acos((1 + r1**4 - r1**2) / (2 * r2)); + + const dist = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); + if (dist < 0.1 || n <= 0) { + vertices.push(new THREE.Vector3(x2, y2, 0)); + return; + } + + const angle = Math.atan2(y2 - y1, x2 - x1); + let px, py; + if (turn) { + px = x1 + dist * r1 * Math.cos(angle + angle1); + py = y1 + dist * r1 * Math.sin(angle + angle1); + } else { + px = x1 + dist * r2 * Math.cos(angle - angle2); + py = y1 + dist * r2 * Math.sin(angle - angle2); + } + + goldenDragon(x1, y1, px, py, true, n - 1, vertices); + goldenDragon(px, py, x2, y2, false, n - 1, vertices); +} +*/ + +/* +init(); +animate(); + +function init() { + // Scene setup + scene = new THREE.Scene(); + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); + camera.position.z = 5; + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + // Controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + + // Icosahedron Geometry + const geometry = new THREE.IcosahedronGeometry(1); + const lineMaterial = new THREE.LineBasicMaterial({ color: 0x00ffff }); + + const edges = new THREE.EdgesGeometry(geometry); + const line = new THREE.LineSegments(edges, lineMaterial); + scene.add(line); + + // Generate Golden Ratio Dragon fractal + const points = pDragon(12, 1.5, 0, 0, 0x00ffff); + const fractalGeometry = new THREE.BufferGeometry().setFromPoints(points); + const fractalLine = new THREE.Line(fractalGeometry, lineMaterial); + fractalLine.position.z = -5; // Position behind the icosahedron + fractalLine.position.x = 3.9; // Position behind the icosahedron + fractalLine.position.y = 1; // Position behind the icosahedron + let s = 0.2; + fractalLine.scale.set(s,s,s); + scene.add(fractalLine); + + // Glow Effect (Bloom) + composer = new EffectComposer(renderer); + const renderPass = new RenderPass(scene, camera); + composer.addPass(renderPass); + + const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); + bloomPass.threshold = 0; + bloomPass.strength = 2; // Intensity of the bloom + bloomPass.radius = 1; + composer.addPass(bloomPass); + + // Handle window resize + window.addEventListener('resize', onWindowResize); +} + +function animate() { + requestAnimationFrame(animate); + + // Rotate the icosahedron + scene.children[0].rotation.y += 0.01; + + composer.render(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} + +function pDragon(order, scale, hShift, vShift, color) { + //const phi = (1 + Math.sqrt(5)) / 2; // Golden ratio + let c = 0, x = 0, y = 0, d = 1; + const n = 1 << order; + const points = []; + x = y = 0; + + for (let i = 0; i <= n;) { + points.push(new THREE.Vector3((x + hShift) * scale, (y + vShift) * scale, 0)); + + let c1 = c & 1, c2 = c & 2; + let c2x = 1 * d; + if (c2 > 0) { c2x = -1 * d; } + let c2y = -1 * c2x; + + if (c1 > 0) { + y += c2y; + } else { + x += c2x; + } + + i++; + c += i / (i & -i); + } + + return points; +} + +*/ + + + +/* +function init() { + // Scene setup + scene = new THREE.Scene(); + camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); + camera.position.z = 5; + + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + // Controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.dampingFactor = 0.03; + + // Icosahedron Geometry + const geometry = new THREE.IcosahedronGeometry(1.5); + const lineMaterial = new THREE.LineBasicMaterial({ color: 0x00ffff }); + + const edges = new THREE.EdgesGeometry(geometry); + const line = new THREE.LineSegments(edges, lineMaterial); + scene.add(line); + + // Generate Lévy C curve fractal + const maxIteration = 12; // Controls the complexity of the fractal + const points = []; + c_curve(-2, 0, 4, 0, maxIteration, points); + const fractalGeometry = new THREE.BufferGeometry().setFromPoints(points); + const fractalLine = new THREE.Line(fractalGeometry, lineMaterial); + fractalLine.rotation.z = Math.PI; + fractalLine.position.z = -5; // Position behind the icosahedron + fractalLine.position.y = 2; + fractalLine.scale.set(1.7, 1.7, 1.7); + + scene.add(fractalLine); + + // Glow Effect (Bloom) + composer = new EffectComposer(renderer); + const renderPass = new RenderPass(scene, camera); + composer.addPass(renderPass); + + const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); + bloomPass.threshold = 0; + bloomPass.strength = 1.9; // Intensity of the bloom + bloomPass.radius = 1; + composer.addPass(bloomPass); + + // Handle window resize + window.addEventListener('resize', onWindowResize); +} + +function animate() { + requestAnimationFrame(animate); + + // Rotate the icosahedron + scene.children[0].rotation.y += 0.005; + scene.children[0].rotation.z += 0.002; + + composer.render(); +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); +} + +// Function to create Lévy C curve fractal +function c_curve(x, y, length, angle, iteration, points) { + const toRadians = (degrees) => degrees * (Math.PI / 180); + const default_angle = 45; + + if (iteration > 0) { + length = length / Math.sqrt(2); + + // First recursive call + c_curve(x, y, length, angle + default_angle, iteration - 1, points); + + // Update x, y coordinates + x = x + length * Math.cos(toRadians(angle + default_angle)); + y = y + length * Math.sin(toRadians(angle + default_angle)); + + // Second recursive call + c_curve(x, y, length, angle - default_angle, iteration - 1, points); + } else { + // Add the final segment to the points array + points.push(new THREE.Vector3(x, y, 0)); + points.push(new THREE.Vector3( + x + length * Math.cos(toRadians(angle)), + y + length * Math.sin(toRadians(angle)), + 0 + )); + } +} +*/ \ No newline at end of file diff --git a/threejs/dodeahedron-fractal/style.css b/threejs/dodeahedron-fractal/style.css new file mode 100644 index 00000000..6eb548eb --- /dev/null +++ b/threejs/dodeahedron-fractal/style.css @@ -0,0 +1,19 @@ +* +{ + margin: 0; + padding: 0; +} + +html, +body +{ + overflow: hidden; +} + +.webgl +{ + position: fixed; + top: 0; + left: 0; + outline: none; +} diff --git a/threejs/icosahedron-fractal/script.js b/threejs/icosahedron-fractal/script.js index 4f477504..05201298 100644 --- a/threejs/icosahedron-fractal/script.js +++ b/threejs/icosahedron-fractal/script.js @@ -29,6 +29,7 @@ function init() { // Icosahedron Geometry const geometry = new THREE.IcosahedronGeometry(1); + //const geometry = new THREE.DodecahedronGeometry(1); const lineMaterial = new THREE.LineBasicMaterial({ color: 0x00ffff }); const edges = new THREE.EdgesGeometry(geometry); @@ -43,7 +44,7 @@ function init() { const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85); bloomPass.threshold = 0; bloomPass.strength = 2; // Intensity of the bloom - bloomPass.radius = 1; + bloomPass.radius = 0.9; composer.addPass(bloomPass); // Handle window resize @@ -54,7 +55,9 @@ function animate() { requestAnimationFrame(animate); // Rotate the icosahedron - scene.children[0].rotation.y += 0.01; + scene.children[0].rotation.y += 0.001; + scene.children[0].rotation.x -= 0.002; + scene.children[0].rotation.z += 0.002; // Update fractal updateFractal();