[READ-ONLY] Mirror of https://github.com/flo-bit/flo-bit.github.io. my personal website, w/ astro, svelte, tailwind, typescript, threlte flo-bit.dev/
portfolio portfolio-website svelte sveltekit tailwind threejs threlte typescript
0

Configure Feed

Select the types of activity you want to include in your feed.

update projects

Florian (Sep 20, 2024, 3:15 AM +0200) 1d307a48 b59c55b8

+491 -1453
+13 -3
package-lock.json
··· 30 30 "tailwindcss": "^3.4.0", 31 31 "threlte-uikit": "^0.4.2", 32 32 "tinygradient": "^1.1.5", 33 + "uber-noise": "^0.2.1", 33 34 "vite-imagetools": "^6.2.9" 34 35 }, 35 36 "devDependencies": { ··· 4467 4468 } 4468 4469 }, 4469 4470 "node_modules/simplex-noise": { 4470 - "version": "4.0.1", 4471 - "resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-4.0.1.tgz", 4472 - "integrity": "sha512-zl/+bdSqW7HJOQ0oDbxrNYaF4F5ik0i7M6YOYmEoIJNtg16NpvWaTTM1Y7oV/7T0jFljawLgYPS81Uu2rsfo1A==" 4471 + "version": "4.0.3", 4472 + "resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-4.0.3.tgz", 4473 + "integrity": "sha512-qSE2I4AngLQG7BXqoZj51jokT4WUXe8mOBrvfOXpci8+6Yu44+/dD5zqDpOx3Ux792eamTd2lLcI8jqFntk/lg==" 4473 4474 }, 4474 4475 "node_modules/sirv": { 4475 4476 "version": "2.0.4", ··· 5384 5385 }, 5385 5386 "engines": { 5386 5387 "node": ">=14.17" 5388 + } 5389 + }, 5390 + "node_modules/uber-noise": { 5391 + "version": "0.2.1", 5392 + "resolved": "https://registry.npmjs.org/uber-noise/-/uber-noise-0.2.1.tgz", 5393 + "integrity": "sha512-0TfMzwduiMJ18BQjeeioBqdQHcBU1Ns+HbnXhA3F6NPdP0vzjNzk/MFaFIN3f2MIe2MdS+WBgIojcynPrTyvlQ==", 5394 + "dependencies": { 5395 + "alea": "^1.0.1", 5396 + "simplex-noise": "^4.0.3" 5387 5397 } 5388 5398 }, 5389 5399 "node_modules/update-browserslist-db": {
+1
package.json
··· 61 61 "tailwindcss": "^3.4.0", 62 62 "threlte-uikit": "^0.4.2", 63 63 "tinygradient": "^1.1.5", 64 + "uber-noise": "^0.2.1", 64 65 "vite-imagetools": "^6.2.9" 65 66 } 66 67 }
+2 -1
tailwind.config.js
··· 6 6 theme: { 7 7 extend: { 8 8 colors: { 9 - accent: colors.cyan 9 + accent: colors.cyan, 10 + background: '#010205' 10 11 }, 11 12 gridTemplateRows: { 12 13 '[auto,auto,1fr]': 'auto auto 1fr'
+6 -4
todo.md
··· 5 5 - youtube party djay 6 6 - instalingua 7 7 - vr portfolio 8 - - planet generator 9 8 - atlas 10 9 - chess puzzles 11 10 - meditation 12 11 - silly engine 12 + - [x] planet generator 13 13 - [x] life 14 14 - improve planet stuff 15 15 - add favourite stack? 16 + - only load videos when visible 16 17 - add images, descriptions, links, tags to all projects 17 - - update favicon 18 - - fix android bug 19 - - improve performance 18 + - update favicon (dark mode) 19 + - featured project? 20 + - autumn planet 21 + - halloween planet
+1 -1
src/app.html
··· 42 42 </head> 43 43 <body 44 44 data-sveltekit-preload-data="hover" 45 - class="h-full bg-white dark:bg-black transition-colors duration-150 dark:transition-color dark:duration-150 lowercase" 45 + class="h-full bg-white dark:bg-background transition-colors duration-150 dark:transition-color dark:duration-150 lowercase" 46 46 > 47 47 <div style="display: contents">%sveltekit.body%</div> 48 48 </body>
+105 -54
src/lib/projects.ts
··· 4 4 import fake3d from '$lib/images/projects-new/depth3dcomponent/depth3dcomponent.mp4'; 5 5 import hyperlumen from '$lib/images/projects-new/hyperlumen-demo.mp4'; 6 6 import marblellous from '$lib/images/projects-new/marblellous-demo.mp4'; 7 - import oldcode from '$lib/images/projects-new/old-code-demo.mp4'; 7 + import oldcode from '$lib/images/projects-new/old-code/old-code-demo.mp4'; 8 8 // import autostereogramrenderer from '$lib/images/projects-new/autostereogram-renderer-demo.mp4'; 9 - // import mandala from '$lib/images/projects-new/mandala-demo.mp4'; 9 + import mandala from '$lib/images/projects-new/mandala-demo.mp4'; 10 10 import threltevrcodeeditor from '$lib/images/projects-new/threlte-vr-code-editor-demo.mp4'; 11 + import tinyplanets from '$lib/images/projects-new/tiny-planets/tiny-planets-demo.mp4'; 12 + 13 + // @ts-expect-error - module not found 14 + import oldcode1 from '$lib/images/projects-new/old-code/image1.png?w=1024&format=webp'; 15 + // @ts-expect-error - module not found 16 + import oldcode2 from '$lib/images/projects-new/old-code/image2.png?w=1024&format=webp'; 17 + // @ts-expect-error - module not found 18 + import oldcode3 from '$lib/images/projects-new/old-code/image4.png?w=1024&format=webp'; 11 19 12 20 // @ts-expect-error - module not found 13 21 import hyperlumen1 from '$lib/images/projects-new/hyperlumen/1.jpg?w=1024&format=webp'; ··· 26 34 import shadowshmup3 from '$lib/images/projects-new/shmup/image3.png?w=1024&format=webp'; 27 35 // @ts-expect-error - module not found 28 36 import shadowshmup4 from '$lib/images/projects-new/shmup/image4.png?w=1024&format=webp'; 37 + 38 + // @ts-expect-error - module not found 39 + import life1 from '$lib/images/projects-new/life/image1.png?w=1024&format=webp'; 40 + // @ts-expect-error - module not found 41 + import life2 from '$lib/images/projects-new/life/image2.png?w=1024&format=webp'; 42 + // @ts-expect-error - module not found 43 + import life3 from '$lib/images/projects-new/life/image3.png?w=1024&format=webp'; 44 + // @ts-expect-error - module not found 45 + import life4 from '$lib/images/projects-new/life/image4.png?w=1024&format=webp'; 46 + 47 + // @ts-expect-error - module not found 48 + import tinyplanets1 from '$lib/images/projects-new/tiny-planets/image1.png?w=1024&format=webp'; 49 + // @ts-expect-error - module not found 50 + import tinyplanets2 from '$lib/images/projects-new/tiny-planets/image2.png?w=1024&format=webp'; 51 + // @ts-expect-error - module not found 52 + import tinyplanets3 from '$lib/images/projects-new/tiny-planets/image3.png?w=1024&format=webp'; 29 53 30 54 import shadowshmup from '$lib/images/projects-new/shmup/shadow-shmup-demo.mp4'; 31 55 ··· 52 76 53 77 export const projects: Project[] = [ 54 78 { 55 - src: life, 56 - key: 'life', 57 - projectUrl: 'https://flo-bit.itch.io/life', 58 - aspect: 'aspect-[15/9]', 59 - name: 'life', 60 - description: 61 - 'made for gmtk jam 2024. relaxing spore/fl0w/agario-like game with a nature documentary vibe. eat, grow, evolve.', 62 - projectPageVersion: 'full' 79 + src: tinyplanets, 80 + key: 'tiny-planets', 81 + projectUrl: 'https://flo-bit.github.io/tiny-planets/', 82 + aspect: 'aspect-[1]', 83 + name: 'tiny planets', 84 + codeUrl: 'https://github.com/flo-bit/tiny-planets', 85 + images: [tinyplanets1, tinyplanets2, tinyplanets3], 86 + description: 'procedurally generated tiny planets made with three.js.', 87 + projectPageVersion: 'tiered', 88 + tags: ['three.js', 'typescript', '3d', 'procedural generation'] 63 89 }, 64 90 { 65 91 src: svelteswipecards, 66 92 key: 'svelte-swiper-cards', 67 - alt: 'svelte-swiper-cards', 68 93 href: 'https://flo-bit.github.io/svelte-swiper-cards/', 69 94 70 95 projectUrl: 'https://flo-bit.github.io/svelte-swiper-cards/', ··· 87 112 'modern (uses @use-gesture/vanilla for gestures)' 88 113 ] 89 114 }, 115 + 116 + { 117 + src: mandala, 118 + key: 'svg-mandala', 119 + projectUrl: 'https://flo-bit.github.io/mandala/', 120 + aspect: 'aspect-1', 121 + name: 'svg mandala drawer', 122 + description: 123 + 'one of my earlier projects, draw svg mandalas in the browser. works on mobile too. made with paperjs and bulma. try the interactive demo above!', 124 + tags: ['svg', 'paperjs', 'javascript', 'bulma'], 125 + projectPageVersion: 'full', 126 + demo: 'mandala', 127 + codeUrl: 'https://github.com/flo-bit/mandala' 128 + }, 129 + 90 130 { 91 131 src: fluidtexteffect, 92 132 key: 'text-effect-fluid', 93 - alt: 'Text Fluid Effect', 94 133 projectUrl: 'https://flo-bit.github.io/text_effect_fluid/', 95 134 aspect: 'aspect-[23/9]', 96 135 name: 'fluid text effect', ··· 101 140 demo: 'fluid' 102 141 }, 103 142 { 104 - src: shadowshmup, 105 - key: 'shadow-shmup', 106 - codeUrl: 'https://flo-bit.github.io/shadow-shmup/', 107 - alt: 'shadow shmup', 108 - aspect: 'aspect-[13/9]', 109 - name: 'shadow shmup', 110 - description: 111 - "fun and casual top-down, roguelike shoot 'em up with shadow elements and colorful neon effects. made for piratejam 2024", 112 - tags: ['game', '2d', 'pixijs', 'rapier2d', 'typescript'], 113 - images: [shadowshmup1, shadowshmup2, shadowshmup3, shadowshmup4], 114 - projectPageVersion: 'carousel', 115 - projectUrl: 'https://flo-bit.itch.io/shadow-shmup' 116 - }, 117 - { 118 143 src: hyperlumen, 119 144 key: 'hyperlumen', 120 - alt: 'Hyperlumen', 121 145 122 146 projectUrl: 'https://hyperlumen.de', 123 147 ··· 152 176 // codeUrl: 'https://github.com/flo-bit/mandala' 153 177 // }, 154 178 { 179 + src: shadowshmup, 180 + key: 'shadow-shmup', 181 + codeUrl: 'https://flo-bit.github.io/shadow-shmup/', 182 + aspect: 'aspect-[13/9]', 183 + name: 'shadow shmup', 184 + description: 185 + "fun and casual top-down, roguelike shoot 'em up with shadow elements and colorful neon effects. made for piratejam 2024", 186 + tags: ['game', '2d', 'pixijs', 'rapier2d', 'typescript'], 187 + images: [shadowshmup1, shadowshmup2, shadowshmup3, shadowshmup4], 188 + projectPageVersion: 'carousel', 189 + projectUrl: 'https://flo-bit.itch.io/shadow-shmup' 190 + }, 191 + { 155 192 src: fake3d, 156 193 key: 'depth3dcomponent', 157 - alt: 'depth3dcomponent', 158 194 projectUrl: 'https://flo-bit.github.io/svelte-depth-3d-component/', 159 195 aspect: 'aspect-[9/16]', 160 196 name: 'svelte depth 3d component', 161 - description: 'turns an image plus depth map into a kind of 3d image. made for svelte.', 197 + description: 198 + 'turns an image plus depth map into a kind of 3d image. made for svelte. click on the image to switch between images.', 162 199 codeUrl: 'https://github.com/flo-bit/svelte-depth-3d-component', 163 - tags: ['svelte', 'depthmap', '3d', 'threlte'] 200 + tags: ['svelte', 'depthmap', '3d', 'threlte'], 201 + projectPageVersion: 'tiered', 202 + demo: 'depth3d' 164 203 }, 204 + { 205 + src: life, 206 + key: 'life', 207 + projectUrl: 'https://flo-bit.itch.io/life', 208 + aspect: 'aspect-[15/9]', 209 + name: 'life', 210 + description: 211 + 'made for gmtk jam 2024. relaxing spore/fl0w/agario-like game with a nature documentary vibe. eat, grow, evolve.', 212 + projectPageVersion: 'grid', 213 + images: [life1, life2, life3, life4], 214 + tags: ['game', '2d', 'pixijs', 'gamejam', 'typescript'] 215 + }, 216 + 217 + { 218 + src: oldcode, 219 + key: 'old-code', 220 + projectUrl: 'https://flo-bit.github.io/old-code/', 221 + aspect: 'aspect-[16/11]', 222 + name: 'old coding projects', 223 + codeUrl: 'https://github.com/flo-bit/old-code', 224 + description: 'a collection of my old coding projects. mostly procedural art made with p5.js', 225 + tags: ['p5.js', 'javascript', 'game', 'art', 'flowfield'], 226 + images: [oldcode1, oldcode2, oldcode3], 227 + projectPageVersion: 'tiered' 228 + }, 229 + { 230 + src: threltevrcodeeditor, 231 + key: 'vr-code-editor', 232 + aspect: 'aspect-[16/11]', 233 + name: 'vr code editor', 234 + description: 'a code editor in virtual reality made with threlte.', 235 + codeUrl: 'https://github.com/flo-bit/threlte-vr-code-editor', 236 + highlights: ['works with hot module replacement'], 237 + tags: ['threlte', 'svelte', 'vr', 'typescript', 'code editor'] 238 + }, 239 + 165 240 { 166 241 src: marblellous, 167 242 key: 'marblellous', 168 - alt: 'marblellous', 169 243 projectUrl: 'https://flo-bit.github.io/ball-game/', 170 244 aspect: 'aspect-[9/10]', 171 245 name: 'marblellous', ··· 173 247 'simple 3d browser game made with threlte. inspired by the game "marble blast gold". work in progress.', 174 248 codeUrl: 'https://github.com/flo-bit/ball-game', 175 249 tags: ['threlte', 'svelte', '3d', 'typescript', 'game'] 176 - }, 177 - { 178 - src: oldcode, 179 - key: 'old-code', 180 - alt: 'old projects', 181 - projectUrl: 'https://flo-bit.github.io/old-code/', 182 - aspect: 'aspect-[16/11]', 183 - name: 'old coding projects', 184 - codeUrl: 'https://github.com/flo-bit/old-code', 185 - description: 'a collection of my old coding projects. mostly made with p5.js', 186 - tags: ['p5.js', 'javascript', 'game', 'art', 'flowfield'] 187 - }, 188 - { 189 - src: threltevrcodeeditor, 190 - key: 'vr-code-editor', 191 - alt: 'vr code editor', 192 - projectUrl: 'https://github.com/flo-bit/threlte-vr-code-editor', 193 - aspect: 'aspect-[16/11]', 194 - name: 'vr code editor', 195 - description: 'a code editor in virtual reality made with threlte.', 196 - codeUrl: 'https://github.com/flo-bit/threlte-vr-code-editor', 197 - highlights: ['works with hot module replacement'], 198 - tags: ['threlte', 'svelte', 'vr', 'typescript', 'code editor'] 199 250 } 200 251 // { 201 252 // src: autostereogramrenderer,
+2 -2
src/lib/3D/Planet.svelte
··· 10 10 let planet = new Planet({ preset: 'beach' }); 11 11 let planetMesh = suspend(planet.create()); 12 12 13 - export const redo = () => { 13 + export const redo = async () => { 14 14 planet = new Planet({ preset: presets[Math.floor(Math.random() * presets.length)] }); 15 - planetMesh = suspend(planet.create()); 15 + planetMesh = planet.create(); 16 16 } 17 17 </script> 18 18
-4
src/lib/3D/Scene.svelte
··· 165 165 shadow.bias={0.0001} 166 166 shadow.mapSize.width={256} 167 167 shadow.mapSize.height={256} 168 - shadow.camera.left={-3} 169 - shadow.camera.right={1} 170 - shadow.camera.top={3} 171 - shadow.camera.bottom={-1} 172 168 /> 173 169 174 170 <SheetObject key="planet" let:Transform let:Sync>
+1 -3
src/lib/3D/Stars.svelte
··· 1 1 <script lang="ts"> 2 2 import { T, useTask } from '@threlte/core'; 3 - 4 - import UberNoise from './uber-noise'; 5 - import { Color } from 'three'; 3 + import UberNoise from 'uber-noise'; 6 4 7 5 const count = 5000; 8 6 const maxHeight = 30;
-611
src/lib/3D/uber-noise.ts
··· 1 - import { 2 - type NoiseFunction2D, 3 - type NoiseFunction3D, 4 - type NoiseFunction4D, 5 - createNoise2D, 6 - createNoise3D, 7 - createNoise4D 8 - } from 'simplex-noise'; 9 - import alea from 'alea'; 10 - 11 - /** 12 - * 13 - * @property {number} seed - seed for the noise, if not provided, Math.random() will be used, 14 - * currently same results can only be guaranteed for newly created noise objects with same seed 15 - * (as opposed to an old noise object where you changed the seed) 16 - * 17 - * @property {number} min - minimun value of noise 18 - * @property {number} max - maximum value of noise 19 - * 20 - * @property {number} scale - scale of the noise 21 - * @property {number} power - power of the noise (1 = linear, 2 = quadratic, etc) 22 - * @property {Vector} shift - move noise in 2D, 3D or 4D space 23 - * 24 - * @property {number} octaves - number of layers for fbm noise 25 - * @property {number} gain - how much to multiply amplitude per layer 26 - * @property {number} lacunarity - how much to multiply scale per layer 27 - * @property {array} amps - array of amplitudes for each layer 28 - * @property {number} erosion - how much previous layers influence amplitude of later layers 29 - * @property {number} sharpness - billowed or rigded noise (0 = normal, 1 = billowed, -1 = ridged) 30 - * @property {number} steps - will turn noise into steps (integer, number of steps) 31 - * 32 - * @property {number} warp - how much to warp the noise 33 - * @property {number} warpNoise - noise to warp the noise with 34 - * @property {number} warp2 - second warp, can only be used if warp is used too 35 - * @property {number} warpNoise2 - second warp noise 36 - * 37 - * @property {boolean} invert - invert the noise 38 - * @property {boolean} abs - absolute value of the noise 39 - * @property {boolean} clamp - clamp the noise between min and max 40 - * @property {boolean} tileX - tile the noise in x direction 41 - * @property {boolean} tileY - tile the noise in y direction 42 - * @property {boolean} tile - tile the noise in all directions (will override tileX and tileY) 43 - * 44 - * @constructor 45 - */ 46 - 47 - function lerp(a: number, b: number, t: number): number { 48 - return (b - a) * t + a; 49 - } 50 - 51 - function setLength(vector: number[], length: number): number[] { 52 - const oldLength = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0)); 53 - if (oldLength === 0) return vector; 54 - const scale = length / oldLength; 55 - return vector.map((v) => v * scale); 56 - } 57 - 58 - // angle between two 3D vectors 59 - function angleTo(a: number[], b: number[]): number { 60 - const dot = a.reduce((sum, v, i) => sum + v * b[i], 0); 61 - const length = Math.sqrt( 62 - a.reduce((sum, v) => sum + v * v, 0) * b.reduce((sum, v) => sum + v * v, 0) 63 - ); 64 - return Math.acos(dot / length); 65 - } 66 - 67 - type ErosionUpFunction = (derivative: number[]) => number[]; 68 - 69 - export type VectorObject = { 70 - x: number; 71 - y: number; 72 - z?: number; 73 - w?: number; 74 - }; 75 - 76 - export type NoiseParameterInput = number | UberNoise | UberNoiseOptions; 77 - export type NoiseParameter = number | UberNoise; 78 - 79 - export type UberNoiseOptions = { 80 - seed?: string | number; 81 - 82 - min?: NoiseParameterInput; 83 - max?: NoiseParameterInput; 84 - 85 - scale?: NoiseParameterInput; 86 - power?: NoiseParameterInput; 87 - 88 - shift?: number[]; 89 - 90 - octaves?: number; 91 - gain?: NoiseParameterInput; 92 - lacunarity?: NoiseParameterInput; 93 - 94 - amps?: number[]; 95 - 96 - layers?: UberNoiseOptions[]; 97 - 98 - erosion?: NoiseParameterInput; 99 - erosionUp?: ErosionUpFunction; 100 - 101 - sharpness?: NoiseParameterInput; 102 - steps?: NoiseParameterInput; 103 - warp?: NoiseParameterInput; 104 - warpNoise?: UberNoise; 105 - warp2?: NoiseParameterInput; 106 - warpNoise2?: UberNoise; 107 - 108 - invert?: boolean; 109 - abs?: boolean; 110 - clamp?: boolean; 111 - tileX?: boolean; 112 - tileY?: boolean; 113 - tile?: boolean; 114 - }; 115 - 116 - export class UberNoise { 117 - private noise2D: NoiseFunction2D | undefined; 118 - private noise3D: NoiseFunction3D | undefined; 119 - private noise4D: NoiseFunction4D | undefined; 120 - private seed: string | number; 121 - 122 - private _min: NoiseParameter = -1; 123 - private _max: NoiseParameter = 1; 124 - 125 - private _scale: NoiseParameter = 1; 126 - private _power: NoiseParameter = 1; 127 - 128 - private shift: number[] | undefined = undefined; 129 - 130 - private octaves = 0; 131 - private _gain: NoiseParameter = 0.5; 132 - private _lacunarity: NoiseParameter = 2; 133 - 134 - private amps: number[] = []; 135 - 136 - private _erosion: NoiseParameter = 0; 137 - private _sharpness: NoiseParameter = 0; 138 - private _steps: NoiseParameter = 0; 139 - 140 - private _warp: NoiseParameter = 0; 141 - private _warpNoise: UberNoise | undefined; 142 - private _warp2: NoiseParameter = 0; 143 - private _warpNoise2: UberNoise | undefined; 144 - 145 - private tileX = false; 146 - private tileY = false; 147 - 148 - private erosionDelta = 0; 149 - private erosionDerivative?: number[] = undefined; 150 - private erosionUp?: ErosionUpFunction; 151 - 152 - private static erosionDefaultUp = [0, 0, 1]; 153 - 154 - private position = [0, 0]; 155 - 156 - private pngr; 157 - 158 - private layers: UberNoise[] = []; 159 - 160 - constructor(options: UberNoiseOptions = {}) { 161 - this.seed = options.seed ?? Math.random(); 162 - this.pngr = alea(this.seed); 163 - 164 - this.min = options.min ?? -1; 165 - this.max = options.max ?? 1; 166 - 167 - this.scale = options.scale ?? 1; 168 - this.power = options.power ?? 1; 169 - 170 - this.octaves = options.octaves ?? options.layers?.length ?? 0; 171 - this.gain = options.gain ?? 0.5; 172 - this.lacunarity = options.lacunarity ?? 2; 173 - 174 - this.erosion = options.erosion ?? 0; 175 - this.sharpness = options.sharpness ?? 0; 176 - this.steps = options.steps ?? 0; 177 - 178 - this.warp = options.warp ?? 0; 179 - this.warpNoise = options.warpNoise; 180 - this.warp2 = options.warp2 ?? 0; 181 - this.warpNoise2 = options.warpNoise2; 182 - 183 - this.processLayers(options); 184 - 185 - this.tileX = options.tileX ?? options.tile ?? false; 186 - this.tileY = options.tileY ?? options.tile ?? false; 187 - } 188 - 189 - processLayers(options: UberNoiseOptions) { 190 - this.amps = options.amps ?? []; 191 - 192 - for (let i = 0; i < this.octaves; i++) { 193 - const layerOptions = options.layers?.[i] ?? {}; 194 - if (layerOptions instanceof UberNoise) { 195 - this.layers[i] = layerOptions; 196 - } else { 197 - if (layerOptions.seed === undefined) layerOptions.seed = this.pngr(); 198 - this.layers[i] = new UberNoise(layerOptions); 199 - } 200 - } 201 - if (this.layers.length == 0) { 202 - this.noise2D = createNoise2D(this.pngr); 203 - this.noise3D = createNoise3D(this.pngr); 204 - this.noise4D = createNoise4D(this.pngr); 205 - } 206 - } 207 - 208 - // same as normalized but returns between min and max 209 - get( 210 - x: number | VectorObject | Array<number>, 211 - y: number | undefined = undefined, 212 - z: number | undefined = undefined, 213 - w: number | undefined = undefined 214 - ): number { 215 - const norm = this.normalized(x, y, z, w); 216 - return this.toMinMax(norm); 217 - } 218 - 219 - // same as get but returns between -1 and 1 220 - normalized( 221 - x: number | VectorObject | Array<number>, 222 - y: number | undefined = undefined, 223 - z: number | undefined = undefined, 224 - w: number | undefined = undefined 225 - ): number { 226 - // if x is an array, treat it as a vector 227 - if (Array.isArray(x)) { 228 - w = x[3]; 229 - z = x[2]; 230 - y = x[1]; 231 - x = x[0]; 232 - } else if (typeof x === 'object') { 233 - w = x.w; 234 - z = x.z; 235 - y = x.y; 236 - x = x.x; 237 - } 238 - // if y is undefined, treat it as 2D noise with y = 0 239 - y = y ?? 0; 240 - 241 - return this.getNoise(x, y, z, w); 242 - } 243 - 244 - private warpPosition(warp: number, warpNoise: UberNoise | undefined) { 245 - if (warp == undefined || warp == 0) return; 246 - 247 - if (warpNoise != undefined) warpNoise.position = this.position; 248 - 249 - let x = this.position[0], 250 - y = this.position[1], 251 - z = this.position[2], 252 - w = this.position[3]; 253 - 254 - const noise = warpNoise ?? this; 255 - const scl = this.scale; 256 - 257 - this.position = [x + 54.47 * scl, y + 34.98 * scl]; 258 - if (z != undefined) this.position.push(z + 21.63 * scl); 259 - if (w != undefined) this.position.push(w + 67.1 * scl); 260 - 261 - x += noise.getFBM() * warp; 262 - y += noise.getFBM() * warp; 263 - if (z != undefined) z += noise.getFBM() * warp; 264 - if (w != undefined) w += noise.getFBM() * warp; 265 - 266 - this.position = [x, y, z, w]; 267 - } 268 - 269 - private tilePosition() { 270 - if (!this.tileX && !this.tileY) return; 271 - const x = this.position[0]; 272 - const y = this.position[1]; 273 - let newX = 0, 274 - newY = 0, 275 - newZ = 0, 276 - newW = 0; 277 - if (this.tileX) { 278 - newX = Math.sin(x * Math.PI * 2); 279 - newY = Math.cos(x * Math.PI * 2); 280 - } 281 - if (this.tileY) { 282 - newZ = Math.sin(y * Math.PI * 2); 283 - newW = Math.cos(y * Math.PI * 2); 284 - } 285 - if (this.tileX && !this.tileY) { 286 - this.position = [newX, newY + y]; 287 - } else if (this.tileY && !this.tileX) { 288 - this.position = [newZ + x, newW]; 289 - } else if (this.tileX && this.tileY) { 290 - this.position = [newX, newY, newZ, newW]; 291 - } 292 - } 293 - 294 - private getDerivative( 295 - x: number, 296 - y: number, 297 - z: number, 298 - n: number | undefined = undefined 299 - ): number[] | undefined { 300 - // left side or central difference 301 - // very expensive (four/six noise calls), should be changed to analytical derivatives 302 - // see https://iquilezles.org/www/articles/morenoise/morenoise.htm 303 - 304 - if (z == undefined) return undefined; 305 - 306 - const mov = this.erosionDelta; 307 - 308 - const dx = (n ?? this.get(x - mov, y, z)) - this.get(x + mov, y, z); 309 - const dy = (n ?? this.get(x, y - mov, z)) - this.get(x, y + mov, z); 310 - const dz = (n ?? this.get(x, y, z - mov)) - this.get(x, y, z + mov); 311 - 312 - this.erosionDerivative = [dx, dy, dz]; 313 - return this.erosionDerivative; 314 - } 315 - 316 - private getBaseLayer(scale: number, x: number, y: number, z?: number, w?: number): number { 317 - if (z != undefined && w != undefined && this.noise4D != undefined) { 318 - return this.noise4D(x * scale, y * scale, z * scale, w * scale); 319 - } 320 - if (z != undefined && this.noise3D != undefined) { 321 - return this.noise3D(x * scale, y * scale, z * scale); 322 - } 323 - if (this.noise2D != undefined) { 324 - return this.noise2D(x * scale, y * scale); 325 - } 326 - return 0; 327 - } 328 - 329 - private getFBM(useErosion = false): number { 330 - const x = this.position[0], 331 - y = this.position[1], 332 - z = this.position[2], 333 - w = this.position[3]; 334 - 335 - const scale = this.scale; 336 - 337 - if (this.layers.length === 0) { 338 - return this.getBaseLayer(scale, x, y, z, w); 339 - } 340 - 341 - let maxAmp = 1; 342 - let amp = 1, 343 - freq = scale; 344 - 345 - const lacunarity = this.lacunarity; 346 - const gain = this.gain; 347 - 348 - // for now we'll always ignore erosion 349 - // remove the following line later to enable erosion 350 - useErosion = false; 351 - const erosion = useErosion ? this.erosion : 0; 352 - const up = 353 - this.erosionUp != undefined ? this.erosionUp(this.position) : UberNoise.erosionDefaultUp; 354 - const sum = [0, 0, 0]; 355 - 356 - let n = 0; 357 - 358 - for (let i = 0; i < this.octaves; i++) { 359 - const layer = this.layers[i]; 360 - 361 - const layerAmp = this.amps[i] ?? 1; 362 - 363 - const value = 364 - layer.get( 365 - x * freq, 366 - y * freq, 367 - z != undefined ? z * freq : undefined, 368 - w != undefined ? w * freq : undefined 369 - ) * 370 - amp * 371 - layerAmp; 372 - if (erosion > 0) { 373 - const derivative = layer.getDerivative(x * freq, y * freq, z * freq); 374 - 375 - if (derivative != undefined) { 376 - setLength(derivative, amp * layerAmp); 377 - sum[0] += derivative[0]; 378 - sum[1] += derivative[1]; 379 - sum[2] += derivative[2]; 380 - 381 - const mult = Math.abs(1 - angleTo(up, sum) / Math.PI); 382 - 383 - n += value * (mult * erosion + 1 - erosion); 384 - } 385 - } else { 386 - n += value; 387 - } 388 - 389 - amp *= gain; 390 - freq *= lacunarity; 391 - maxAmp += amp * layerAmp; 392 - } 393 - return n / maxAmp; 394 - } 395 - 396 - move(x: number, y: number, z: number | undefined = undefined, w: number | undefined = undefined) { 397 - if (this.shift == undefined) this.shift = [0, 0, 0, 0]; 398 - 399 - this.shift[0] += x; 400 - this.shift[1] += y; 401 - if (z != undefined) this.shift[2] += z; 402 - if (w != undefined) this.shift[3] += w; 403 - } 404 - 405 - private getNoise( 406 - x: number, 407 - y: number, 408 - z: number | undefined = undefined, 409 - w: number | undefined = undefined 410 - ): number { 411 - // set position 412 - this.position[0] = x; 413 - this.position[1] = y; 414 - if (z !== undefined) { 415 - this.position[2] = z; 416 - } 417 - if (w !== undefined) { 418 - this.position[3] = w; 419 - } 420 - 421 - // apply shift 422 - const shift = this.shift; 423 - if (shift !== undefined) { 424 - for (let i = 0; i < this.position.length && i < shift.length; i++) { 425 - this.position[i] += shift[i]; 426 - } 427 - } 428 - 429 - // apply tiling 430 - this.tilePosition(); 431 - 432 - // apply warp 433 - this.warpPosition(this.warp, this.warpNoise); 434 - this.warpPosition(this.warp2, this.warpNoise2); 435 - 436 - let norm = this.getFBM(true); 437 - 438 - // apply power 439 - const power = this.power; 440 - if (power != 1) { 441 - // convert to [0 - 1], apply power and back to [-1, 1] 442 - norm = (Math.pow((norm + 1) * 0.5, power) - 0.5) * 2; 443 - } 444 - 445 - // apply sharpness 446 - const sharpness = this.sharpness; 447 - if (sharpness != 0) { 448 - const billow = (Math.abs(norm) - 0.5) * 2; 449 - const ridged = (0.5 - Math.abs(norm)) * 2; 450 - 451 - norm = lerp(norm, billow, Math.max(0, sharpness)); 452 - norm = lerp(norm, ridged, Math.max(0, -sharpness)); 453 - } 454 - 455 - // apply steps 456 - const steps = this.steps; 457 - if (steps != 0) { 458 - // convert from -1 to 1 to 0 to 1 459 - norm = (norm + 1) * 0.5; 460 - // apply steps 461 - norm = Math.floor(norm * steps) / steps; 462 - // convert back to -1 to 1 463 - norm = norm * 2 - 1; 464 - } 465 - 466 - return norm; 467 - } 468 - 469 - /** 470 - * value between min and max to normalized value between -1 and 1 471 - * 472 - * @param value {number} 473 - * @returns {number} 474 - */ 475 - toNormalized(value: number): number { 476 - return ((value - this.min) / (this.max - this.min)) * 2 - 1; 477 - } 478 - 479 - /** 480 - * normlized value to value between min and max 481 - * 482 - * @param value {number} 483 - * @returns {number} 484 - */ 485 - toMinMax(value: number): number { 486 - return (value + 1) * 0.5 * (this.max - this.min) + this.min; 487 - } 488 - 489 - private checkParameterInput(value: NoiseParameterInput): UberNoise | number { 490 - if (typeof value === 'object' && !(value instanceof UberNoise)) { 491 - if (value.seed === undefined) { 492 - value.seed = this.pngr(); 493 - } 494 - value = new UberNoise(value); 495 - } 496 - return value; 497 - } 498 - 499 - private getParameter(value: NoiseParameter): number { 500 - if (typeof value === 'number') { 501 - return value; 502 - } 503 - return value.get(this.position); 504 - } 505 - 506 - // getter and setter for min and max 507 - get min(): number { 508 - return this.getParameter(this._min); 509 - } 510 - set min(value: NoiseParameterInput) { 511 - this._min = this.checkParameterInput(value); 512 - } 513 - get max(): number { 514 - return this.getParameter(this._max); 515 - } 516 - set max(value: NoiseParameterInput) { 517 - this._max = this.checkParameterInput(value); 518 - } 519 - 520 - // getter and setter for scale and power 521 - get scale(): number { 522 - return this.getParameter(this._scale); 523 - } 524 - set scale(value: NoiseParameterInput) { 525 - this._scale = this.checkParameterInput(value); 526 - } 527 - get power(): number { 528 - return this.getParameter(this._power); 529 - } 530 - set power(value: NoiseParameterInput) { 531 - this._power = this.checkParameterInput(value); 532 - } 533 - 534 - // getter and setter for gain and lacunarity 535 - get gain(): number { 536 - return this.getParameter(this._gain); 537 - } 538 - set gain(value: NoiseParameterInput) { 539 - this._gain = this.checkParameterInput(value); 540 - } 541 - get lacunarity(): number { 542 - return this.getParameter(this._lacunarity); 543 - } 544 - set lacunarity(value: NoiseParameterInput) { 545 - this._lacunarity = this.checkParameterInput(value); 546 - } 547 - 548 - // getter and setter for erosion, sharpness and steps 549 - get erosion(): number { 550 - return this.getParameter(this._erosion); 551 - } 552 - set erosion(value: NoiseParameterInput) { 553 - this._erosion = this.checkParameterInput(value); 554 - } 555 - get sharpness(): number { 556 - return this.getParameter(this._sharpness); 557 - } 558 - set sharpness(value: NoiseParameterInput) { 559 - this._sharpness = this.checkParameterInput(value); 560 - } 561 - get steps(): number { 562 - return Math.round(this.getParameter(this._steps)); 563 - } 564 - set steps(value: NoiseParameterInput) { 565 - this._steps = this.checkParameterInput(value); 566 - } 567 - 568 - // getter and setter for warp, warpNoise, warp2 and warpNoise2 569 - get warp(): number { 570 - return this.getParameter(this._warp); 571 - } 572 - set warp(value: NoiseParameterInput) { 573 - this._warp = this.checkParameterInput(value); 574 - } 575 - get warpNoise(): UberNoise | undefined { 576 - return this._warpNoise; 577 - } 578 - set warpNoise(value: UberNoise | UberNoiseOptions | undefined) { 579 - if (value == undefined) { 580 - this._warpNoise = undefined; 581 - return; 582 - } 583 - 584 - const processed = this.checkParameterInput(value); 585 - if (processed instanceof UberNoise) { 586 - this._warpNoise = processed; 587 - } 588 - } 589 - get warp2(): number { 590 - return this.getParameter(this._warp2); 591 - } 592 - set warp2(value: NoiseParameterInput) { 593 - this._warp2 = this.checkParameterInput(value); 594 - } 595 - get warpNoise2(): UberNoise | undefined { 596 - return this._warpNoise2; 597 - } 598 - set warpNoise2(value: UberNoise | UberNoiseOptions | undefined) { 599 - if (value == undefined) { 600 - this._warpNoise2 = undefined; 601 - return; 602 - } 603 - 604 - const processed = this.checkParameterInput(value); 605 - if (processed instanceof UberNoise) { 606 - this._warpNoise2 = processed; 607 - } 608 - } 609 - } 610 - 611 - export default UberNoise;
+1 -1
src/lib/components/About.svelte
··· 9 9 import Resume from './Resume.svelte'; 10 10 </script> 11 11 12 - <div class="relative isolate overflow-hidden bg-black"> 12 + <div class="relative isolate overflow-hidden bg-background"> 13 13 <div class="mx-auto max-w-5xl px-6 lg:px-8"> 14 14 <div id="about" class="py-16 md:py-32 section"> 15 15 <div class="grid grid-cols-1 gap-y-16 lg:grid-cols-2 lg:grid-rows-[auto_1fr] lg:gap-y-12">
+1 -1
src/lib/components/Footer.svelte
··· 1 - <footer class="bg-black relative isolate"> 1 + <footer class="bg-background relative isolate"> 2 2 <div class="mx-auto max-w-7xl overflow-hidden px-6 py-12 pb-20 sm:pb-12 lg:px-8"> 3 3 <p 4 4 class="mt-4 flex items-center justify-center text-center text-xs leading-5 text-zinc-500 dark:text-zinc-400 gap-1"
+7 -2
src/lib/components/Learning.svelte
··· 15 15 date: '2024-07' 16 16 }, 17 17 { 18 + title: 'rapier', 19 + description: 'explored the internals of the physics engine rapier for a game engine i\'m working on.', 20 + date: '2024-06', 21 + }, 22 + { 18 23 title: 'supabase', 19 24 description: 20 25 'started using supabase for a few projects. still prefer mongodb as a database, but it has some cool features.', ··· 75 80 $: shownLearnings = showAll ? learnings : [...learnings].slice(0, 3); 76 81 </script> 77 82 78 - <div class="relative isolate overflow-hidden bg-black"> 83 + <div class="relative isolate overflow-hidden bg-background"> 79 84 <div class="mx-auto max-w-5xl px-6 lg:px-8"> 80 85 <div id="learning" class="py-16 md:py-32 section"> 81 86 <div class="max-w-2xl"> ··· 84 89 </h1> 85 90 <p class="mt-6 text-base text-zinc-600 dark:text-zinc-400"> 86 91 one of the things i love most about programming is that there's always something new to 87 - learn. i try to learn something new every month. here's a selection of some of the things i've been 92 + learn. here's a selection of some of the things i've been 88 93 learning lately: 89 94 </p> 90 95 </div>
+1 -1
src/lib/components/Loading.svelte
··· 68 68 </g> 69 69 </svg> 70 70 {/if} 71 - <div class="absolute bg-black h-screen w-full z-10 background"></div> 71 + <div class="absolute bg-background h-screen w-full z-10 background"></div> 72 72 </div> 73 73 74 74 <style>
+1 -1
src/lib/components/Posts.svelte
··· 54 54 $: shownArticles = showAll ? articles : [...articles].slice(0, 4); 55 55 </script> 56 56 57 - <div class="relative isolate overflow-hidden bg-black"> 57 + <div class="relative isolate overflow-hidden bg-background"> 58 58 <div class="mx-auto max-w-5xl px-6 lg:px-8"> 59 59 <div id="articles" class="py-16 md:py-32 section"> 60 60 <div class="max-w-2xl">
+3 -3
src/lib/components/Projects.svelte
··· 3 3 import { projects } from '$lib/projects'; 4 4 </script> 5 5 6 - <div id="projects" class="z-20 py-16 md:py-32 section bg-black relative isolate"> 6 + <div id="projects" class="z-20 py-16 md:py-32 section bg-background relative isolate"> 7 7 <div class="mx-auto max-w-5xl px-6 lg:px-8"> 8 8 <div class="max-w-2xl mb-8"> 9 9 <h1 class="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl"> ··· 16 16 </p> 17 17 </div> 18 18 19 - <div class="columns-2 sm:columns-3 lg:columns-3 gap-8 group/projects"> 19 + <div class="columns-2 sm:columns-3 lg:columns-3 gap-8 group/projects pointer-events-none"> 20 20 {#each projects as project} 21 21 <div 22 22 class="group relative break-inside-avoid-column {project.aspect ?? 23 - 'aspect-square'} group-hover/projects:opacity-30 group-focus/projects:opacity-30 hover:!opacity-100 mb-8 w-full flex-none overflow-hidden rounded-xl ring-1 ring-white/10 focus:ring-accent-500 focus:!opacity-100 sm:rounded-2xl transition-opacity duration-500" 23 + 'aspect-square'} pointer-events-auto group-hover/projects:opacity-30 group-focus/projects:opacity-30 hover:!opacity-100 mb-8 w-full flex-none overflow-hidden rounded-xl ring-1 ring-white/10 focus:ring-accent-500 focus:!opacity-100 sm:rounded-2xl transition-opacity duration-500" 24 24 > 25 25 {#if project.src.endsWith('.mp4')} 26 26 <video
+1 -1
src/lib/components/Tools.svelte
··· 64 64 }]; 65 65 </script> 66 66 67 - <div class="relative isolate overflow-hidden bg-black"> 67 + <div class="relative isolate overflow-hidden bg-background"> 68 68 <div class="mx-auto max-w-5xl px-6 lg:px-8"> 69 69 <div id="learning" class="py-16 md:py-32 section"> 70 70 <div class="max-w-2xl">
+85 -91
src/lib/3D/worlds/biome.ts
··· 1 - import { UberNoise, type UberNoiseOptions } from "./helper/uber-noise"; 2 - import { Color, Vector3 } from "three"; 1 + import { UberNoise, type NoiseOptions } from 'uber-noise'; 2 + import { Color, Vector3 } from 'three'; 3 3 4 - import { 5 - ColorGradient, 6 - type ColorGradientOptions, 7 - } from "./helper/colorgradient"; 8 - import { biomePresets } from "./presets"; 4 + import { ColorGradient, type ColorGradientOptions } from './helper/colorgradient'; 5 + import { biomePresets } from './presets'; 9 6 10 7 export type BiomeOptions = { 11 - name?: string; 8 + name?: string; 12 9 13 - preset?: string; 10 + preset?: string; 14 11 15 - noise?: UberNoiseOptions; 16 - colors?: ColorGradientOptions; 12 + noise?: NoiseOptions; 13 + colors?: ColorGradientOptions; 17 14 18 - seaNoise?: UberNoiseOptions; 19 - seaColors?: ColorGradientOptions; 15 + seaNoise?: NoiseOptions; 16 + seaColors?: ColorGradientOptions; 20 17 21 - tintColor?: number; 18 + tintColor?: number; 22 19 23 - vegetation?: { 24 - items: { 25 - name: string; 26 - density: number; 20 + vegetation?: { 21 + items: { 22 + name: string; 23 + density: number; 27 24 28 - minimumHeight?: number; 29 - maximumHeight?: number; 25 + minimumHeight?: number; 26 + maximumHeight?: number; 30 27 31 - minimumDistance?: number; 32 - maximumDistance?: number; 33 - colors?: Record<string, { array?: number[] }>; 34 - }[]; 35 - }; 28 + minimumDistance?: number; 29 + maximumDistance?: number; 30 + colors?: Record<string, { array?: number[] }>; 31 + }[]; 32 + }; 36 33 }; 37 34 38 35 export class Biome { 39 - noise: UberNoise | undefined; 40 - colors: ColorGradient | undefined; 36 + noise: UberNoise | undefined; 37 + colors: ColorGradient | undefined; 41 38 42 - seaNoise: UberNoise | undefined; 43 - seaColors: ColorGradient | undefined; 39 + seaNoise: UberNoise | undefined; 40 + seaColors: ColorGradient | undefined; 44 41 45 - options: BiomeOptions; 42 + options: BiomeOptions; 46 43 47 - constructor(opts: BiomeOptions = {}) { 48 - if (opts.preset) { 49 - const preset = biomePresets[opts.preset]; 50 - if (preset) { 51 - opts = { 52 - ...preset, 53 - ...opts, 54 - }; 55 - } 56 - } 44 + constructor(opts: BiomeOptions = {}) { 45 + if (opts.preset) { 46 + const preset = biomePresets[opts.preset]; 47 + if (preset) { 48 + opts = { 49 + ...preset, 50 + ...opts 51 + }; 52 + } 53 + } 57 54 58 - this.options = opts; 55 + this.options = opts; 59 56 60 - if (opts.noise) this.noise = new UberNoise(opts.noise); 57 + if (opts.noise) this.noise = new UberNoise(opts.noise); 61 58 62 - if (opts.colors) { 63 - this.colors = new ColorGradient(opts.colors); 64 - } 59 + if (opts.colors) { 60 + this.colors = new ColorGradient(opts.colors); 61 + } 65 62 66 - if (opts.seaNoise) this.seaNoise = new UberNoise(opts.seaNoise); 63 + if (opts.seaNoise) this.seaNoise = new UberNoise(opts.seaNoise); 67 64 68 - if (opts.seaColors) { 69 - this.seaColors = new ColorGradient(opts.seaColors); 70 - } 71 - } 65 + if (opts.seaColors) { 66 + this.seaColors = new ColorGradient(opts.seaColors); 67 + } 68 + } 72 69 73 - get min(): number { 74 - return this.noise?.min ?? -1; 75 - } 70 + get min(): number { 71 + return this.noise?.min ?? -1; 72 + } 76 73 77 - get max(): number { 78 - return this.noise?.max ?? 1; 79 - } 74 + get max(): number { 75 + return this.noise?.max ?? 1; 76 + } 80 77 81 - getHeight(pos: Vector3): number { 82 - if (this.noise) return this.noise.get(pos); 78 + getHeight(pos: Vector3): number { 79 + if (this.noise) return this.noise.get(pos); 83 80 84 - return 0; 85 - } 81 + return 0; 82 + } 86 83 87 - getColor( 88 - pos: Vector3, 89 - height: number | undefined = undefined, 90 - steepness: number = 0, 91 - ): Color | undefined { 92 - if (this.noise && height === undefined) { 93 - height = this.noise.normalized(pos); 94 - } 84 + getColor( 85 + pos: Vector3, 86 + height: number | undefined = undefined, 87 + steepness: number = 0 88 + ): Color | undefined { 89 + if (this.noise && height === undefined) { 90 + height = this.noise.normalized(pos); 91 + } 95 92 96 - if (this.colors) { 97 - const color = this.colors.get(height); 98 - if (this.options.tintColor) { 99 - return color.lerp( 100 - new Color(this.options.tintColor), 101 - steepness / Math.PI, 102 - ); 103 - } 104 - return color; 105 - } 93 + if (this.colors) { 94 + const color = this.colors.get(height); 95 + if (this.options.tintColor) { 96 + return color.lerp(new Color(this.options.tintColor), steepness / Math.PI); 97 + } 98 + return color; 99 + } 106 100 107 - return undefined; 108 - } 101 + return undefined; 102 + } 109 103 110 - getSeaHeight(pos: Vector3): number { 111 - if (this.seaNoise) return this.seaNoise.get(pos); 104 + getSeaHeight(pos: Vector3): number { 105 + if (this.seaNoise) return this.seaNoise.get(pos); 112 106 113 - return 0; 114 - } 107 + return 0; 108 + } 115 109 116 - getSeaColor(pos: Vector3, ground: number | undefined): Color | undefined { 117 - ground ??= this.noise?.normalized(pos) ?? 0; 110 + getSeaColor(pos: Vector3, ground: number | undefined): Color | undefined { 111 + ground ??= this.noise?.normalized(pos) ?? 0; 118 112 119 - if (this.seaColors) { 120 - return this.seaColors.get(ground); 121 - } 113 + if (this.seaColors) { 114 + return this.seaColors.get(ground); 115 + } 122 116 123 - return undefined; 124 - } 117 + return undefined; 118 + } 125 119 }
+21 -25
src/lib/3D/worlds/planet.ts
··· 150 150 151 151 if (!positions) continue; 152 152 153 - let item = this.biomeOptions.vegetation?.items[i]; 153 + const item = this.biomeOptions.vegetation?.items[i]; 154 154 155 - for (const position of positions) { 156 - const model = models[Math.floor(Math.random() * models.length)].clone(); 157 - model.position.set(0, 1, 0); 158 - this.updatePosition( 159 - model, 160 - new Vector3(position.x, position.y, position.z), 161 - ); 162 - model.scale.setScalar(0.04); 155 + for (const position of positions) { 156 + const model = models[Math.floor(Math.random() * models.length)].clone(); 157 + model.position.set(0, 1, 0); 158 + this.updatePosition(model, new Vector3(position.x, position.y, position.z)); 159 + model.scale.setScalar(0.04); 163 160 164 - model.traverse((child) => { 165 - if (child instanceof Mesh) { 166 - let color = item?.colors?.[child.material.name]; 167 - if (color && color.array) { 168 - let randomColor = 169 - color.array[Math.floor(Math.random() * color.array.length)]; 170 - child.material.color.setHex(randomColor); 171 - } 161 + model.traverse((child: Object3D) => { 162 + if (child instanceof Mesh) { 163 + const color = item?.colors?.[child.material.name]; 164 + if (color?.array) { 165 + const randomColor = color.array[Math.floor(Math.random() * color.array.length)]; 166 + child.material.color.setHex(randomColor); 167 + } 172 168 173 - if (child.material.name === "Snow") { 174 - child.material.roughness = 0.2; 175 - child.material.color.setHex(0xffffff); 176 - } 177 - } 178 - }); 179 - planet.add(model); 180 - } 169 + if (child.material.name === 'Snow') { 170 + child.material.roughness = 0.2; 171 + child.material.color.setHex(0xffffff); 172 + } 173 + } 174 + }); 175 + planet.add(model); 176 + } 181 177 } 182 178 183 179 return planetPromise;
+1 -1
src/lib/3D/worlds/worker.ts
··· 2 2 3 3 import { Biome, type BiomeOptions } from "./biome"; 4 4 import { type PlanetOptions } from "./planet"; 5 - import UberNoise from "./helper/uber-noise"; 5 + import UberNoise from 'uber-noise'; 6 6 7 7 onmessage = function (e) { 8 8 const { type, data, requestId } = e.data;
+1 -1
src/lib/components/project/ProjectCarousel.svelte
··· 55 55 <img 56 56 transition:fade 57 57 src={project.images[selectedImage]} 58 - alt="Angled front view with bag zipped and handles upright." 58 + alt="" 59 59 class="h-full w-full object-cover object-center sm:rounded-xl" 60 60 /> 61 61
+2 -2
src/lib/components/project/ProjectFull.svelte
··· 20 20 title="fluid text effect demo" 21 21 class="w-full h-full" 22 22 /> 23 - {:else if project.demo == 'mandala'} 23 + {:else if project.demo == 'mandala'} 24 24 <iframe 25 25 src="https://flo-bit.github.io/mandala/" 26 26 title="fluid text effect demo" ··· 29 29 {:else if project.images} 30 30 <img 31 31 src={project.images[0]} 32 - alt="Two each of neutral, white, and black shirts laying flat." 32 + alt="" 33 33 class="h-full w-full object-cover object-center" 34 34 /> 35 35 {/if}
+21 -21
src/lib/components/project/ProjectGrid.svelte
··· 14 14 <!-- Image gallery --> 15 15 {#if project.images} 16 16 <div 17 - class="mx-auto mt-6 max-w-2xl sm:px-6 lg:grid lg:max-w-5xl lg:grid-cols-3 lg:gap-x-8 lg:px-8" 17 + class="mx-auto mt-6 max-w-2xl sm:px-6 md:grid md:max-w-5xl md:grid-cols-3 md:gap-x-8 md:px-8" 18 18 > 19 - <div class="aspect-h-4 aspect-w-3 hidden overflow-hidden rounded-xl lg:block"> 19 + <div class="aspect-h-4 aspect-w-3 hidden overflow-hidden rounded-xl md:block border border-white/10"> 20 20 <img 21 21 src={project.images[0]} 22 - alt="Two each of neutral, white, and black shirts laying flat." 22 + alt="" 23 23 class="h-full w-full object-cover object-center" 24 24 /> 25 25 </div> 26 - <div class="hidden lg:grid lg:grid-cols-1 lg:gap-y-8"> 27 - <div class="aspect-h-2 aspect-w-3 overflow-hidden rounded-xl"> 26 + <div class="hidden md:grid md:grid-cols-1 md:gap-y-8"> 27 + <div class="aspect-h-2 aspect-w-3 overflow-hidden rounded-xl border border-white/10"> 28 28 <img 29 29 src={project.images[1]} 30 - alt="Model wearing plain black basic tee." 30 + alt="" 31 31 class="h-full w-full object-cover object-center" 32 32 /> 33 33 </div> 34 - <div class="aspect-h-2 aspect-w-3 overflow-hidden rounded-xl"> 34 + <div class="aspect-h-2 aspect-w-3 overflow-hidden rounded-xl border border-white/10"> 35 35 <img 36 36 src={project.images[2]} 37 - alt="Model wearing plain neutral basic tee." 37 + alt="" 38 38 class="h-full w-full object-cover object-center" 39 39 /> 40 40 </div> 41 41 </div> 42 42 <div 43 - class="aspect-h-5 aspect-w-4 lg:aspect-h-4 lg:aspect-w-3 sm:overflow-hidden sm:rounded-xl" 43 + class="aspect-h-5 aspect-w-4 md:aspect-h-4 md:aspect-w-3 sm:overflow-hidden sm:rounded-xl border border-white/10" 44 44 > 45 45 <img 46 46 src={project.images[3]} 47 - alt="Model wearing plain white basic tee." 47 + alt="" 48 48 class="h-full w-full object-cover object-center" 49 49 /> 50 50 </div> ··· 53 53 54 54 <!-- Product info --> 55 55 <div 56 - class="mx-auto max-w-2xl px-4 pb-0 pt-10 sm:px-6 lg:grid lg:max-w-5xl lg:grid-cols-3 lg:grid-rows-[auto,auto,1fr] lg:gap-x-8 lg:px-8 lg:pb-24 lg:pt-16" 56 + class="mx-auto max-w-2xl px-4 pb-0 pt-10 sm:px-6 md:grid md:max-w-5xl md:grid-cols-3 md:grid-rows-[auto,auto,1fr] md:gap-x-8 md:px-8 md:pb-24 md:pt-16" 57 57 > 58 - <div class="lg:col-span-2 lg:border-r lg:border-neutral-200 dark:border-neutral-800 lg:pr-8"> 58 + <div class="md:col-span-2 md:border-r md:border-neutral-200 dark:border-neutral-800 md:pr-8"> 59 59 <h1 class="text-2xl font-bold tracking-tight text-neutral-900 dark:text-white sm:text-3xl"> 60 60 {project.name} 61 61 </h1> 62 62 </div> 63 63 64 64 <!-- Options --> 65 - <div class="mt-4 lg:row-span-3 lg:mt-0"> 65 + <div class="mt-4 md:row-span-3 md:mt-0"> 66 66 <h2 class="sr-only">project information</h2> 67 67 68 68 <div class="mt-0"> ··· 84 84 </div> 85 85 86 86 <div 87 - class="py-10 lg:col-span-2 lg:col-start-1 lg:border-r lg:border-neutral-200 dark:border-neutral-800 lg:pb-16 lg:pr-8 lg:pt-6" 87 + class="py-10 md:col-span-2 md:col-start-1 md:border-r md:border-neutral-200 dark:border-neutral-800 md:pb-16 md:pr-8 md:pt-6" 88 88 > 89 89 <!-- Description and details --> 90 90 <div> ··· 128 128 </div> 129 129 130 130 {#if project.images} 131 - <div class="mx-auto mt-6 max-w-2xl sm:px-6 lg:hidden pb-24"> 132 - <div class="aspect-h-4 aspect-w-3 overflow-hidden sm:rounded-xl"> 131 + <div class="mx-auto mt-6 max-w-2xl sm:px-6 md:hidden pb-24"> 132 + <div class="aspect-h-4 aspect-w-3 overflow-hidden sm:rounded-xl border border-white/10"> 133 133 <img 134 134 src={project.images[0]} 135 - alt="Two each of neutral, white, and black shirts laying flat." 135 + alt="" 136 136 class="h-full w-full object-cover object-center" 137 137 /> 138 138 </div> 139 - <div class="aspect-h-2 aspect-w-3 overflow-hidden sm:rounded-xl mt-4"> 139 + <div class="aspect-h-2 aspect-w-3 overflow-hidden sm:rounded-xl mt-4 border border-white/10"> 140 140 <img 141 141 src={project.images[1]} 142 - alt="Model wearing plain black basic tee." 142 + alt="" 143 143 class="h-full w-full object-cover object-center" 144 144 /> 145 145 </div> 146 - <div class="aspect-h-2 aspect-w-3 overflow-hidden sm:rounded-xl mt-4"> 146 + <div class="aspect-h-2 aspect-w-3 overflow-hidden sm:rounded-xl mt-4 border border-white/10"> 147 147 <img 148 148 src={project.images[2]} 149 - alt="Model wearing plain neutral basic tee." 149 + alt="" 150 150 class="h-full w-full object-cover object-center" 151 151 /> 152 152 </div>
+15 -15
src/lib/components/project/ProjectTiered.svelte
··· 1 1 <script lang="ts"> 2 + import { Depth3D } from '$lib/3D/Depth3D'; 2 3 import type { Project } from '$lib/projects'; 3 4 import Badge from '../badge/badge.svelte'; 5 + import Depth3ddemo from './demos/depth3ddemo.svelte'; 4 6 import Swiper from './demos/swiper.svelte'; 5 7 import ProjectBreadcrumb from './ProjectBreadcrumb.svelte'; 6 8 import ProjectButtons from './ProjectButtons.svelte'; ··· 26 28 <h2 class="sr-only">Images</h2> 27 29 28 30 <div 29 - class="grid grid-cols-1 overflow-hidden rounded-xl lg:grid-cols-2 {project.images && project.images 30 - .length > 1 31 + class="grid grid-cols-1 overflow-hidden rounded-xl lg:grid-cols-2 {project.images && 32 + project.images.length > 1 31 33 ? 'lg:grid-rows-3' 32 34 : ''} lg:gap-8" 33 35 > ··· 43 45 class="w-full h-full" 44 46 /> 45 47 </div> 48 + {:else if project.demo == 'depth3d'} 49 + <div 50 + class="mx-auto aspect-[2/3] h-[75vh] rounded-xl overflow-hidden lg:col-span-2 lg:row-span-2" 51 + > 52 + <Depth3ddemo /> 53 + </div> 46 54 {:else if project.images} 47 55 <img 48 56 src={project.images[0]} 49 - alt="Back of women&#039;s Basic Tee in black." 57 + alt="" 50 58 class="rounded-xl lg:col-span-2 lg:row-span-2" 51 59 /> 52 60 {/if} 53 61 {#if project.images && project.images.length > 1} 54 - <img 55 - src={project.images[1]} 56 - alt="Side profile of women&#039;s Basic Tee in black." 57 - class="hidden rounded-xl lg:block" 58 - /> 62 + <img src={project.images[1]} alt="" class="hidden rounded-xl lg:block" /> 59 63 {/if} 60 64 {#if project.images && project.images.length > 2} 61 - <img 62 - src={project.images[2]} 63 - alt="Front of women&#039;s Basic Tee in black." 64 - class="hidden rounded-xl lg:block" 65 - /> 65 + <img src={project.images[2]} alt="" class="hidden rounded-xl lg:block" /> 66 66 {/if} 67 67 </div> 68 68 </div> ··· 83 83 </div> 84 84 85 85 <div class="mt-8 border-t border-neutral-200 dark:border-neutral-800 pt-8"> 86 - <h2 class="text-sm font-medium text-neutral-900 dark:text-neutral-100">highlights</h2> 87 - 88 86 {#if project.highlights} 87 + <h2 class="text-sm font-medium text-neutral-900 dark:text-neutral-100">highlights</h2> 88 + 89 89 <div class="prose prose-sm mt-4 text-neutral-500 dark:text-neutral-400"> 90 90 <ul role="list"> 91 91 {#each project.highlights as highlight}
+1 -1
src/lib/components/project/ProjectV3.svelte
··· 37 37 <div class="aspect-h-1 aspect-w-1 overflow-hidden rounded-lg"> 38 38 <img 39 39 src={project.images[0]} 40 - alt="Model wearing light green backpack with black canvas straps and front zipper pouch." 40 + alt="" 41 41 class="h-full w-full object-cover object-center" 42 42 /> 43 43 </div>
src/lib/images/projects-new/old-code-demo.mp4

This is a binary file and will not be displayed.

src/lib/images/projects-new/old-code-image.png

This is a binary file and will not be displayed.

-602
src/lib/3D/worlds/helper/uber-noise.ts
··· 1 - import { 2 - type NoiseFunction2D, 3 - type NoiseFunction3D, 4 - type NoiseFunction4D, 5 - createNoise2D, 6 - createNoise3D, 7 - createNoise4D 8 - } from 'simplex-noise'; 9 - import alea from 'alea'; 10 - 11 - /** 12 - * 13 - * @property {number} seed - seed for the noise, if not provided, Math.random() will be used, 14 - * currently same results can only be guaranteed for newly created noise objects with same seed 15 - * (as opposed to an old noise object where you changed the seed) 16 - * 17 - * @property {number} min - minimun value of noise 18 - * @property {number} max - maximum value of noise 19 - * 20 - * @property {number} scale - scale of the noise 21 - * @property {number} power - power of the noise (1 = linear, 2 = quadratic, etc) 22 - * @property {Vector} shift - move noise in 2D, 3D or 4D space 23 - * 24 - * @property {number} octaves - number of layers for fbm noise 25 - * @property {number} gain - how much to multiply amplitude per layer 26 - * @property {number} lacunarity - how much to multiply scale per layer 27 - * @property {array} amps - array of amplitudes for each layer 28 - * @property {number} erosion - how much previous layers influence amplitude of later layers 29 - * @property {number} sharpness - billowed or rigded noise (0 = normal, 1 = billowed, -1 = ridged) 30 - * @property {number} steps - will turn noise into steps (integer, number of steps) 31 - * 32 - * @property {number} warp - how much to warp the noise 33 - * @property {number} warpNoise - noise to warp the noise with 34 - * @property {number} warp2 - second warp, can only be used if warp is used too 35 - * @property {number} warpNoise2 - second warp noise 36 - * 37 - * @property {boolean} invert - invert the noise 38 - * @property {boolean} abs - absolute value of the noise 39 - * @property {boolean} clamp - clamp the noise between min and max 40 - * @property {boolean} tileX - tile the noise in x direction 41 - * @property {boolean} tileY - tile the noise in y direction 42 - * @property {boolean} tile - tile the noise in all directions (will override tileX and tileY) 43 - * 44 - * @constructor 45 - */ 46 - 47 - function lerp(a: number, b: number, t: number): number { 48 - return (b - a) * t + a; 49 - } 50 - 51 - function setLength(vector: number[], length: number): number[] { 52 - const oldLength = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0)); 53 - if (oldLength === 0) return vector; 54 - const scale = length / oldLength; 55 - return vector.map((v) => v * scale); 56 - } 57 - 58 - // angle between two 3D vectors 59 - function angleTo(a: number[], b: number[]): number { 60 - const dot = a.reduce((sum, v, i) => sum + v * b[i], 0); 61 - const length = Math.sqrt( 62 - a.reduce((sum, v) => sum + v * v, 0) * b.reduce((sum, v) => sum + v * v, 0) 63 - ); 64 - return Math.acos(dot / length); 65 - } 66 - 67 - type ErosionUpFunction = (derivative: number[]) => number[]; 68 - 69 - export type VectorObject = { 70 - x: number; 71 - y: number; 72 - z?: number; 73 - w?: number; 74 - }; 75 - 76 - export type NoiseParameterInput = number | UberNoise | UberNoiseOptions; 77 - export type NoiseParameter = number | UberNoise; 78 - 79 - export type UberNoiseOptions = { 80 - seed?: string | number; 81 - 82 - min?: NoiseParameterInput; 83 - max?: NoiseParameterInput; 84 - 85 - scale?: NoiseParameterInput; 86 - power?: NoiseParameterInput; 87 - 88 - shift?: number[]; 89 - 90 - octaves?: number; 91 - gain?: NoiseParameterInput; 92 - lacunarity?: NoiseParameterInput; 93 - 94 - amps?: number[]; 95 - 96 - layers?: UberNoiseOptions[]; 97 - 98 - erosion?: NoiseParameterInput; 99 - erosionUp?: ErosionUpFunction; 100 - 101 - sharpness?: NoiseParameterInput; 102 - steps?: NoiseParameterInput; 103 - warp?: NoiseParameterInput; 104 - warpNoise?: UberNoise; 105 - warp2?: NoiseParameterInput; 106 - warpNoise2?: UberNoise; 107 - 108 - invert?: boolean; 109 - abs?: boolean; 110 - clamp?: boolean; 111 - tileX?: boolean; 112 - tileY?: boolean; 113 - tile?: boolean; 114 - }; 115 - 116 - export class UberNoise { 117 - private noise2D: NoiseFunction2D | undefined; 118 - private noise3D: NoiseFunction3D | undefined; 119 - private noise4D: NoiseFunction4D | undefined; 120 - private seed: string | number; 121 - 122 - private _min: NoiseParameter = -1; 123 - private _max: NoiseParameter = 1; 124 - 125 - private _scale: NoiseParameter = 1; 126 - private _power: NoiseParameter = 1; 127 - 128 - private shift: number[] = []; 129 - 130 - private octaves = 0; 131 - private _gain: NoiseParameter = 0.5; 132 - private _lacunarity: NoiseParameter = 2; 133 - 134 - private amps: number[] = []; 135 - 136 - private _erosion: NoiseParameter = 0; 137 - private _sharpness: NoiseParameter = 0; 138 - private _steps: NoiseParameter = 0; 139 - 140 - private _warp: NoiseParameter = 0; 141 - private _warpNoise: UberNoise | undefined; 142 - private _warp2: NoiseParameter = 0; 143 - private _warpNoise2: UberNoise | undefined; 144 - 145 - private tileX = false; 146 - private tileY = false; 147 - 148 - private erosionDelta = 0; 149 - private erosionDerivative?: number[] = undefined; 150 - private erosionUp?: ErosionUpFunction; 151 - 152 - private static erosionDefaultUp = [0, 0, 1]; 153 - 154 - private position = [0, 0]; 155 - 156 - private pngr; 157 - 158 - private layers: UberNoise[] = []; 159 - 160 - constructor(options: UberNoiseOptions = {}) { 161 - this.seed = options.seed ?? Math.random(); 162 - this.pngr = alea(this.seed); 163 - 164 - this.min = options.min ?? -1; 165 - this.max = options.max ?? 1; 166 - 167 - this.scale = options.scale ?? 1; 168 - this.power = options.power ?? 1; 169 - 170 - this.octaves = options.octaves ?? options.layers?.length ?? 0; 171 - this.gain = options.gain ?? 0.5; 172 - this.lacunarity = options.lacunarity ?? 2; 173 - 174 - this.erosion = options.erosion ?? 0; 175 - this.sharpness = options.sharpness ?? 0; 176 - this.steps = options.steps ?? 0; 177 - 178 - this.warp = options.warp ?? 0; 179 - this.warpNoise = options.warpNoise; 180 - this.warp2 = options.warp2 ?? 0; 181 - this.warpNoise2 = options.warpNoise2; 182 - 183 - this.processLayers(options); 184 - 185 - this.tileX = options.tileX ?? options.tile ?? false; 186 - this.tileY = options.tileY ?? options.tile ?? false; 187 - } 188 - 189 - processLayers(options: UberNoiseOptions) { 190 - this.amps = options.amps ?? []; 191 - 192 - for (let i = 0; i < this.octaves; i++) { 193 - const layerOptions = options.layers?.[i] ?? {}; 194 - if (layerOptions instanceof UberNoise) { 195 - this.layers[i] = layerOptions; 196 - } else { 197 - if (layerOptions.seed === undefined) layerOptions.seed = this.pngr(); 198 - this.layers[i] = new UberNoise(layerOptions); 199 - } 200 - } 201 - if (this.layers.length == 0) { 202 - this.noise2D = createNoise2D(this.pngr); 203 - this.noise3D = createNoise3D(this.pngr); 204 - this.noise4D = createNoise4D(this.pngr); 205 - } 206 - } 207 - 208 - // same as normalized but returns between min and max 209 - get( 210 - x: number | VectorObject | Array<number>, 211 - y: number | undefined = undefined, 212 - z: number | undefined = undefined, 213 - w: number | undefined = undefined 214 - ): number { 215 - const norm = this.normalized(x, y, z, w); 216 - return this.toMinMax(norm); 217 - } 218 - 219 - // same as get but returns between -1 and 1 220 - normalized( 221 - x: number | VectorObject | Array<number>, 222 - y: number | undefined = undefined, 223 - z: number | undefined = undefined, 224 - w: number | undefined = undefined 225 - ): number { 226 - // if x is an array, treat it as a vector 227 - if (Array.isArray(x)) { 228 - w = x[3]; 229 - z = x[2]; 230 - y = x[1]; 231 - x = x[0]; 232 - } else if (typeof x === 'object') { 233 - w = x.w; 234 - z = x.z; 235 - y = x.y; 236 - x = x.x; 237 - } 238 - // if y is undefined, treat it as 2D noise with y = 0 239 - y = y ?? 0; 240 - 241 - return this.getNoise(x, y, z, w); 242 - } 243 - 244 - private warpPosition(warp: number, warpNoise: UberNoise | undefined) { 245 - if (warp == undefined || warp == 0) return; 246 - 247 - if (warpNoise != undefined) warpNoise.position = this.position; 248 - 249 - let x = this.position[0], 250 - y = this.position[1], 251 - z = this.position[2], 252 - w = this.position[3]; 253 - 254 - const noise = warpNoise ?? this; 255 - const scl = this.scale; 256 - 257 - this.position = [x + 54.47 * scl, y + 34.98 * scl]; 258 - if (z != undefined) this.position.push(z + 21.63 * scl); 259 - if (w != undefined) this.position.push(w + 67.1 * scl); 260 - 261 - x += noise.getFBM() * warp; 262 - y += noise.getFBM() * warp; 263 - if (z != undefined) z += noise.getFBM() * warp; 264 - if (w != undefined) w += noise.getFBM() * warp; 265 - 266 - this.position = [x, y, z, w]; 267 - } 268 - 269 - private tilePosition() { 270 - if (!this.tileX && !this.tileY) return; 271 - const x = this.position[0]; 272 - const y = this.position[1]; 273 - let newX = 0, 274 - newY = 0, 275 - newZ = 0, 276 - newW = 0; 277 - if (this.tileX) { 278 - newX = Math.sin(x * Math.PI * 2); 279 - newY = Math.cos(x * Math.PI * 2); 280 - } 281 - if (this.tileY) { 282 - newZ = Math.sin(y * Math.PI * 2); 283 - newW = Math.cos(y * Math.PI * 2); 284 - } 285 - if (this.tileX && !this.tileY) { 286 - this.position = [newX, newY + y]; 287 - } else if (this.tileY && !this.tileX) { 288 - this.position = [newZ + x, newW]; 289 - } else if (this.tileX && this.tileY) { 290 - this.position = [newX, newY, newZ, newW]; 291 - } 292 - } 293 - 294 - private getDerivative( 295 - x: number, 296 - y: number, 297 - z: number, 298 - n: number | undefined = undefined 299 - ): number[] | undefined { 300 - // left side or central difference 301 - // very expensive (four/six noise calls), should be changed to analytical derivatives 302 - // see https://iquilezles.org/www/articles/morenoise/morenoise.htm 303 - 304 - if (z == undefined) return undefined; 305 - 306 - const mov = this.erosionDelta; 307 - 308 - const dx = (n ?? this.get(x - mov, y, z)) - this.get(x + mov, y, z); 309 - const dy = (n ?? this.get(x, y - mov, z)) - this.get(x, y + mov, z); 310 - const dz = (n ?? this.get(x, y, z - mov)) - this.get(x, y, z + mov); 311 - 312 - this.erosionDerivative = [dx, dy, dz]; 313 - return this.erosionDerivative; 314 - } 315 - 316 - private getBaseLayer(scale: number, x: number, y: number, z?: number, w?: number): number { 317 - if (z != undefined && w != undefined && this.noise4D != undefined) { 318 - return this.noise4D(x * scale, y * scale, z * scale, w * scale); 319 - } 320 - if (z != undefined && this.noise3D != undefined) { 321 - return this.noise3D(x * scale, y * scale, z * scale); 322 - } 323 - if (this.noise2D != undefined) { 324 - return this.noise2D(x * scale, y * scale); 325 - } 326 - return 0; 327 - } 328 - 329 - private getFBM(useErosion = false): number { 330 - const x = this.position[0], 331 - y = this.position[1], 332 - z = this.position[2], 333 - w = this.position[3]; 334 - 335 - const scale = this.scale; 336 - 337 - if (this.layers.length === 0) { 338 - return this.getBaseLayer(scale, x, y, z, w); 339 - } 340 - 341 - let maxAmp = 1; 342 - let amp = 1, 343 - freq = scale; 344 - 345 - const lacunarity = this.lacunarity; 346 - const gain = this.gain; 347 - 348 - // for now we'll always ignore erosion 349 - // remove the following line later to enable erosion 350 - useErosion = false; 351 - const erosion = useErosion ? this.erosion : 0; 352 - const up = 353 - this.erosionUp != undefined ? this.erosionUp(this.position) : UberNoise.erosionDefaultUp; 354 - const sum = [0, 0, 0]; 355 - 356 - let n = 0; 357 - 358 - for (let i = 0; i < this.octaves; i++) { 359 - const layer = this.layers[i]; 360 - 361 - const layerAmp = this.amps[i] ?? 1; 362 - 363 - const value = 364 - layer.get( 365 - x * freq, 366 - y * freq, 367 - z != undefined ? z * freq : undefined, 368 - w != undefined ? w * freq : undefined 369 - ) * 370 - amp * 371 - layerAmp; 372 - if (erosion > 0) { 373 - const derivative = layer.getDerivative(x * freq, y * freq, z * freq); 374 - 375 - if (derivative != undefined) { 376 - setLength(derivative, amp * layerAmp); 377 - sum[0] += derivative[0]; 378 - sum[1] += derivative[1]; 379 - sum[2] += derivative[2]; 380 - 381 - const mult = Math.abs(1 - angleTo(up, sum) / Math.PI); 382 - 383 - n += value * (mult * erosion + 1 - erosion); 384 - } 385 - } else { 386 - n += value; 387 - } 388 - 389 - amp *= gain; 390 - freq *= lacunarity; 391 - maxAmp += amp * layerAmp; 392 - } 393 - return n / maxAmp; 394 - } 395 - 396 - private getNoise( 397 - x: number, 398 - y: number, 399 - z: number | undefined = undefined, 400 - w: number | undefined = undefined 401 - ): number { 402 - // set position 403 - this.position[0] = x; 404 - this.position[1] = y; 405 - if (z !== undefined) { 406 - this.position[2] = z; 407 - } 408 - if (w !== undefined) { 409 - this.position[3] = w; 410 - } 411 - 412 - // apply shift 413 - const shift = this.shift; 414 - if (shift !== undefined) { 415 - for (let i = 0; i < this.position.length && i < shift.length; i++) { 416 - this.position[i] += shift[i]; 417 - } 418 - } 419 - 420 - // apply tiling 421 - this.tilePosition(); 422 - 423 - // apply warp 424 - this.warpPosition(this.warp, this.warpNoise); 425 - this.warpPosition(this.warp2, this.warpNoise2); 426 - 427 - let norm = this.getFBM(true); 428 - 429 - // apply power 430 - const power = this.power; 431 - if (power != 1) { 432 - // convert to [0 - 1], apply power and back to [-1, 1] 433 - norm = (Math.pow((norm + 1) * 0.5, power) - 0.5) * 2; 434 - } 435 - 436 - // apply sharpness 437 - const sharpness = this.sharpness; 438 - if (sharpness != 0) { 439 - const billow = (Math.abs(norm) - 0.5) * 2; 440 - const ridged = (0.5 - Math.abs(norm)) * 2; 441 - 442 - norm = lerp(norm, billow, Math.max(0, sharpness)); 443 - norm = lerp(norm, ridged, Math.max(0, -sharpness)); 444 - } 445 - 446 - // apply steps 447 - const steps = this.steps; 448 - if (steps != 0) { 449 - // convert from -1 to 1 to 0 to 1 450 - norm = (norm + 1) * 0.5; 451 - // apply steps 452 - norm = Math.floor(norm * steps) / steps; 453 - // convert back to -1 to 1 454 - norm = norm * 2 - 1; 455 - } 456 - 457 - return norm; 458 - } 459 - 460 - /** 461 - * value between min and max to normalized value between -1 and 1 462 - * 463 - * @param value {number} 464 - * @returns {number} 465 - */ 466 - toNormalized(value: number): number { 467 - return ((value - this.min) / (this.max - this.min)) * 2 - 1; 468 - } 469 - 470 - /** 471 - * normlized value to value between min and max 472 - * 473 - * @param value {number} 474 - * @returns {number} 475 - */ 476 - toMinMax(value: number): number { 477 - return (value + 1) * 0.5 * (this.max - this.min) + this.min; 478 - } 479 - 480 - private checkParameterInput(value: NoiseParameterInput): UberNoise | number { 481 - if (typeof value === 'object' && !(value instanceof UberNoise)) { 482 - if (value.seed === undefined) { 483 - value.seed = this.pngr(); 484 - } 485 - value = new UberNoise(value); 486 - } 487 - return value; 488 - } 489 - 490 - private getParameter(value: NoiseParameter): number { 491 - if (typeof value === 'number') { 492 - return value; 493 - } 494 - return value.get(this.position); 495 - } 496 - 497 - // getter and setter for min and max 498 - get min(): number { 499 - return this.getParameter(this._min); 500 - } 501 - set min(value: NoiseParameterInput) { 502 - this._min = this.checkParameterInput(value); 503 - } 504 - get max(): number { 505 - return this.getParameter(this._max); 506 - } 507 - set max(value: NoiseParameterInput) { 508 - this._max = this.checkParameterInput(value); 509 - } 510 - 511 - // getter and setter for scale and power 512 - get scale(): number { 513 - return this.getParameter(this._scale); 514 - } 515 - set scale(value: NoiseParameterInput) { 516 - this._scale = this.checkParameterInput(value); 517 - } 518 - get power(): number { 519 - return this.getParameter(this._power); 520 - } 521 - set power(value: NoiseParameterInput) { 522 - this._power = this.checkParameterInput(value); 523 - } 524 - 525 - // getter and setter for gain and lacunarity 526 - get gain(): number { 527 - return this.getParameter(this._gain); 528 - } 529 - set gain(value: NoiseParameterInput) { 530 - this._gain = this.checkParameterInput(value); 531 - } 532 - get lacunarity(): number { 533 - return this.getParameter(this._lacunarity); 534 - } 535 - set lacunarity(value: NoiseParameterInput) { 536 - this._lacunarity = this.checkParameterInput(value); 537 - } 538 - 539 - // getter and setter for erosion, sharpness and steps 540 - get erosion(): number { 541 - return this.getParameter(this._erosion); 542 - } 543 - set erosion(value: NoiseParameterInput) { 544 - this._erosion = this.checkParameterInput(value); 545 - } 546 - get sharpness(): number { 547 - return this.getParameter(this._sharpness); 548 - } 549 - set sharpness(value: NoiseParameterInput) { 550 - this._sharpness = this.checkParameterInput(value); 551 - } 552 - get steps(): number { 553 - return Math.round(this.getParameter(this._steps)); 554 - } 555 - set steps(value: NoiseParameterInput) { 556 - this._steps = this.checkParameterInput(value); 557 - } 558 - 559 - // getter and setter for warp, warpNoise, warp2 and warpNoise2 560 - get warp(): number { 561 - return this.getParameter(this._warp); 562 - } 563 - set warp(value: NoiseParameterInput) { 564 - this._warp = this.checkParameterInput(value); 565 - } 566 - get warpNoise(): UberNoise | undefined { 567 - return this._warpNoise; 568 - } 569 - set warpNoise(value: UberNoise | UberNoiseOptions | undefined) { 570 - if (value == undefined) { 571 - this._warpNoise = undefined; 572 - return; 573 - } 574 - 575 - const processed = this.checkParameterInput(value); 576 - if (processed instanceof UberNoise) { 577 - this._warpNoise = processed; 578 - } 579 - } 580 - get warp2(): number { 581 - return this.getParameter(this._warp2); 582 - } 583 - set warp2(value: NoiseParameterInput) { 584 - this._warp2 = this.checkParameterInput(value); 585 - } 586 - get warpNoise2(): UberNoise | undefined { 587 - return this._warpNoise2; 588 - } 589 - set warpNoise2(value: UberNoise | UberNoiseOptions | undefined) { 590 - if (value == undefined) { 591 - this._warpNoise2 = undefined; 592 - return; 593 - } 594 - 595 - const processed = this.checkParameterInput(value); 596 - if (processed instanceof UberNoise) { 597 - this._warpNoise2 = processed; 598 - } 599 - } 600 - } 601 - 602 - export default UberNoise;
+63
src/lib/components/project/demos/depth3ddemo.svelte
··· 1 + <script lang="ts"> 2 + import { Depth3D } from './depth-3d'; 3 + 4 + //@ts-expect-error - not found 5 + import girl from './depth-3d/images/girl.jpg?format=webp&w=1024'; 6 + //@ts-expect-error - not found 7 + import girldepth from './depth-3d/images/girl-depth.png?format=webp&w=1024'; 8 + 9 + //@ts-expect-error - not found 10 + import napoleon from './depth-3d/images/napoleon-crossing-the-alps.jpg?format=webp&w=1024'; 11 + //@ts-expect-error - not found 12 + import napoleondepth from './depth-3d/images/napoleon-crossing-the-alps-depth.png?format=webp&w=1024'; 13 + 14 + //@ts-expect-error - not found 15 + import vanGogh from './depth-3d/images/van-gogh-self-portrait.webp?format=webp&w=1024'; 16 + //@ts-expect-error - not found 17 + import vanGoghdepth from './depth-3d/images/van-gogh-self-portrait-depth.png?format=webp&w=1024'; 18 + 19 + //@ts-expect-error - not found 20 + import monaLisa from './depth-3d/images/mona-lisa.jpg?format=webp&w=1024'; 21 + //@ts-expect-error - not found 22 + import monaLisadepth from './depth-3d/images/mona-lisa-depth.png?format=webp&w=1024'; 23 + 24 + let images: { 25 + image: string; 26 + depth: string; 27 + }[] = [ 28 + { 29 + image: girl, 30 + depth: girldepth 31 + }, 32 + { 33 + image: napoleon, 34 + depth: napoleondepth 35 + }, 36 + { 37 + image: vanGogh, 38 + depth: vanGoghdepth 39 + }, 40 + { 41 + image: monaLisa, 42 + depth: monaLisadepth 43 + } 44 + ]; 45 + 46 + let currentImage = 0; 47 + </script> 48 + 49 + <button 50 + on:click={() => { 51 + console.log('clicked'); 52 + currentImage = (currentImage + 1) % images.length; 53 + }} 54 + class="h-full w-full" 55 + > 56 + {#key currentImage} 57 + <Depth3D 58 + image={{ image: images[currentImage].image, depth: images[currentImage].depth }} 59 + detail={500} 60 + cameraPosition={[0, 0, 7]} 61 + /> 62 + {/key} 63 + </button>
src/lib/images/projects-new/life/image1.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/life/image2.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/life/image3.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/life/image4.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/old-code/image1.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/old-code/image2.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/old-code/image3.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/old-code/image4.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/old-code/old-code-demo.mp4

This is a binary file and will not be displayed.

src/lib/images/projects-new/old-code/old-code-image.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/tiny-planets/image1.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/tiny-planets/image2.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/tiny-planets/image3.png

This is a binary file and will not be displayed.

src/lib/images/projects-new/tiny-planets/tiny-planets-demo.mp4

This is a binary file and will not be displayed.

src/lib/images/projects-new/tiny-planets/tiny-planets-image.png

This is a binary file and will not be displayed.

+1 -1
src/lib/components/project/demos/CardSwiper/Card.svelte
··· 15 15 > 16 16 {#key image} 17 17 {#if image} 18 - <img class="w-full h-full rounded-xl object-cover" src={image} alt={title} /> 18 + <img class="w-full h-full rounded-xl object-cover" src={image} alt="" /> 19 19 {/if} 20 20 {/key} 21 21 <div class="absolute inset-0 bg-gradient-to-t from-black/80 via-black/0 rounded-b-xl"></div>
+13
src/lib/components/project/demos/depth-3d/Depth3D.svelte
··· 1 + <script lang="ts"> 2 + import { Canvas } from '@threlte/core'; 3 + import Scene3D from './Scene3D.svelte'; 4 + 5 + export let image: { 6 + image: string; 7 + depth: string; 8 + }; 9 + </script> 10 + 11 + <Canvas> 12 + <Scene3D {image} {...$$restProps} /> 13 + </Canvas>
+119
src/lib/components/project/demos/depth-3d/Scene3D.svelte
··· 1 + <script lang="ts"> 2 + import { T, useTask } from '@threlte/core'; 3 + import { useTexture, Align } from '@threlte/extras'; 4 + 5 + import { Vector2, ShaderMaterial, PlaneGeometry, LinearSRGBColorSpace } from 'three'; 6 + 7 + type DepthImage = { 8 + image: string; 9 + depth: string; 10 + }; 11 + export let image: DepthImage; 12 + 13 + export let rounded = false; 14 + 15 + export let cameraPosition: [number, number, number] = [0, 0, 9]; 16 + 17 + export let rotate = true; 18 + export let rotationScale = 0.2; 19 + export let rotationSpeed = 1.5; 20 + 21 + export let detail = 200; 22 + 23 + export let depthScale = 2; 24 + 25 + const map = useTexture(image.image, { 26 + transform: (texture) => { 27 + texture.colorSpace = LinearSRGBColorSpace; 28 + //texture.encoding = LinearEncoding; 29 + return texture; 30 + } 31 + }); 32 + const depthMap = useTexture(image.depth, { 33 + transform: (texture) => { 34 + //texture.encoding = LinearEncoding; 35 + return texture; 36 + } 37 + }); 38 + 39 + const rotation = new Vector2(0.5, 0.5); 40 + 41 + const uniforms = { 42 + uTexture: { type: 't', value: map }, 43 + depthMap: { type: 't', value: depthMap } 44 + }; 45 + const material = new ShaderMaterial({ 46 + uniforms: uniforms, 47 + vertexShader: ` 48 + varying vec2 vUv; 49 + uniform sampler2D depthMap; 50 + 51 + void main() { 52 + vUv = uv; 53 + // move z position based on the depth map 54 + float depth = texture2D(depthMap, vUv).r; 55 + vec3 newPosition = position + vec3(0.0, 0.0, depth * ${depthScale.toFixed(1)}); 56 + gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); 57 + }`, 58 + fragmentShader: ` 59 + uniform sampler2D uTexture; 60 + varying vec2 vUv; 61 + 62 + void main() { 63 + ${ 64 + rounded 65 + ? `// get distance from center 66 + vec2 center = vec2(0.5, 0.5); 67 + float dist = distance(vUv, center); 68 + 69 + // if distance is greater than 0.5, discard the pixel 70 + if (dist > 0.5) discard;` 71 + : '' 72 + } 73 + 74 + gl_FragColor = texture2D(uTexture, vUv); 75 + }` 76 + }); 77 + 78 + const geometry = new PlaneGeometry(7, 7, detail, detail); 79 + 80 + export let time = 0; 81 + const { start, stop } = useTask( 82 + (dt) => { 83 + time += dt * rotationSpeed; 84 + rotation.x = Math.sin(time) * 0.5; 85 + rotation.y = Math.cos(time) * 0.5; 86 + }, 87 + { autoStart: false } 88 + ); 89 + 90 + $: if (rotate) { 91 + start(); 92 + } else { 93 + stop(); 94 + } 95 + </script> 96 + 97 + <T.PerspectiveCamera makeDefault position={cameraPosition}></T.PerspectiveCamera> 98 + 99 + {#await map then mapValue} 100 + {#await depthMap then depthValue} 101 + <Align> 102 + <T.Mesh 103 + rotation.x={rotation.y * rotationScale} 104 + rotation.y={rotation.x * rotationScale} 105 + scale.x={mapValue.image.width / mapValue.image.height} 106 + > 107 + <T is={geometry} /> 108 + <T 109 + is={material} 110 + uniforms={{ 111 + depthMap: { value: depthValue }, 112 + uTexture: { value: mapValue }, 113 + mouse: { value: rotation } 114 + }} 115 + /> 116 + </T.Mesh> 117 + </Align> 118 + {/await} 119 + {/await}
+2
src/lib/components/project/demos/depth-3d/index.ts
··· 1 + export { default as Depth3D } from './Depth3D.svelte'; 2 + export { default } from './Depth3D.svelte';
src/lib/components/project/demos/depth-3d/images/girl-depth.png

This is a binary file and will not be displayed.

src/lib/components/project/demos/depth-3d/images/girl.jpg

This is a binary file and will not be displayed.

src/lib/components/project/demos/depth-3d/images/mona-lisa-depth.png

This is a binary file and will not be displayed.

src/lib/components/project/demos/depth-3d/images/mona-lisa.jpg

This is a binary file and will not be displayed.

src/lib/components/project/demos/depth-3d/images/napoleon-crossing-the-alps-depth.png

This is a binary file and will not be displayed.

src/lib/components/project/demos/depth-3d/images/napoleon-crossing-the-alps.jpg

This is a binary file and will not be displayed.

src/lib/components/project/demos/depth-3d/images/van-gogh-self-portrait-depth.png

This is a binary file and will not be displayed.

src/lib/components/project/demos/depth-3d/images/van-gogh-self-portrait.webp

This is a binary file and will not be displayed.