@@ -39,6 +39,16 @@ const t = useTranslations(Astro);
align-items: flex-end;
}
+ .image::before {
+ content: "";
+ position: absolute;
+ height: 80%;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(-12deg, var(--background) 0%, transparent 50%);
+ }
+
.image > :global(img) {
height: 100%;
object-fit: contain;
@@ -50,7 +60,7 @@ const t = useTranslations(Astro);
}
.eye.left {
top: 29%;
- right: 27%;
+ right: 28%;
}
.eye.right {
top: 31%;
diff --git a/src/components/SmallCard.astro b/src/components/SmallCard.astro
new file mode 100644
index 0000000..5f078f8
--- /dev/null
+++ b/src/components/SmallCard.astro
@@ -0,0 +1,45 @@
+---
+import markdownToText from "@helpers/markdownToText";
+
+interface Props {
+ post: {
+ data: {
+ title: string;
+ description: string;
+ icon: string;
+ tags: string[];
+ };
+ collection: string;
+ body: string;
+ slug: string;
+ };
+}
+
+const { post } = Astro.props;
+---
+
+
diff --git a/src/components/arrows/ArrowA.astro b/src/components/arrows/ArrowA.astro
new file mode 100644
index 0000000..49320b1
--- /dev/null
+++ b/src/components/arrows/ArrowA.astro
@@ -0,0 +1,21 @@
+---
+import { useTranslations } from "@i18n/utils";
+const t = useTranslations(Astro);
+---
+
+
+
+
diff --git a/src/components/arrows/ArrowB.astro b/src/components/arrows/ArrowB.astro
new file mode 100644
index 0000000..ad8040e
--- /dev/null
+++ b/src/components/arrows/ArrowB.astro
@@ -0,0 +1,21 @@
+---
+import { useTranslations } from "@i18n/utils";
+const t = useTranslations(Astro);
+---
+
+
+
+
diff --git a/src/components/card/Content.svelte b/src/components/card/Content.svelte
index b1c4d0a..b144079 100644
--- a/src/components/card/Content.svelte
+++ b/src/components/card/Content.svelte
@@ -2,6 +2,6 @@
export let classes = "";
-
+
diff --git a/src/components/card/Image.svelte b/src/components/card/Image.svelte
index e67e1b4..0154e32 100644
--- a/src/components/card/Image.svelte
+++ b/src/components/card/Image.svelte
@@ -1,11 +1,11 @@
-
![]()
+
![]()
diff --git a/src/content/blog/alpine-valley/index.mdx b/src/content/blog/alpine-valley/index.mdx
index d9b592d..a58e133 100644
--- a/src/content/blog/alpine-valley/index.mdx
+++ b/src/content/blog/alpine-valley/index.mdx
@@ -5,7 +5,19 @@ headerImg: "images/Render.png"
comments: true
---
+
+Die Inspiration diesen Render zu machen kam von einem Bild was 2013 auf dem Weg nach Italien entstanden ist.
+
+
+
+
+
import Image from "@components/Image.astro";
+import ImageGallery from "@components/ImageGallery.svelte";
import ValleyView from "./images/Render.png";
import DSC_1601 from "./images/DSC_1601.jpg";
import Unbenannt1 from "./images/Unbenannt-1.jpg";
@@ -13,15 +25,8 @@ import Render4 from "./images/Render4.png";
import UntitledPng from "./images/untitled1-1.png"
import Render6 from "./images/Render6.png";
-
-
-
-Die Inspiration diesen Render zu machen kam von einem Bild was 2013 auf dem Weg nach Italien entstanden ist.
-
diff --git a/src/content/blog/random-renders-02/index.en.mdx b/src/content/blog/random-renders-02/index.en.mdx
index 25f461c..ca50e05 100644
--- a/src/content/blog/random-renders-02/index.en.mdx
+++ b/src/content/blog/random-renders-02/index.en.mdx
@@ -1,9 +1,13 @@
---
title: "Random Renders NO°2"
date: 2023-05-11
-headerImg: "images/render_05.png"
+headerImg: "images/palma.png"
+featured: true
comments: true
---
+
+Last month I felt like creating some renderings again. I also found a few old renderings that haven't ended up on my blog yet.
+
import Image from "@components/Image.astro"
import Palma from "./images/palma.png"
import Render05 from "./images/render_05.png"
@@ -13,7 +17,6 @@ import Cabin_new from "./images/Cabin_new.png";
import Home from "./images/Home.png";
import Workroom from "./images/Workroom.png";
-Last month I felt like creating some renderings again. I also found a few old renderings that haven't ended up on my blog yet.
## Palma
diff --git a/src/content/blog/random-renders-02/index.mdx b/src/content/blog/random-renders-02/index.mdx
index 1a18253..782ac18 100644
--- a/src/content/blog/random-renders-02/index.mdx
+++ b/src/content/blog/random-renders-02/index.mdx
@@ -1,38 +1,41 @@
---
title: "Random Renders NO°2"
date: 2023-05-11
-headerImg: "images/render_05.png"
+headerImg: "images/palma.png"
+featured: true
comments: true
---
+Im letzten Monat hatte ich wieder Lust, einige Renderings zu erstellen. Außerdem habe ich noch ein paar alte Renderings gefunden die noch nicht auf meinem Blog gelandet sind.
+
+import ImageGallery from "@components/ImageGallery.svelte"
import Image from "@components/Image.astro"
import Palma from "./images/palma.png"
import Render05 from "./images/render_05.png"
+import Poster_var2 from "./images/Poster_var2.jpg";
+import Cabin_old from "./images/Cabin_old.jpg";
+import Cabin_new from "./images/Cabin_new.png";
+import Home from "./images/Home.png";
+import Workroom from "./images/Workroom.png";
-Im letzten Monat hatte ich wieder Lust, einige Renderings zu erstellen. Außerdem habe ich noch ein paar alte Renderings gefunden die noch nicht auf meinem Blog gelandet sind.
-
+
## Palma
-
+
## [Sudoku.nvim](https://github.com/jim-fx/sudoku.nvim)
-
+
-
+
## Super old renders:
-
+
-
+
-
+
+
+
-
diff --git a/src/content/blog/server-setup/index.mdx b/src/content/blog/server-setup/index.mdx
index e01488a..5f8a285 100644
--- a/src/content/blog/server-setup/index.mdx
+++ b/src/content/blog/server-setup/index.mdx
@@ -1,11 +1,13 @@
---
-title: "Wie ich mein Server-Setup komplett neu aufbaue."
+title: "Mein neues Server-Setup"
date: 2020-06-09
comments: true
---
import Image from "@components/Image.astro"
+import ServerSetup from "./images/new-server.svg"
+
Dieses Thema beschäftigt mich also schon seit längerem. Und in den letzten zwei Wochen ist es mir endlich klar geworden. Dieser Blogbeitrag ist nicht als Leitfaden gedacht, sondern als Möglichkeit für mein zukünftiges Ich, die Entscheidungsprozesse meines jetzigen Ichs zu verstehen.
> **Mein Plan ist es, die Leitfäden später als separate Beiträge zu veröffentlichen:**
@@ -44,7 +46,7 @@ In meinem vorherigen Setup habe ich eine Mischung aus externen Diensten verwende
## My new Setup
-

+
Eine Frage, die man sich jetzt stellen könnte wäre: „Warum benutzt du Cloud und lokale Server?“ Beide haben einige Nachteile und einige Vorteile. Cloud-Server bieten schnelle Netzwerkgeschwindigkeiten und statische öffentliche IPs, aber die Speicherung großer Datenmengen ist recht teuer. Der Festplattenspeicher ist bei lokalen Servern vergleichsweise günstig. Ich habe etwa 60 € für meine 1-TB-Festplatte bezahlt, die speziell für NAS-Situationen entwickelt wurde, bei denen die Laufwerke rund um die Uhr laufen. Außerdem gefällt mir die Idee sehr gut, physischen Zugriff auf meine eigenen Daten zu haben und diese nicht einem Dritten anvertrauen zu müssen. Ein weiterer Vorteil des physischen Zugriffs auf meinen eigenen Server besteht darin, dass ich an Hardwareaspekten wie Netzwerk, Laufwerken und Kühlung herumbasteln und physische Messungen wie Raumtemperatur und Luftfeuchtigkeit vornehmen kann.
diff --git a/src/content/blog/zentralwerk_2051/Fishes.svelte b/src/content/blog/zentralwerk_2051/Fishes.svelte
index 41952ec..89d7970 100644
--- a/src/content/blog/zentralwerk_2051/Fishes.svelte
+++ b/src/content/blog/zentralwerk_2051/Fishes.svelte
@@ -2,6 +2,7 @@
import { onMount } from "svelte";
import createFishes from "./fishes/webgl-fishes";
import { Color } from "ogl";
+ import { rgbToHex } from "@helpers/colors";
let canvasBottom: HTMLCanvasElement;
@@ -26,20 +27,7 @@
fishCanvasBack.resize();
};
- // function to turn css rgb() strings to hex
- function rgbToHex(rgb: string) {
- let hex = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
- if (!hex) return rgb;
- return (
- "#" +
- hex
- .slice(1)
- .map((x) => {
- return ("0" + parseInt(x).toString(16)).slice(-2);
- })
- .join("")
- );
- }
+ let loaded = false;
onMount(async () => {
const background = window.getComputedStyle(document.body);
@@ -56,6 +44,7 @@
speed *= 0.99;
render && fishCanvasBack.update(t, timeOffset);
}
+ loaded = true;
});
@@ -69,7 +58,7 @@
}}
/>
-
+
diff --git a/src/content/blog/zentralwerk_2051/index.mdx b/src/content/blog/zentralwerk_2051/index.mdx
index 4e5f70c..6476a8a 100644
--- a/src/content/blog/zentralwerk_2051/index.mdx
+++ b/src/content/blog/zentralwerk_2051/index.mdx
@@ -1,7 +1,6 @@
---
title: "Zentralwerk_2051"
date: 2021-01-07
-_layout: "transparent"
links: [["PDF", "/Zentralwerk_2051.pdf"]]
license: "CC-BY-SA:4.0"
comments: true
diff --git a/src/content/config.ts b/src/content/config.ts
index b2907a2..f487799 100644
--- a/src/content/config.ts
+++ b/src/content/config.ts
@@ -5,8 +5,11 @@ const blogCollection = defineCollection({
title: z.string(),
date: z.date(),
headerImg: z.string().optional(),
+ description: z.string().optional(),
+ icon: z.string().optional(),
draft: z.boolean().optional(),
featured: z.boolean().optional(),
+ tags: z.array(z.string()).optional(),
_layout: z.enum(['normal', 'transparent']).optional(),
})
});
diff --git a/src/content/photos/bigge-changes/index.mdx b/src/content/photos/bigge-changes/index.mdx
index e263a45..501ad7a 100644
--- a/src/content/photos/bigge-changes/index.mdx
+++ b/src/content/photos/bigge-changes/index.mdx
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3de9da5d2c700a83deb91624f1e5958c66a8c55cb8ef1813c5be6eef2146b2f7
-size 1539
+oid sha256:c8313ec2e7cd4e62735591a440a8c311bcbd489102b953bdd56b7e2e6a6e9f3c
+size 1527
diff --git a/src/content/photos/bilder-caen-ogrove/index.mdx b/src/content/photos/bilder-caen-ogrove/index.mdx
index f94c302..b736c07 100644
--- a/src/content/photos/bilder-caen-ogrove/index.mdx
+++ b/src/content/photos/bilder-caen-ogrove/index.mdx
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8035ddbac41c667cc09049ab33ed36532ba9e0fa437ab980a84e77495939146a
-size 3807
+oid sha256:516fd6ad169fd725656e0c413203bfdf21511dc5a0b37f6013a7522c2d55e75f
+size 3819
diff --git a/src/content/photos/erasmus-valencia/index.en.mdx b/src/content/photos/erasmus-valencia/index.en.mdx
index a226bd2..9bd039f 100644
--- a/src/content/photos/erasmus-valencia/index.en.mdx
+++ b/src/content/photos/erasmus-valencia/index.en.mdx
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5d1fbb68dd9806fc0834a3968b5430e80e56be2e4d74805f00f5d986a86c9f53
-size 3156
+oid sha256:8e99f77ffd3df85a12f43e006c852b9362c01f18b301ffceb4dff6b8239236b3
+size 3296
diff --git a/src/content/photos/erasmus-valencia/index.mdx b/src/content/photos/erasmus-valencia/index.mdx
index fdf929b..9331a77 100644
--- a/src/content/photos/erasmus-valencia/index.mdx
+++ b/src/content/photos/erasmus-valencia/index.mdx
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3a19f4fff65895d5df8f0e93c3e6f2b908abebf44ec46261cb3f56247abd3b1d
-size 3373
+oid sha256:5a1441fa05a5855386f3f6c6f991f1911547548a8e591c89b7a84c6adf169f66
+size 3362
diff --git a/src/content/photos/point-de-lisle/index.en.mdx b/src/content/photos/point-de-lisle/index.en.mdx
index 5c923d1..b442c66 100644
--- a/src/content/photos/point-de-lisle/index.en.mdx
+++ b/src/content/photos/point-de-lisle/index.en.mdx
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:dd253a70ffab454517571aea3a50ec3d188297757c51f7f8b8985f8b796f5ae1
-size 890
+oid sha256:1d458d264e1d3447e7ba86015e62086784bbed080a23292f1a63d58688b97df6
+size 818
diff --git a/src/content/photos/point-de-lisle/index.mdx b/src/content/photos/point-de-lisle/index.mdx
index 5ff8f0f..5958b7b 100644
--- a/src/content/photos/point-de-lisle/index.mdx
+++ b/src/content/photos/point-de-lisle/index.mdx
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:97b199aa299327150d4b9730c3c5f35a32751f0c6dbf6235dd9d04871eeac643
-size 909
+oid sha256:d18c8ae5454465a0d5b41df4571120ce9b1ecc7c1196897f12854bc0ad9410d9
+size 927
diff --git a/src/content/photos/portugal-2021/index.mdx b/src/content/photos/portugal-2021/index.mdx
index b02e7ae..07ce30e 100644
--- a/src/content/photos/portugal-2021/index.mdx
+++ b/src/content/photos/portugal-2021/index.mdx
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:85027bd748f68d38c2a46b96e702887d5069d7745fcfebce0fafbe5e6ff67f08
-size 1374
+oid sha256:92736b48ea09593d7648eebe779b4078d5802d4a2ec6a694c34eecdb0613dce9
+size 2339
diff --git a/src/content/projects/argenti/index.md b/src/content/projects/argenti/index.md
index f9643a0..8115064 100644
--- a/src/content/projects/argenti/index.md
+++ b/src/content/projects/argenti/index.md
@@ -1,7 +1,10 @@
---
title: "Argenti"
date: 2023-08-21
+icon: "/projects/argenti.png"
draft: true
+description: "A central database for things I enjoy stored in Markdown/Redis"
+tags: ["deno", "fresh", "redis"]
links:
[
["live", "https://invoice.app.max-richter.dev"],
diff --git a/src/content/projects/invoice/index.md b/src/content/projects/invoice/index.md
index 2bd0ca1..8308ce6 100644
--- a/src/content/projects/invoice/index.md
+++ b/src/content/projects/invoice/index.md
@@ -2,6 +2,8 @@
title: "Invoice"
date: 2023-08-21
headerImg: "bg.jpg"
+icon: "/projects/invoice.svg"
+tags: ["sveltekit", "unocss", "prisma", "sqlite"]
draft: true
links:
[
diff --git a/src/content/projects/isyncrasy/index.mdx b/src/content/projects/isyncrasy/index.mdx
new file mode 100644
index 0000000..f0fcdec
--- /dev/null
+++ b/src/content/projects/isyncrasy/index.mdx
@@ -0,0 +1,10 @@
+---
+date: 2019-06-10
+title: "Isyncrasy"
+draft: false
+icon: "/projects/isyncrasy/favicon.ico"
+description: "A small fun virtual OS build with svelte"
+tags: ["svelte", "web", "os"]
+---
+
+# Isyncrasy
diff --git a/src/content/projects/plantarium/_components/Leaf.frag b/src/content/projects/plantarium/_components/Leaf.frag
new file mode 100644
index 0000000..29bf290
--- /dev/null
+++ b/src/content/projects/plantarium/_components/Leaf.frag
@@ -0,0 +1,23 @@
+precision highp float;
+precision highp int;
+
+uniform vec3 uBackground;
+
+varying vec3 vNormal;
+varying vec2 vUv;
+varying float fog;
+
+void main(){
+
+ vec3 normal=normalize(vNormal);
+
+ float lighting=dot(normal,normalize(vec3(-.3,.8,.6)));
+
+ vec3 col = (vec3(.308*vUv.x,.712,.5)+lighting*.5)-.25;
+ /* gl_FragColor.rgb=vec3(vUv.x,vUv.y,vNormal.x); */
+ gl_FragColor.rgb=mix(col, uBackground,fog);
+
+ gl_FragColor.a=1.;
+
+}
+
diff --git a/src/content/projects/plantarium/_components/Leaf.vert b/src/content/projects/plantarium/_components/Leaf.vert
new file mode 100644
index 0000000..0353b14
--- /dev/null
+++ b/src/content/projects/plantarium/_components/Leaf.vert
@@ -0,0 +1,49 @@
+attribute vec2 uv;
+attribute vec3 position;
+// Add instanced attributes just like any attribute
+attribute vec3 offset;
+attribute vec3 random;
+attribute vec3 normal;
+
+uniform mat4 modelViewMatrix;
+uniform mat4 projectionMatrix;
+uniform mat4 normalMatrix;
+
+uniform float uTime;
+uniform float uRot;
+varying vec2 vUv;
+varying vec3 vNormal;
+varying float fog;
+
+void rotate2d(inout vec2 v, float a){
+ mat2 m = mat2(cos(a), -sin(a), sin(a), cos(a));
+ v = m * v;
+}
+void main() {
+ vUv = uv;
+
+ vNormal = normal;
+
+ /* vNormal = normalize(normalMatrix * normal); */
+
+ // copy position so that we can modify the instances
+ vec3 pos = position;
+
+ // scale first
+ fog = (offset.z+1.0)/2.0;
+ pos *= 0.4 + (1.0-fog) * 0.6;
+
+ // rotate around y axis
+ rotate2d(pos.xz, random.x * 6.28 + 1.0 * uTime * (random.y - 0.5));
+
+ rotate2d(pos.xy, random.y * 6.28 + 1.0 * uTime * (random.y - 0.5));
+ // rotate around x axis just to add some extra variation
+ rotate2d(pos.zy, random.z * 0.5 * sin(uTime * random.x + random.z * 3.14));
+
+ pos.y += sin(uRot/500.0+random.y*50.0)/5.0+(1.0-fog)*uRot;
+
+ pos += offset;
+
+
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
+}
diff --git a/src/content/projects/plantarium/_components/Leaves.svelte b/src/content/projects/plantarium/_components/Leaves.svelte
new file mode 100644
index 0000000..d3edb66
--- /dev/null
+++ b/src/content/projects/plantarium/_components/Leaves.svelte
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
diff --git a/src/content/projects/plantarium/_components/leave.json b/src/content/projects/plantarium/_components/leave.json
new file mode 100644
index 0000000..b6ffaf1
--- /dev/null
+++ b/src/content/projects/plantarium/_components/leave.json
@@ -0,0 +1,5 @@
+{
+ "position": [0.09, -0.076, -0.054, 0.124, -0.046, 0.021, 0.087, -0.144, -0.023, 0.087, -0.144, -0.023, 0.124, -0.046, 0.021, 0.119, -0.115, 0.049, 0.09, -0.016, 0.096, 0.009, -0.003, 0.127, 0.087, -0.086, 0.121, 0.087, -0.086, 0.121, 0.009, -0.003, 0.127, 0.009, -0.074, 0.151, -0.072, -0.016, 0.096, -0.105, -0.046, 0.021, -0.068, -0.086, 0.121, -0.068, -0.086, 0.121, -0.105, -0.046, 0.021, -0.101, -0.115, 0.049, -0.072, -0.076, -0.054, 0.009, -0.089, -0.085, -0.068, -0.144, -0.023, -0.068, -0.144, -0.023, 0.009, -0.089, -0.085, 0.009, -0.156, -0.053, 0.009, -0.089, -0.085, 0.09, -0.076, -0.054, 0.009, -0.156, -0.053, 0.009, -0.156, -0.053, 0.09, -0.076, -0.054, 0.087, -0.144, -0.023, 0.124, -0.046, 0.021, 0.09, -0.016, 0.096, 0.119, -0.115, 0.049, 0.119, -0.115, 0.049, 0.09, -0.016, 0.096, 0.087, -0.086, 0.121, 0.009, -0.003, 0.127, -0.072, -0.016, 0.096, 0.009, -0.074, 0.151, 0.009, -0.074, 0.151, -0.072, -0.016, 0.096, -0.068, -0.086, 0.121, -0.105, -0.046, 0.021, -0.072, -0.076, -0.054, -0.101, -0.115, 0.049, -0.101, -0.115, 0.049, -0.072, -0.076, -0.054, -0.068, -0.144, -0.023, -0.068, -0.144, -0.023, 0.009, -0.156, -0.053, -0.058, -0.199, 0.01, -0.058, -0.199, 0.01, 0.009, -0.156, -0.053, 0.009, -0.209, -0.016, 0.009, -0.156, -0.053, 0.087, -0.144, -0.023, 0.009, -0.209, -0.016, 0.009, -0.209, -0.016, 0.087, -0.144, -0.023, 0.077, -0.199, 0.01, 0.119, -0.115, 0.049, 0.087, -0.086, 0.121, 0.105, -0.173, 0.073, 0.105, -0.173, 0.073, 0.087, -0.086, 0.121, 0.077, -0.148, 0.135, 0.009, -0.074, 0.151, -0.068, -0.086, 0.121, 0.009, -0.137, 0.161, 0.009, -0.137, 0.161, -0.068, -0.086, 0.121, -0.058, -0.148, 0.135, -0.101, -0.115, 0.049, -0.068, -0.144, -0.023, -0.086, -0.173, 0.073, -0.086, -0.173, 0.073, -0.068, -0.144, -0.023, -0.058, -0.199, 0.01, 0.087, -0.144, -0.023, 0.119, -0.115, 0.049, 0.077, -0.199, 0.01, 0.077, -0.199, 0.01, 0.119, -0.115, 0.049, 0.105, -0.173, 0.073, 0.087, -0.086, 0.121, 0.009, -0.074, 0.151, 0.077, -0.148, 0.135, 0.077, -0.148, 0.135, 0.009, -0.074, 0.151, 0.009, -0.137, 0.161, -0.068, -0.086, 0.121, -0.101, -0.115, 0.049, -0.058, -0.148, 0.135, -0.058, -0.148, 0.135, -0.101, -0.115, 0.049, -0.086, -0.173, 0.073, 0.077, -0.199, 0.01, 0.105, -0.173, 0.073, 0.038, -0.219, 0.061, 0.038, -0.219, 0.061, 0.105, -0.173, 0.073, 0.049, -0.208, 0.087, 0.077, -0.148, 0.135, 0.009, -0.137, 0.161, 0.038, -0.198, 0.113, 0.038, -0.198, 0.113, 0.009, -0.137, 0.161, 0.009, -0.193, 0.124, -0.058, -0.148, 0.135, -0.086, -0.173, 0.073, -0.019, -0.198, 0.113, -0.019, -0.198, 0.113, -0.086, -0.173, 0.073, -0.031, -0.208, 0.087, -0.058, -0.199, 0.01, 0.009, -0.209, -0.016, -0.019, -0.219, 0.061, -0.019, -0.219, 0.061, 0.009, -0.209, -0.016, 0.009, -0.223, 0.05, 0.009, -0.209, -0.016, 0.077, -0.199, 0.01, 0.009, -0.223, 0.05, 0.009, -0.223, 0.05, 0.077, -0.199, 0.01, 0.038, -0.219, 0.061, 0.105, -0.173, 0.073, 0.077, -0.148, 0.135, 0.049, -0.208, 0.087, 0.049, -0.208, 0.087, 0.077, -0.148, 0.135, 0.038, -0.198, 0.113, 0.009, -0.137, 0.161, -0.058, -0.148, 0.135, 0.009, -0.193, 0.124, 0.009, -0.193, 0.124, -0.058, -0.148, 0.135, -0.019, -0.198, 0.113, -0.086, -0.173, 0.073, -0.058, -0.199, 0.01, -0.031, -0.208, 0.087, -0.031, -0.208, 0.087, -0.058, -0.199, 0.01, -0.019, -0.219, 0.061, -0.019, -0.219, 0.061, 0.009, -0.223, 0.05, 0.038, -0.219, 0.061, 0.038, -0.219, 0.061, 0.009, -0.193, 0.124, -0.019, -0.219, 0.061, 0.049, -0.208, 0.087, 0.009, -0.193, 0.124, 0.038, -0.219, 0.061, 0.009, -0.193, 0.124, -0.031, -0.208, 0.087, -0.019, -0.219, 0.061, 0.038, -0.198, 0.113, 0.009, -0.193, 0.124, 0.049, -0.208, 0.087, 0.009, -0.193, 0.124, -0.019, -0.198, 0.113, -0.031, -0.208, 0.087, 0.124, -0.046, 0.021, 0.09, -0.076, -0.054, 0.14, -0.035, 0.017, 0.14, -0.035, 0.017, 0.09, -0.076, -0.054, 0.102, -0.07, -0.069, 0.009, -0.003, 0.127, 0.09, -0.016, 0.096, 0.009, 0.014, 0.138, 0.009, 0.014, 0.138, 0.09, -0.016, 0.096, 0.102, -0.001, 0.103, -0.105, -0.046, 0.021, -0.072, -0.016, 0.096, -0.122, -0.035, 0.017, -0.122, -0.035, 0.017, -0.072, -0.016, 0.096, -0.083, -0.001, 0.103, 0.009, -0.089, -0.085, -0.072, -0.076, -0.054, 0.009, -0.085, -0.105, 0.009, -0.085, -0.105, -0.072, -0.076, -0.054, -0.083, -0.07, -0.069, 0.09, -0.076, -0.054, 0.009, -0.089, -0.085, 0.102, -0.07, -0.069, 0.102, -0.07, -0.069, 0.009, -0.089, -0.085, 0.009, -0.085, -0.105, 0.09, -0.016, 0.096, 0.124, -0.046, 0.021, 0.102, -0.001, 0.103, 0.102, -0.001, 0.103, 0.124, -0.046, 0.021, 0.14, -0.035, 0.017, -0.072, -0.016, 0.096, 0.009, -0.003, 0.127, -0.083, -0.001, 0.103, -0.083, -0.001, 0.103, 0.009, -0.003, 0.127, 0.009, 0.014, 0.138, -0.072, -0.076, -0.054, -0.105, -0.046, 0.021, -0.083, -0.07, -0.069, -0.083, -0.07, -0.069, -0.105, -0.046, 0.021, -0.122, -0.035, 0.017, -0.122, -0.035, 0.017, -0.083, -0.001, 0.103, -0.13, -0.006, 0.005, -0.13, -0.006, 0.005, -0.083, -0.001, 0.103, -0.089, 0.031, 0.096, 0.102, -0.07, -0.069, 0.009, -0.085, -0.105, 0.108, -0.043, -0.087, 0.108, -0.043, -0.087, 0.009, -0.085, -0.105, 0.009, -0.058, -0.124, 0.009, -0.085, -0.105, -0.083, -0.07, -0.069, 0.009, -0.058, -0.124, 0.009, -0.058, -0.124, -0.083, -0.07, -0.069, -0.089, -0.043, -0.087, 0.102, -0.001, 0.103, 0.14, -0.035, 0.017, 0.108, 0.031, 0.096, 0.108, 0.031, 0.096, 0.14, -0.035, 0.017, 0.149, -0.006, 0.005, -0.083, -0.001, 0.103, 0.009, 0.014, 0.138, -0.089, 0.031, 0.096, -0.089, 0.031, 0.096, 0.009, 0.014, 0.138, 0.009, 0.047, 0.134, -0.083, -0.07, -0.069, -0.122, -0.035, 0.017, -0.089, -0.043, -0.087, -0.089, -0.043, -0.087, -0.122, -0.035, 0.017, -0.13, -0.006, 0.005, 0.14, -0.035, 0.017, 0.102, -0.07, -0.069, 0.149, -0.006, 0.005, 0.149, -0.006, 0.005, 0.102, -0.07, -0.069, 0.108, -0.043, -0.087, 0.009, 0.014, 0.138, 0.102, -0.001, 0.103, 0.009, 0.047, 0.134, 0.009, 0.047, 0.134, 0.102, -0.001, 0.103, 0.108, 0.031, 0.096, 0.149, -0.006, 0.005, 0.108, -0.043, -0.087, 0.129, 0.036, -0.012, 0.129, 0.036, -0.012, 0.108, -0.043, -0.087, 0.094, 0.004, -0.09, 0.009, 0.047, 0.134, 0.108, 0.031, 0.096, 0.009, 0.081, 0.098, 0.009, 0.081, 0.098, 0.108, 0.031, 0.096, 0.094, 0.067, 0.066, -0.13, -0.006, 0.005, -0.089, 0.031, 0.096, -0.11, 0.036, -0.012, -0.11, 0.036, -0.012, -0.089, 0.031, 0.096, -0.075, 0.067, 0.066, 0.009, -0.058, -0.124, -0.089, -0.043, -0.087, 0.009, -0.009, -0.123, 0.009, -0.009, -0.123, -0.089, -0.043, -0.087, -0.075, 0.004, -0.09, 0.108, -0.043, -0.087, 0.009, -0.058, -0.124, 0.094, 0.004, -0.09, 0.094, 0.004, -0.09, 0.009, -0.058, -0.124, 0.009, -0.009, -0.123, 0.108, 0.031, 0.096, 0.149, -0.006, 0.005, 0.094, 0.067, 0.066, 0.094, 0.067, 0.066, 0.149, -0.006, 0.005, 0.129, 0.036, -0.012, -0.089, 0.031, 0.096, 0.009, 0.047, 0.134, -0.075, 0.067, 0.066, -0.075, 0.067, 0.066, 0.009, 0.047, 0.134, 0.009, 0.081, 0.098, -0.089, -0.043, -0.087, -0.13, -0.006, 0.005, -0.075, 0.004, -0.09, -0.075, 0.004, -0.09, -0.13, -0.006, 0.005, -0.11, 0.036, -0.012, -0.075, 0.067, 0.066, 0.009, 0.081, 0.098, -0.031, 0.081, 0.013, -0.031, 0.081, 0.013, 0.009, 0.081, 0.098, 0.009, 0.087, 0.028, -0.075, 0.004, -0.09, -0.11, 0.036, -0.012, -0.031, 0.051, -0.061, -0.031, 0.051, -0.061, -0.11, 0.036, -0.012, -0.047, 0.066, -0.024, 0.129, 0.036, -0.012, 0.094, 0.004, -0.09, 0.066, 0.066, -0.024, 0.066, 0.066, -0.024, 0.094, 0.004, -0.09, 0.049, 0.051, -0.061, 0.009, 0.081, 0.098, 0.094, 0.067, 0.066, 0.009, 0.087, 0.028, 0.009, 0.087, 0.028, 0.094, 0.067, 0.066, 0.049, 0.081, 0.013, -0.11, 0.036, -0.012, -0.075, 0.067, 0.066, -0.047, 0.066, -0.024, -0.047, 0.066, -0.024, -0.075, 0.067, 0.066, -0.031, 0.081, 0.013, 0.009, -0.009, -0.123, -0.075, 0.004, -0.09, 0.009, 0.045, -0.077, 0.009, 0.045, -0.077, -0.075, 0.004, -0.09, -0.031, 0.051, -0.061, 0.094, 0.004, -0.09, 0.009, -0.009, -0.123, 0.049, 0.051, -0.061, 0.049, 0.051, -0.061, 0.009, -0.009, -0.123, 0.009, 0.045, -0.077, 0.094, 0.067, 0.066, 0.129, 0.036, -0.012, 0.049, 0.081, 0.013, 0.049, 0.081, 0.013, 0.129, 0.036, -0.012, 0.066, 0.066, -0.024, 0.049, 0.051, -0.061, 0.009, 0.045, -0.077, 0.025, 0.071, -0.043, 0.025, 0.071, -0.043, 0.009, 0.045, -0.077, 0.009, 0.069, -0.049, 0.049, 0.081, 0.013, 0.066, 0.066, -0.024, 0.025, 0.083, -0.015, 0.025, 0.083, -0.015, 0.066, 0.066, -0.024, 0.031, 0.077, -0.029, -0.031, 0.081, 0.013, 0.009, 0.087, 0.028, -0.006, 0.083, -0.015, -0.006, 0.083, -0.015, 0.009, 0.087, 0.028, 0.009, 0.085, -0.009, -0.031, 0.051, -0.061, -0.047, 0.066, -0.024, -0.006, 0.071, -0.043, -0.006, 0.071, -0.043, -0.047, 0.066, -0.024, -0.012, 0.077, -0.029, 0.066, 0.066, -0.024, 0.049, 0.051, -0.061, 0.031, 0.077, -0.029, 0.031, 0.077, -0.029, 0.049, 0.051, -0.061, 0.025, 0.071, -0.043, 0.009, 0.087, 0.028, 0.049, 0.081, 0.013, 0.009, 0.085, -0.009, 0.009, 0.085, -0.009, 0.049, 0.081, 0.013, 0.025, 0.083, -0.015, -0.047, 0.066, -0.024, -0.031, 0.081, 0.013, -0.012, 0.077, -0.029, -0.012, 0.077, -0.029, -0.031, 0.081, 0.013, -0.006, 0.083, -0.015, 0.009, 0.045, -0.077, -0.031, 0.051, -0.061, 0.009, 0.069, -0.049, 0.009, 0.069, -0.049, -0.031, 0.051, -0.061, -0.006, 0.071, -0.043, 0.025, 0.083, -0.015, 0.012, 0.121, -0.03, 0.009, 0.085, -0.009, 0.009, 0.085, -0.009, 0.012, 0.121, -0.03, -0.003, 0.118, -0.022, -0.006, 0.083, -0.015, -0.017, 0.111, -0.026, -0.012, 0.077, -0.029, -0.012, 0.077, -0.029, -0.017, 0.111, -0.026, -0.023, 0.103, -0.039, 0.009, 0.069, -0.049, -0.006, 0.071, -0.043, -0.003, 0.102, -0.062, -0.003, 0.102, -0.062, -0.006, 0.071, -0.043, -0.017, 0.099, -0.054, 0.025, 0.071, -0.043, 0.009, 0.069, -0.049, 0.012, 0.11, -0.059, 0.012, 0.11, -0.059, 0.009, 0.069, -0.049, -0.003, 0.102, -0.062, 0.031, 0.077, -0.029, 0.018, 0.117, -0.045, 0.025, 0.083, -0.015, 0.025, 0.083, -0.015, 0.018, 0.117, -0.045, 0.012, 0.121, -0.03, 0.009, 0.085, -0.009, -0.003, 0.118, -0.022, -0.006, 0.083, -0.015, -0.006, 0.083, -0.015, -0.003, 0.118, -0.022, -0.017, 0.111, -0.026, -0.006, 0.071, -0.043, -0.012, 0.077, -0.029, -0.017, 0.099, -0.054, -0.017, 0.099, -0.054, -0.012, 0.077, -0.029, -0.023, 0.103, -0.039, 0.031, 0.077, -0.029, 0.025, 0.071, -0.043, 0.018, 0.117, -0.045, 0.018, 0.117, -0.045, 0.025, 0.071, -0.043, 0.012, 0.11, -0.059, 0.018, 0.117, -0.045, 0.012, 0.11, -0.059, -0.022, 0.158, -0.062, -0.022, 0.158, -0.062, 0.012, 0.11, -0.059, -0.026, 0.148, -0.074, 0.012, 0.121, -0.03, -0.026, 0.16, -0.046, -0.003, 0.118, -0.022, -0.003, 0.118, -0.022, -0.026, 0.16, -0.046, -0.037, 0.152, -0.036, -0.017, 0.111, -0.026, -0.048, 0.14, -0.038, -0.023, 0.103, -0.039, -0.023, 0.103, -0.039, -0.048, 0.14, -0.038, -0.052, 0.13, -0.05, -0.003, 0.102, -0.062, -0.017, 0.099, -0.054, -0.037, 0.136, -0.076, -0.037, 0.136, -0.076, -0.017, 0.099, -0.054, -0.048, 0.128, -0.066, 0.012, 0.11, -0.059, -0.003, 0.102, -0.062, -0.026, 0.148, -0.074, -0.026, 0.148, -0.074, -0.003, 0.102, -0.062, -0.037, 0.136, -0.076, 0.018, 0.117, -0.045, -0.022, 0.158, -0.062, 0.012, 0.121, -0.03, 0.012, 0.121, -0.03, -0.022, 0.158, -0.062, -0.026, 0.16, -0.046, -0.003, 0.118, -0.022, -0.037, 0.152, -0.036, -0.017, 0.111, -0.026, -0.017, 0.111, -0.026, -0.037, 0.152, -0.036, -0.048, 0.14, -0.038, -0.017, 0.099, -0.054, -0.023, 0.103, -0.039, -0.048, 0.128, -0.066, -0.048, 0.128, -0.066, -0.023, 0.103, -0.039, -0.052, 0.13, -0.05, -0.048, 0.14, -0.038, -0.026, 0.16, -0.046, -0.052, 0.13, -0.05, -0.037, 0.152, -0.036, -0.026, 0.16, -0.046, -0.048, 0.14, -0.038, -0.048, 0.128, -0.066, -0.052, 0.13, -0.05, -0.026, 0.148, -0.074, -0.037, 0.136, -0.076, -0.048, 0.128, -0.066, -0.026, 0.148, -0.074, -0.022, 0.158, -0.062, -0.026, 0.148, -0.074, -0.026, 0.16, -0.046, -0.052, 0.13, -0.05, -0.026, 0.16, -0.046, -0.026, 0.148, -0.074],
+ "normal": [0.922, -0.198, -0.332, 0.922, -0.198, -0.332, 0.922, -0.198, -0.332, 0.922, -0.198, -0.332, 0.922, -0.198, -0.332, 0.922, -0.198, -0.332, 0.382, 0.292, 0.877, 0.382, 0.292, 0.877, 0.382, 0.292, 0.877, 0.382, 0.292, 0.877, 0.382, 0.292, 0.877, 0.382, 0.292, 0.877, -0.922, 0.089, 0.376, -0.922, 0.089, 0.376, -0.922, 0.089, 0.376, -0.922, 0.089, 0.376, -0.922, 0.089, 0.376, -0.922, 0.089, 0.376, -0.382, -0.401, -0.833, -0.382, -0.401, -0.833, -0.382, -0.401, -0.833, -0.382, -0.401, -0.833, -0.382, -0.401, -0.833, -0.382, -0.401, -0.833, 0.382, -0.401, -0.833, 0.382, -0.401, -0.833, 0.382, -0.401, -0.833, 0.382, -0.401, -0.833, 0.382, -0.401, -0.833, 0.382, -0.401, -0.833, 0.922, 0.089, 0.376, 0.922, 0.089, 0.376, 0.922, 0.089, 0.376, 0.922, 0.089, 0.376, 0.922, 0.089, 0.376, 0.922, 0.089, 0.376, -0.382, 0.292, 0.877, -0.382, 0.292, 0.877, -0.382, 0.292, 0.877, -0.382, 0.292, 0.877, -0.382, 0.292, 0.877, -0.382, 0.292, 0.877, -0.922, -0.198, -0.332, -0.922, -0.198, -0.332, -0.922, -0.198, -0.332, -0.922, -0.198, -0.332, -0.922, -0.198, -0.332, -0.922, -0.198, -0.332, -0.374, -0.531, -0.76, -0.374, -0.531, -0.76, -0.374, -0.531, -0.76, -0.374, -0.531, -0.76, -0.374, -0.531, -0.76, -0.374, -0.531, -0.76, 0.374, -0.531, -0.76, 0.374, -0.531, -0.76, 0.374, -0.531, -0.76, 0.374, -0.531, -0.76, 0.374, -0.531, -0.76, 0.374, -0.531, -0.76, 0.904, -0.051, 0.425, 0.904, -0.051, 0.425, 0.904, -0.051, 0.425, 0.904, -0.051, 0.425, 0.904, -0.051, 0.425, 0.904, -0.051, 0.425, -0.374, 0.148, 0.915, -0.374, 0.148, 0.915, -0.374, 0.148, 0.915, -0.374, 0.148, 0.915, -0.374, 0.148, 0.915, -0.374, 0.148, 0.915, -0.904, -0.332, -0.269, -0.904, -0.332, -0.269, -0.904, -0.332, -0.269, -0.904, -0.332, -0.269, -0.904, -0.332, -0.269, -0.904, -0.332, -0.269, 0.904, -0.332, -0.269, 0.904, -0.332, -0.269, 0.904, -0.332, -0.269, 0.904, -0.332, -0.269, 0.904, -0.332, -0.269, 0.904, -0.332, -0.269, 0.374, 0.148, 0.915, 0.374, 0.148, 0.915, 0.374, 0.148, 0.915, 0.374, 0.148, 0.915, 0.374, 0.148, 0.915, 0.374, 0.148, 0.915, -0.904, -0.051, 0.425, -0.904, -0.051, 0.425, -0.904, -0.051, 0.425, -0.904, -0.051, 0.425, -0.904, -0.051, 0.425, -0.904, -0.051, 0.425, 0.545, -0.833, 0.094, 0.545, -0.833, 0.094, 0.545, -0.833, 0.094, 0.545, -0.833, 0.094, 0.545, -0.833, 0.094, 0.545, -0.833, 0.094, 0.226, -0.543, 0.809, 0.226, -0.543, 0.809, 0.226, -0.543, 0.809, 0.226, -0.543, 0.809, 0.226, -0.543, 0.809, 0.226, -0.543, 0.809, -0.545, -0.663, 0.513, -0.545, -0.663, 0.513, -0.545, -0.663, 0.513, -0.545, -0.663, 0.513, -0.545, -0.663, 0.513, -0.545, -0.663, 0.513, -0.226, -0.953, -0.202, -0.226, -0.953, -0.202, -0.226, -0.953, -0.202, -0.226, -0.953, -0.202, -0.226, -0.953, -0.202, -0.226, -0.953, -0.202, 0.226, -0.953, -0.202, 0.226, -0.953, -0.202, 0.226, -0.953, -0.202, 0.226, -0.953, -0.202, 0.226, -0.953, -0.202, 0.226, -0.953, -0.202, 0.545, -0.663, 0.513, 0.545, -0.663, 0.513, 0.545, -0.663, 0.513, 0.545, -0.663, 0.513, 0.545, -0.663, 0.513, 0.545, -0.663, 0.513, -0.226, -0.543, 0.809, -0.226, -0.543, 0.809, -0.226, -0.543, 0.809, -0.226, -0.543, 0.809, -0.226, -0.543, 0.809, -0.226, -0.543, 0.809, -0.545, -0.833, 0.094, -0.545, -0.833, 0.094, -0.545, -0.833, 0.094, -0.545, -0.833, 0.094, -0.545, -0.833, 0.094, -0.545, -0.833, 0.094, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.0, -0.927, 0.376, 0.559, -0.825, 0.085, 0.559, -0.825, 0.085, 0.559, -0.825, 0.085, 0.559, -0.825, 0.085, 0.559, -0.825, 0.085, 0.559, -0.825, 0.085, 0.231, -0.528, 0.817, 0.231, -0.528, 0.817, 0.231, -0.528, 0.817, 0.231, -0.528, 0.817, 0.231, -0.528, 0.817, 0.231, -0.528, 0.817, -0.559, -0.651, 0.514, -0.559, -0.651, 0.514, -0.559, -0.651, 0.514, -0.559, -0.651, 0.514, -0.559, -0.651, 0.514, -0.559, -0.651, 0.514, -0.231, -0.948, -0.219, -0.231, -0.948, -0.219, -0.231, -0.948, -0.219, -0.231, -0.948, -0.219, -0.231, -0.948, -0.219, -0.231, -0.948, -0.219, 0.231, -0.948, -0.219, 0.231, -0.948, -0.219, 0.231, -0.948, -0.219, 0.231, -0.948, -0.219, 0.231, -0.948, -0.219, 0.231, -0.948, -0.219, 0.559, -0.651, 0.514, 0.559, -0.651, 0.514, 0.559, -0.651, 0.514, 0.559, -0.651, 0.514, 0.559, -0.651, 0.514, 0.559, -0.651, 0.514, -0.231, -0.528, 0.817, -0.231, -0.528, 0.817, -0.231, -0.528, 0.817, -0.231, -0.528, 0.817, -0.231, -0.528, 0.817, -0.231, -0.528, 0.817, -0.559, -0.825, 0.085, -0.559, -0.825, 0.085, -0.559, -0.825, 0.085, -0.559, -0.825, 0.085, -0.559, -0.825, 0.085, -0.559, -0.825, 0.085, -0.897, -0.082, 0.434, -0.897, -0.082, 0.434, -0.897, -0.082, 0.434, -0.897, -0.082, 0.434, -0.897, -0.082, 0.434, -0.897, -0.082, 0.434, 0.372, -0.559, -0.742, 0.372, -0.559, -0.742, 0.372, -0.559, -0.742, 0.372, -0.559, -0.742, 0.372, -0.559, -0.742, 0.372, -0.559, -0.742, -0.372, -0.559, -0.742, -0.372, -0.559, -0.742, -0.372, -0.559, -0.742, -0.372, -0.559, -0.742, -0.372, -0.559, -0.742, -0.372, -0.559, -0.742, 0.897, -0.082, 0.434, 0.897, -0.082, 0.434, 0.897, -0.082, 0.434, 0.897, -0.082, 0.434, 0.897, -0.082, 0.434, 0.897, -0.082, 0.434, -0.372, 0.116, 0.921, -0.372, 0.116, 0.921, -0.372, 0.116, 0.921, -0.372, 0.116, 0.921, -0.372, 0.116, 0.921, -0.372, 0.116, 0.921, -0.897, -0.361, -0.255, -0.897, -0.361, -0.255, -0.897, -0.361, -0.255, -0.897, -0.361, -0.255, -0.897, -0.361, -0.255, -0.897, -0.361, -0.255, 0.897, -0.361, -0.255, 0.897, -0.361, -0.255, 0.897, -0.361, -0.255, 0.897, -0.361, -0.255, 0.897, -0.361, -0.255, 0.897, -0.361, -0.255, 0.372, 0.116, 0.921, 0.372, 0.116, 0.921, 0.372, 0.116, 0.921, 0.372, 0.116, 0.921, 0.372, 0.116, 0.921, 0.372, 0.116, 0.921, 0.853, 0.223, -0.472, 0.853, 0.223, -0.472, 0.853, 0.223, -0.472, 0.853, 0.223, -0.472, 0.853, 0.223, -0.472, 0.853, 0.223, -0.472, 0.353, 0.677, 0.646, 0.353, 0.677, 0.646, 0.353, 0.677, 0.646, 0.353, 0.677, 0.646, 0.353, 0.677, 0.646, 0.353, 0.677, 0.646, -0.853, 0.489, 0.183, -0.853, 0.489, 0.183, -0.853, 0.489, 0.183, -0.853, 0.489, 0.183, -0.853, 0.489, 0.183, -0.853, 0.489, 0.183, -0.353, 0.035, -0.935, -0.353, 0.035, -0.935, -0.353, 0.035, -0.935, -0.353, 0.035, -0.935, -0.353, 0.035, -0.935, -0.353, 0.035, -0.935, 0.353, 0.035, -0.935, 0.353, 0.035, -0.935, 0.353, 0.035, -0.935, 0.353, 0.035, -0.935, 0.353, 0.035, -0.935, 0.353, 0.035, -0.935, 0.853, 0.489, 0.183, 0.853, 0.489, 0.183, 0.853, 0.489, 0.183, 0.853, 0.489, 0.183, 0.853, 0.489, 0.183, 0.853, 0.489, 0.183, -0.353, 0.677, 0.646, -0.353, 0.677, 0.646, -0.353, 0.677, 0.646, -0.353, 0.677, 0.646, -0.353, 0.677, 0.646, -0.353, 0.677, 0.646, -0.853, 0.223, -0.472, -0.853, 0.223, -0.472, -0.853, 0.223, -0.472, -0.853, 0.223, -0.472, -0.853, 0.223, -0.472, -0.853, 0.223, -0.472, -0.188, 0.978, 0.093, -0.188, 0.978, 0.093, -0.188, 0.978, 0.093, -0.188, 0.978, 0.093, -0.188, 0.978, 0.093, -0.188, 0.978, 0.093, -0.454, 0.737, -0.501, -0.454, 0.737, -0.501, -0.454, 0.737, -0.501, -0.454, 0.737, -0.501, -0.454, 0.737, -0.501, -0.454, 0.737, -0.501, 0.454, 0.737, -0.501, 0.454, 0.737, -0.501, 0.454, 0.737, -0.501, 0.454, 0.737, -0.501, 0.454, 0.737, -0.501, 0.454, 0.737, -0.501, 0.188, 0.978, 0.093, 0.188, 0.978, 0.093, 0.188, 0.978, 0.093, 0.188, 0.978, 0.093, 0.188, 0.978, 0.093, 0.188, 0.978, 0.093, -0.454, 0.878, -0.153, -0.454, 0.878, -0.153, -0.454, 0.878, -0.153, -0.454, 0.878, -0.153, -0.454, 0.878, -0.153, -0.454, 0.878, -0.153, -0.188, 0.637, -0.748, -0.188, 0.637, -0.748, -0.188, 0.637, -0.748, -0.188, 0.637, -0.748, -0.188, 0.637, -0.748, -0.188, 0.637, -0.748, 0.188, 0.637, -0.748, 0.188, 0.637, -0.748, 0.188, 0.637, -0.748, 0.188, 0.637, -0.748, 0.188, 0.637, -0.748, 0.188, 0.637, -0.748, 0.454, 0.878, -0.153, 0.454, 0.878, -0.153, 0.454, 0.878, -0.153, 0.454, 0.878, -0.153, 0.454, 0.878, -0.153, 0.454, 0.878, -0.153, 0.133, 0.748, -0.651, 0.133, 0.748, -0.651, 0.133, 0.748, -0.651, 0.133, 0.748, -0.651, 0.133, 0.748, -0.651, 0.133, 0.748, -0.651, 0.322, 0.919, -0.229, 0.322, 0.919, -0.229, 0.322, 0.919, -0.229, 0.322, 0.919, -0.229, 0.322, 0.919, -0.229, 0.322, 0.919, -0.229, -0.133, 0.99, -0.054, -0.133, 0.99, -0.054, -0.133, 0.99, -0.054, -0.133, 0.99, -0.054, -0.133, 0.99, -0.054, -0.133, 0.99, -0.054, -0.322, 0.818, -0.476, -0.322, 0.818, -0.476, -0.322, 0.818, -0.476, -0.322, 0.818, -0.476, -0.322, 0.818, -0.476, -0.322, 0.818, -0.476, 0.322, 0.818, -0.476, 0.322, 0.818, -0.476, 0.322, 0.818, -0.476, 0.322, 0.818, -0.476, 0.322, 0.818, -0.476, 0.322, 0.818, -0.476, 0.133, 0.99, -0.054, 0.133, 0.99, -0.054, 0.133, 0.99, -0.054, 0.133, 0.99, -0.054, 0.133, 0.99, -0.054, 0.133, 0.99, -0.054, -0.322, 0.919, -0.229, -0.322, 0.919, -0.229, -0.322, 0.919, -0.229, -0.322, 0.919, -0.229, -0.322, 0.919, -0.229, -0.322, 0.919, -0.229, -0.133, 0.748, -0.651, -0.133, 0.748, -0.651, -0.133, 0.748, -0.651, -0.133, 0.748, -0.651, -0.133, 0.748, -0.651, -0.133, 0.748, -0.651, 0.372, 0.457, 0.808, 0.372, 0.457, 0.808, 0.372, 0.457, 0.808, 0.372, 0.457, 0.808, 0.372, 0.457, 0.808, 0.372, 0.457, 0.808, -0.871, -0.157, 0.466, -0.871, -0.157, 0.466, -0.871, -0.157, 0.466, -0.871, -0.157, 0.466, -0.871, -0.157, 0.466, -0.871, -0.157, 0.466, -0.371, -0.464, -0.804, -0.371, -0.464, -0.804, -0.371, -0.464, -0.804, -0.371, -0.464, -0.804, -0.371, -0.464, -0.804, -0.371, -0.464, -0.804, 0.372, -0.235, -0.898, 0.372, -0.235, -0.898, 0.372, -0.235, -0.898, 0.372, -0.235, -0.898, 0.372, -0.235, -0.898, 0.372, -0.235, -0.898, 0.885, 0.396, 0.244, 0.885, 0.396, 0.244, 0.885, 0.396, 0.244, 0.885, 0.396, 0.244, 0.885, 0.396, 0.244, 0.885, 0.396, 0.244, -0.37, 0.227, 0.901, -0.37, 0.227, 0.901, -0.37, 0.227, 0.901, -0.37, 0.227, 0.901, -0.37, 0.227, 0.901, -0.37, 0.227, 0.901, -0.871, -0.437, -0.225, -0.871, -0.437, -0.225, -0.871, -0.437, -0.225, -0.871, -0.437, -0.225, -0.871, -0.437, -0.225, -0.871, -0.437, -0.225, 0.885, 0.114, -0.451, 0.885, 0.114, -0.451, 0.885, 0.114, -0.451, 0.885, 0.114, -0.451, 0.885, 0.114, -0.451, 0.885, 0.114, -0.451, 0.691, 0.434, -0.578, 0.691, 0.434, -0.578, 0.691, 0.434, -0.578, 0.691, 0.434, -0.578, 0.691, 0.434, -0.578, 0.691, 0.434, -0.578, 0.29, 0.592, 0.752, 0.29, 0.592, 0.752, 0.29, 0.592, 0.752, 0.29, 0.592, 0.752, 0.29, 0.592, 0.752, 0.29, 0.592, 0.752, -0.659, -0.469, 0.589, -0.659, -0.469, 0.589, -0.659, -0.469, 0.589, -0.659, -0.469, 0.589, -0.659, -0.469, 0.589, -0.659, -0.469, 0.589, -0.285, -0.599, -0.749, -0.285, -0.599, -0.749, -0.285, -0.599, -0.749, -0.285, -0.599, -0.749, -0.285, -0.599, -0.749, -0.285, -0.599, -0.749, 0.29, -0.098, -0.952, 0.29, -0.098, -0.952, 0.29, -0.098, -0.952, 0.29, -0.098, -0.952, 0.29, -0.098, -0.952, 0.29, -0.098, -0.952, 0.691, 0.714, 0.112, 0.691, 0.714, 0.112, 0.691, 0.714, 0.112, 0.691, 0.714, 0.112, 0.691, 0.714, 0.112, 0.691, 0.714, 0.112, -0.285, 0.092, 0.954, -0.285, 0.092, 0.954, -0.285, 0.092, 0.954, -0.285, 0.092, 0.954, -0.285, 0.092, 0.954, -0.285, 0.092, 0.954, -0.659, -0.746, -0.096, -0.659, -0.746, -0.096, -0.659, -0.746, -0.096, -0.659, -0.746, -0.096, -0.659, -0.746, -0.096, -0.659, -0.746, -0.096, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267, -0.705, 0.657, -0.267],
+ "uv": [0.151, 0.302, 0.151, 0.169, 0.265, 0.299, 0.265, 0.299, 0.151, 0.169, 0.265, 0.171, 0.136, 0.169, 0.136, 0.302, 0.022, 0.172, 0.022, 0.172, 0.136, 0.302, 0.022, 0.299, 0.022, 0.155, 0.022, 0.022, 0.137, 0.152, 0.137, 0.152, 0.022, 0.022, 0.137, 0.024, 0.151, 0.155, 0.151, 0.022, 0.266, 0.152, 0.266, 0.152, 0.151, 0.022, 0.266, 0.024, 0.28, 0.461, 0.377, 0.463, 0.284, 0.574, 0.284, 0.574, 0.377, 0.463, 0.377, 0.576, 0.488, 0.575, 0.393, 0.58, 0.488, 0.462, 0.488, 0.462, 0.393, 0.58, 0.397, 0.467, 0.119, 0.57, 0.022, 0.577, 0.119, 0.458, 0.119, 0.458, 0.022, 0.577, 0.026, 0.464, 0.151, 0.465, 0.247, 0.468, 0.155, 0.578, 0.155, 0.578, 0.247, 0.468, 0.247, 0.581, 0.12, 0.316, 0.12, 0.444, 0.022, 0.324, 0.022, 0.324, 0.12, 0.444, 0.022, 0.435, 0.022, 0.591, 0.116, 0.591, 0.023, 0.688, 0.023, 0.688, 0.116, 0.591, 0.104, 0.688, 0.689, 0.591, 0.599, 0.602, 0.689, 0.496, 0.689, 0.496, 0.599, 0.602, 0.61, 0.506, 0.151, 0.595, 0.243, 0.595, 0.163, 0.692, 0.163, 0.692, 0.243, 0.595, 0.243, 0.692, 0.599, 0.616, 0.69, 0.616, 0.599, 0.714, 0.599, 0.714, 0.69, 0.616, 0.679, 0.714, 0.489, 0.022, 0.489, 0.149, 0.393, 0.03, 0.393, 0.03, 0.489, 0.149, 0.393, 0.141, 0.393, 0.448, 0.393, 0.32, 0.489, 0.439, 0.489, 0.439, 0.393, 0.32, 0.489, 0.328, 0.378, 0.022, 0.378, 0.149, 0.28, 0.03, 0.28, 0.03, 0.378, 0.149, 0.28, 0.141, 0.23, 0.706, 0.23, 0.817, 0.151, 0.738, 0.151, 0.738, 0.23, 0.817, 0.151, 0.785, 0.835, 0.13, 0.835, 0.241, 0.756, 0.162, 0.756, 0.162, 0.835, 0.241, 0.756, 0.209, 0.359, 0.731, 0.359, 0.842, 0.28, 0.763, 0.28, 0.763, 0.359, 0.842, 0.28, 0.809, 0.819, 0.32, 0.819, 0.431, 0.74, 0.352, 0.74, 0.352, 0.819, 0.431, 0.74, 0.399, 0.819, 0.445, 0.819, 0.556, 0.74, 0.477, 0.74, 0.477, 0.819, 0.556, 0.74, 0.524, 0.678, 0.728, 0.678, 0.839, 0.599, 0.76, 0.599, 0.76, 0.678, 0.839, 0.599, 0.806, 0.472, 0.711, 0.472, 0.822, 0.393, 0.743, 0.393, 0.743, 0.472, 0.822, 0.393, 0.79, 0.101, 0.702, 0.101, 0.813, 0.022, 0.734, 0.022, 0.734, 0.101, 0.813, 0.022, 0.781, 0.262, 0.791, 0.244, 0.749, 0.262, 0.706, 0.393, 0.697, 0.436, 0.594, 0.48, 0.697, 0.087, 0.906, 0.12, 0.827, 0.12, 0.939, 0.184, 0.943, 0.151, 0.864, 0.184, 0.831, 0.28, 0.899, 0.298, 0.856, 0.298, 0.941, 0.944, 0.107, 0.926, 0.064, 0.944, 0.022, 0.954, 0.759, 0.954, 0.626, 0.977, 0.768, 0.977, 0.768, 0.954, 0.626, 0.977, 0.616, 0.973, 0.14, 0.973, 0.273, 0.95, 0.13, 0.95, 0.13, 0.973, 0.273, 0.95, 0.282, 0.98, 0.328, 0.98, 0.461, 0.957, 0.318, 0.957, 0.318, 0.98, 0.461, 0.957, 0.47, 0.919, 0.465, 0.897, 0.333, 0.943, 0.47, 0.943, 0.47, 0.897, 0.333, 0.919, 0.32, 0.935, 0.14, 0.935, 0.273, 0.912, 0.13, 0.912, 0.13, 0.935, 0.273, 0.912, 0.282, 0.979, 0.802, 0.979, 0.935, 0.955, 0.792, 0.955, 0.792, 0.979, 0.935, 0.955, 0.944, 0.916, 0.759, 0.916, 0.626, 0.939, 0.768, 0.939, 0.768, 0.916, 0.626, 0.939, 0.616, 0.918, 0.935, 0.918, 0.802, 0.941, 0.944, 0.941, 0.944, 0.918, 0.802, 0.941, 0.792, 0.839, 0.621, 0.839, 0.773, 0.789, 0.616, 0.789, 0.616, 0.839, 0.773, 0.789, 0.778, 0.854, 0.902, 0.854, 0.792, 0.904, 0.91, 0.904, 0.91, 0.854, 0.792, 0.904, 0.793, 0.833, 0.477, 0.833, 0.325, 0.883, 0.482, 0.883, 0.482, 0.833, 0.325, 0.883, 0.32, 0.789, 0.9, 0.789, 0.792, 0.838, 0.913, 0.838, 0.913, 0.789, 0.792, 0.838, 0.799, 0.947, 0.484, 0.947, 0.593, 0.897, 0.485, 0.897, 0.485, 0.947, 0.593, 0.897, 0.6, 0.072, 0.835, 0.072, 0.942, 0.022, 0.827, 0.022, 0.827, 0.072, 0.942, 0.022, 0.942, 0.849, 0.287, 0.849, 0.135, 0.898, 0.292, 0.898, 0.292, 0.849, 0.135, 0.898, 0.13, 0.902, 0.621, 0.902, 0.773, 0.854, 0.616, 0.854, 0.616, 0.902, 0.773, 0.854, 0.778, 0.506, 0.292, 0.506, 0.13, 0.576, 0.28, 0.576, 0.28, 0.506, 0.13, 0.576, 0.142, 0.573, 0.32, 0.573, 0.482, 0.503, 0.331, 0.503, 0.331, 0.573, 0.482, 0.503, 0.47, 0.656, 0.13, 0.656, 0.292, 0.59, 0.142, 0.59, 0.142, 0.656, 0.292, 0.59, 0.28, 0.654, 0.32, 0.654, 0.482, 0.588, 0.331, 0.588, 0.331, 0.654, 0.482, 0.588, 0.47, 0.573, 0.643, 0.573, 0.758, 0.503, 0.634, 0.503, 0.634, 0.573, 0.758, 0.503, 0.732, 0.775, 0.616, 0.775, 0.731, 0.705, 0.642, 0.705, 0.642, 0.775, 0.731, 0.705, 0.74, 0.742, 0.13, 0.742, 0.247, 0.671, 0.155, 0.671, 0.155, 0.742, 0.247, 0.671, 0.255, 0.725, 0.32, 0.725, 0.481, 0.668, 0.326, 0.668, 0.326, 0.725, 0.481, 0.668, 0.463, 0.491, 0.169, 0.491, 0.292, 0.396, 0.218, 0.396, 0.218, 0.491, 0.292, 0.396, 0.276, 0.28, 0.306, 0.28, 0.169, 0.382, 0.27, 0.382, 0.27, 0.28, 0.169, 0.382, 0.205, 0.765, 0.754, 0.765, 0.889, 0.705, 0.771, 0.705, 0.771, 0.765, 0.889, 0.705, 0.834, 0.353, 0.59, 0.353, 0.717, 0.28, 0.652, 0.28, 0.652, 0.353, 0.717, 0.28, 0.712, 0.253, 0.316, 0.253, 0.451, 0.151, 0.353, 0.151, 0.353, 0.253, 0.451, 0.151, 0.417, 0.379, 0.32, 0.379, 0.447, 0.28, 0.343, 0.28, 0.343, 0.379, 0.447, 0.28, 0.403, 0.503, 0.62, 0.503, 0.496, 0.584, 0.615, 0.584, 0.615, 0.503, 0.496, 0.584, 0.557, 0.559, 0.772, 0.559, 0.909, 0.503, 0.819, 0.503, 0.819, 0.559, 0.909, 0.503, 0.884, 0.815, 0.022, 0.815, 0.08, 0.768, 0.028, 0.768, 0.028, 0.815, 0.08, 0.768, 0.051, 0.428, 0.836, 0.428, 0.901, 0.393, 0.861, 0.393, 0.861, 0.428, 0.901, 0.393, 0.886, 0.754, 0.022, 0.754, 0.08, 0.703, 0.045, 0.703, 0.045, 0.754, 0.08, 0.703, 0.067, 0.503, 0.087, 0.503, 0.022, 0.555, 0.066, 0.555, 0.066, 0.503, 0.022, 0.555, 0.041, 0.236, 0.831, 0.236, 0.895, 0.199, 0.842, 0.199, 0.842, 0.236, 0.895, 0.199, 0.867, 0.872, 0.022, 0.872, 0.081, 0.829, 0.052, 0.829, 0.052, 0.872, 0.081, 0.829, 0.075, 0.622, 0.022, 0.622, 0.085, 0.57, 0.04, 0.57, 0.04, 0.622, 0.085, 0.57, 0.065, 0.689, 0.022, 0.689, 0.081, 0.636, 0.038, 0.636, 0.038, 0.689, 0.081, 0.636, 0.061, 0.337, 0.856, 0.337, 0.921, 0.313, 0.863, 0.313, 0.863, 0.337, 0.921, 0.312, 0.919, 0.781, 0.255, 0.781, 0.302, 0.756, 0.259, 0.756, 0.259, 0.781, 0.302, 0.756, 0.302, 0.73, 0.96, 0.706, 0.952, 0.73, 0.903, 0.73, 0.903, 0.706, 0.952, 0.705, 0.904, 0.98, 0.55, 0.963, 0.543, 0.98, 0.484, 0.98, 0.484, 0.963, 0.543, 0.962, 0.487, 0.976, 0.022, 0.976, 0.09, 0.959, 0.025, 0.959, 0.025, 0.976, 0.09, 0.958, 0.091, 0.521, 0.923, 0.521, 0.98, 0.504, 0.93, 0.504, 0.93, 0.521, 0.98, 0.503, 0.978, 0.813, 0.304, 0.796, 0.3, 0.813, 0.256, 0.813, 0.256, 0.796, 0.3, 0.796, 0.255, 0.664, 0.92, 0.638, 0.917, 0.664, 0.853, 0.664, 0.853, 0.638, 0.917, 0.638, 0.853, 0.624, 0.926, 0.599, 0.922, 0.624, 0.854, 0.624, 0.854, 0.599, 0.922, 0.599, 0.853, 0.912, 0.022, 0.912, 0.105, 0.888, 0.03, 0.888, 0.03, 0.912, 0.105, 0.886, 0.105, 0.77, 0.903, 0.77, 0.958, 0.745, 0.908, 0.745, 0.908, 0.77, 0.958, 0.744, 0.959, 0.444, 0.836, 0.468, 0.845, 0.443, 0.911, 0.443, 0.911, 0.468, 0.845, 0.468, 0.911, 0.723, 0.581, 0.705, 0.576, 0.723, 0.496, 0.723, 0.496, 0.705, 0.576, 0.704, 0.5, 0.116, 0.793, 0.116, 0.706, 0.132, 0.786, 0.132, 0.786, 0.116, 0.706, 0.132, 0.702, 0.371, 0.856, 0.371, 0.93, 0.353, 0.861, 0.353, 0.861, 0.371, 0.93, 0.352, 0.926, 0.199, 0.909, 0.222, 0.918, 0.199, 0.956, 0.199, 0.956, 0.222, 0.918, 0.222, 0.963, 0.236, 0.95, 0.254, 0.909, 0.254, 0.968, 0.367, 0.673, 0.377, 0.651, 0.377, 0.696, 0.393, 0.933, 0.411, 0.915, 0.411, 0.974, 0.574, 0.794, 0.583, 0.772, 0.583, 0.817, 0.367, 0.614, 0.377, 0.59, 0.377, 0.637, 0.876, 0.496, 0.876, 0.555, 0.833, 0.536]
+}
diff --git a/src/content/projects/plantarium/_components/leaves.ts b/src/content/projects/plantarium/_components/leaves.ts
new file mode 100644
index 0000000..27da6bb
--- /dev/null
+++ b/src/content/projects/plantarium/_components/leaves.ts
@@ -0,0 +1,88 @@
+import { Camera, Color, Geometry, GLTFLoader, Mesh, Program, Renderer, Transform } from 'ogl';
+import { rand, randMinMax } from "./random";
+
+import fragment from "./Leaf.frag";
+import vertex from "./Leaf.vert";
+import { rgbToHex } from '@helpers/colors';
+
+let rotation = 0;
+
+export function setRotation(r: number) {
+ rotation = r;
+}
+
+export function createLeaves({ canvas, num = 20, alpha = false, minZ = -1, maxZ = 1 }: { canvas: HTMLCanvasElement, num?: number, alpha?: boolean, minZ?: number, maxZ?: number }) {
+
+
+ const renderer = new Renderer({ dpr: 1, canvas, alpha: true });
+ const gl = renderer.gl;
+
+ const background = window.getComputedStyle(document.body);
+ const d = new Color(rgbToHex(background.backgroundColor));
+
+ const camera = new Camera(gl, { fov: 15 });
+ camera.position.z = 5;
+
+ function resize() {
+ renderer.setSize(window.innerWidth, window.innerHeight);
+ camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
+ }
+ window.addEventListener('resize', resize, false);
+ resize();
+
+ const scene = new Transform();
+
+ const program = new Program(gl, {
+ vertex,
+ fragment,
+ cullFace: gl.NONE,
+ depthTest: true,
+ uniforms: {
+ uTime: { value: 0 },
+ uRot: { value: 0 },
+ uBackground: { value: d },
+ },
+ });
+
+ let mesh: Mesh;
+ loadModel();
+ async function loadModel() {
+
+ const model = await GLTFLoader.load(gl, "/models/leaf.glb");
+
+ const data = model.nodes[0].children[0].geometry.attributes;
+
+ let offset = new Float32Array(num * 3);
+ let random = new Float32Array(num * 3);
+ for (let i = 0; i < num; i++) {
+ offset.set([rand(8), rand(4), randMinMax(minZ, maxZ)], i * 3);
+
+ // unique random values are always handy for instances.
+ // Here they will be used for rotation, scale and movement.
+ random.set([Math.random(), Math.random(), Math.random()], i * 3);
+ }
+
+ const geometry = new Geometry(gl, {
+ ...data,
+
+ // simply add the 'instanced' property to flag as an instanced attribute.
+ // set the value as the divisor number
+ offset: { instanced: 1, size: 3, data: offset },
+ random: { instanced: 1, size: 3, data: random },
+ });
+
+ mesh = new Mesh(gl, { geometry, program });
+ mesh.scale.set(0.2, 0.2, 0.2)
+ mesh.setParent(scene);
+ }
+
+ requestAnimationFrame(update);
+ function update(t: number) {
+ requestAnimationFrame(update);
+
+ program.uniforms.uTime.value = t * 0.001;
+ program.uniforms.uRot.value = rotation;
+ renderer.render({ scene, camera });
+ }
+
+}
diff --git a/src/content/projects/plantarium/_components/random.ts b/src/content/projects/plantarium/_components/random.ts
new file mode 100644
index 0000000..d1fb2d7
--- /dev/null
+++ b/src/content/projects/plantarium/_components/random.ts
@@ -0,0 +1,3 @@
+export const rand = (r = 1) => (Math.random() * 2 - 1) * r;
+
+export const randMinMax = (min = 0, max = 1) => min + Math.random() * (max - min)
diff --git a/public/projects/plantarium/plantarium.png b/src/content/projects/plantarium/images/plantarium.png
similarity index 100%
rename from public/projects/plantarium/plantarium.png
rename to src/content/projects/plantarium/images/plantarium.png
diff --git a/src/content/projects/plantarium/index.en.mdx b/src/content/projects/plantarium/index.en.mdx
index fae0f46..1876d90 100644
--- a/src/content/projects/plantarium/index.en.mdx
+++ b/src/content/projects/plantarium/index.en.mdx
@@ -1,7 +1,7 @@
---
title: "Plantarium"
date: 2022-08-31
-headerImg: "/projects/plantarium/plantarium.png"
+headerImg: "images/plantarium.png"
featured: true
links: [["website", "https://plant.max-richter.com"], ["git", "https://github.com/jim-fx/plantarium"]]
draft: true
diff --git a/src/content/projects/plantarium/index.mdx b/src/content/projects/plantarium/index.mdx
index 8affaa2..c4e24e3 100644
--- a/src/content/projects/plantarium/index.mdx
+++ b/src/content/projects/plantarium/index.mdx
@@ -1,7 +1,7 @@
---
title: "Plantarium"
date: 2022-08-31T20:46:26+02:00
-headerImg: "/projects/plantarium/plantarium.png"
+headerImg: "images/plantarium.png"
featured: true
links: [["website", "https://plant.max-richter.dev"], ["git", "https://github.com/jim-fx/plantarium"]]
tags: ["Web", "3D", "Svelte", "Node-Systeme"]
@@ -14,14 +14,31 @@ Plantarium ist wohl das Hobby Projekt, mit dem ich am meisten Zeit verbracht hab
Plantarium ist eine WebApp mit der Nutzer 3D Model von Pflanzen generieren können. Der erste Prototyp war innerhalb von zwei Wochen intensiver Arbeit fertig und sah ungefähr so aus:
-
-
-
-
-
-
-
-
+import ImageSlider from "@components/ImageSlider.svelte"
+import Leaves from "./_components/Leaves.svelte"
+import Image from "@components/Image.astro"
+import page01_0 from "./images/page01-0.jpg"
+import page01_1 from "./images/page01-1.jpg"
+import page01_2 from "./images/page01-2.jpg"
+import page01_3 from "./images/page01-3.jpg"
+import page01_5 from "./images/page01-5.jpg"
+import page01_6 from "./images/page01-6.jpg"
+import screenshot_geometry_nodes from "./images/screenshot-geometry-nodes.jpg"
+import screenshot_houdini from "./images/screenshot-houdini.jpg"
+import screenshot_unreal from "./images/screenshot-unreal.jpg"
+import screenshot_davinci from "./images/screenshot-davinci.jpg"
+
+
+
+
+
+
+
+
+
+
+
+
Schon gar nicht schlecht, aber wie das mit Prototypen so ist gab es noch einiges zu verbessern. Also eine Kurze Historie der größten Änderungen bis heute:
@@ -56,13 +73,13 @@ In der Beispielgrafik haben wir zwei `input-color` nodes die jeweils eine Farbe
Das coole ist das man dieses System sehr generisch gestalten kann und zum beispiel eine `generate-stem`, `generate-branches` oder eine `add-leaves` node programmieren kann. Aufgrund der

-
-
-
-
-
-
-
+
+
+
+
+
+
+
### Svelte-Kit Rewrite
diff --git a/src/helpers/colors.ts b/src/helpers/colors.ts
new file mode 100644
index 0000000..5bddaac
--- /dev/null
+++ b/src/helpers/colors.ts
@@ -0,0 +1,14 @@
+// function to turn css rgb() strings to hex
+export function rgbToHex(rgb: string) {
+ let hex = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
+ if (!hex) return rgb;
+ return (
+ "#" +
+ hex
+ .slice(1)
+ .map((x) => {
+ return ("0" + parseInt(x).toString(16)).slice(-2);
+ })
+ .join("")
+ );
+}
diff --git a/src/helpers/exif.ts b/src/helpers/exif.ts
new file mode 100644
index 0000000..453463a
--- /dev/null
+++ b/src/helpers/exif.ts
@@ -0,0 +1,1033 @@
+let debug = true;
+
+let EXIF = {};
+
+var ExifTags = EXIF.Tags = {
+
+ // version tags
+ 0x9000: "ExifVersion", // EXIF version
+ 0xA000: "FlashpixVersion", // Flashpix format version
+
+ // colorspace tags
+ 0xA001: "ColorSpace", // Color space information tag
+
+ // image configuration
+ 0xA002: "PixelXDimension", // Valid width of meaningful image
+ 0xA003: "PixelYDimension", // Valid height of meaningful image
+ 0x9101: "ComponentsConfiguration", // Information about channels
+ 0x9102: "CompressedBitsPerPixel", // Compressed bits per pixel
+
+ // user information
+ 0x927C: "MakerNote", // Any desired information written by the manufacturer
+ 0x9286: "UserComment", // Comments by user
+
+ // related file
+ 0xA004: "RelatedSoundFile", // Name of related sound file
+
+ // date and time
+ 0x9003: "DateTimeOriginal", // Date and time when the original image was generated
+ 0x9004: "DateTimeDigitized", // Date and time when the image was stored digitally
+ 0x9290: "SubsecTime", // Fractions of seconds for DateTime
+ 0x9291: "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
+ 0x9292: "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized
+
+ // picture-taking conditions
+ 0x829A: "ExposureTime", // Exposure time (in seconds)
+ 0x829D: "FNumber", // F number
+ 0x8822: "ExposureProgram", // Exposure program
+ 0x8824: "SpectralSensitivity", // Spectral sensitivity
+ 0x8827: "ISOSpeedRatings", // ISO speed rating
+ 0x8828: "OECF", // Optoelectric conversion factor
+ 0x9201: "ShutterSpeedValue", // Shutter speed
+ 0x9202: "ApertureValue", // Lens aperture
+ 0x9203: "BrightnessValue", // Value of brightness
+ 0x9204: "ExposureBias", // Exposure bias
+ 0x9205: "MaxApertureValue", // Smallest F number of lens
+ 0x9206: "SubjectDistance", // Distance to subject in meters
+ 0x9207: "MeteringMode", // Metering mode
+ 0x9208: "LightSource", // Kind of light source
+ 0x9209: "Flash", // Flash status
+ 0x9214: "SubjectArea", // Location and area of main subject
+ 0x920A: "FocalLength", // Focal length of the lens in mm
+ 0xA20B: "FlashEnergy", // Strobe energy in BCPS
+ 0xA20C: "SpatialFrequencyResponse", //
+ 0xA20E: "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
+ 0xA20F: "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
+ 0xA210: "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
+ 0xA214: "SubjectLocation", // Location of subject in image
+ 0xA215: "ExposureIndex", // Exposure index selected on camera
+ 0xA217: "SensingMethod", // Image sensor type
+ 0xA300: "FileSource", // Image source (3 == DSC)
+ 0xA301: "SceneType", // Scene type (1 == directly photographed)
+ 0xA302: "CFAPattern", // Color filter array geometric pattern
+ 0xA401: "CustomRendered", // Special processing
+ 0xA402: "ExposureMode", // Exposure mode
+ 0xA403: "WhiteBalance", // 1 = auto white balance, 2 = manual
+ 0xA404: "DigitalZoomRation", // Digital zoom ratio
+ 0xA405: "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
+ 0xA406: "SceneCaptureType", // Type of scene
+ 0xA407: "GainControl", // Degree of overall image gain adjustment
+ 0xA408: "Contrast", // Direction of contrast processing applied by camera
+ 0xA409: "Saturation", // Direction of saturation processing applied by camera
+ 0xA40A: "Sharpness", // Direction of sharpness processing applied by camera
+ 0xA40B: "DeviceSettingDescription", //
+ 0xA40C: "SubjectDistanceRange", // Distance to subject
+
+ // other tags
+ 0xA005: "InteroperabilityIFDPointer",
+ 0xA420: "ImageUniqueID" // Identifier assigned uniquely to each image
+};
+
+var TiffTags = EXIF.TiffTags = {
+ 0x0100: "ImageWidth",
+ 0x0101: "ImageHeight",
+ 0x8769: "ExifIFDPointer",
+ 0x8825: "GPSInfoIFDPointer",
+ 0xA005: "InteroperabilityIFDPointer",
+ 0x0102: "BitsPerSample",
+ 0x0103: "Compression",
+ 0x0106: "PhotometricInterpretation",
+ 0x0112: "Orientation",
+ 0x0115: "SamplesPerPixel",
+ 0x011C: "PlanarConfiguration",
+ 0x0212: "YCbCrSubSampling",
+ 0x0213: "YCbCrPositioning",
+ 0x011A: "XResolution",
+ 0x011B: "YResolution",
+ 0x0128: "ResolutionUnit",
+ 0x0111: "StripOffsets",
+ 0x0116: "RowsPerStrip",
+ 0x0117: "StripByteCounts",
+ 0x0201: "JPEGInterchangeFormat",
+ 0x0202: "JPEGInterchangeFormatLength",
+ 0x012D: "TransferFunction",
+ 0x013E: "WhitePoint",
+ 0x013F: "PrimaryChromaticities",
+ 0x0211: "YCbCrCoefficients",
+ 0x0214: "ReferenceBlackWhite",
+ 0x0132: "DateTime",
+ 0x010E: "ImageDescription",
+ 0x010F: "Make",
+ 0x0110: "Model",
+ 0x0131: "Software",
+ 0x013B: "Artist",
+ 0x8298: "Copyright"
+};
+
+var GPSTags = EXIF.GPSTags = {
+ 0x0000: "GPSVersionID",
+ 0x0001: "GPSLatitudeRef",
+ 0x0002: "GPSLatitude",
+ 0x0003: "GPSLongitudeRef",
+ 0x0004: "GPSLongitude",
+ 0x0005: "GPSAltitudeRef",
+ 0x0006: "GPSAltitude",
+ 0x0007: "GPSTimeStamp",
+ 0x0008: "GPSSatellites",
+ 0x0009: "GPSStatus",
+ 0x000A: "GPSMeasureMode",
+ 0x000B: "GPSDOP",
+ 0x000C: "GPSSpeedRef",
+ 0x000D: "GPSSpeed",
+ 0x000E: "GPSTrackRef",
+ 0x000F: "GPSTrack",
+ 0x0010: "GPSImgDirectionRef",
+ 0x0011: "GPSImgDirection",
+ 0x0012: "GPSMapDatum",
+ 0x0013: "GPSDestLatitudeRef",
+ 0x0014: "GPSDestLatitude",
+ 0x0015: "GPSDestLongitudeRef",
+ 0x0016: "GPSDestLongitude",
+ 0x0017: "GPSDestBearingRef",
+ 0x0018: "GPSDestBearing",
+ 0x0019: "GPSDestDistanceRef",
+ 0x001A: "GPSDestDistance",
+ 0x001B: "GPSProcessingMethod",
+ 0x001C: "GPSAreaInformation",
+ 0x001D: "GPSDateStamp",
+ 0x001E: "GPSDifferential"
+};
+
+// EXIF 2.3 Spec
+var IFD1Tags = EXIF.IFD1Tags = {
+ 0x0100: "ImageWidth",
+ 0x0101: "ImageHeight",
+ 0x0102: "BitsPerSample",
+ 0x0103: "Compression",
+ 0x0106: "PhotometricInterpretation",
+ 0x0111: "StripOffsets",
+ 0x0112: "Orientation",
+ 0x0115: "SamplesPerPixel",
+ 0x0116: "RowsPerStrip",
+ 0x0117: "StripByteCounts",
+ 0x011A: "XResolution",
+ 0x011B: "YResolution",
+ 0x011C: "PlanarConfiguration",
+ 0x0128: "ResolutionUnit",
+ 0x0201: "JpegIFOffset", // When image format is JPEG, this value show offset to JPEG data stored.(aka "ThumbnailOffset" or "JPEGInterchangeFormat")
+ 0x0202: "JpegIFByteCount", // When image format is JPEG, this value shows data size of JPEG image (aka "ThumbnailLength" or "JPEGInterchangeFormatLength")
+ 0x0211: "YCbCrCoefficients",
+ 0x0212: "YCbCrSubSampling",
+ 0x0213: "YCbCrPositioning",
+ 0x0214: "ReferenceBlackWhite"
+};
+
+var StringValues = EXIF.StringValues = {
+ ExposureProgram: {
+ 0: "Not defined",
+ 1: "Manual",
+ 2: "Normal program",
+ 3: "Aperture priority",
+ 4: "Shutter priority",
+ 5: "Creative program",
+ 6: "Action program",
+ 7: "Portrait mode",
+ 8: "Landscape mode"
+ },
+ MeteringMode: {
+ 0: "Unknown",
+ 1: "Average",
+ 2: "CenterWeightedAverage",
+ 3: "Spot",
+ 4: "MultiSpot",
+ 5: "Pattern",
+ 6: "Partial",
+ 255: "Other"
+ },
+ LightSource: {
+ 0: "Unknown",
+ 1: "Daylight",
+ 2: "Fluorescent",
+ 3: "Tungsten (incandescent light)",
+ 4: "Flash",
+ 9: "Fine weather",
+ 10: "Cloudy weather",
+ 11: "Shade",
+ 12: "Daylight fluorescent (D 5700 - 7100K)",
+ 13: "Day white fluorescent (N 4600 - 5400K)",
+ 14: "Cool white fluorescent (W 3900 - 4500K)",
+ 15: "White fluorescent (WW 3200 - 3700K)",
+ 17: "Standard light A",
+ 18: "Standard light B",
+ 19: "Standard light C",
+ 20: "D55",
+ 21: "D65",
+ 22: "D75",
+ 23: "D50",
+ 24: "ISO studio tungsten",
+ 255: "Other"
+ },
+ Flash: {
+ 0x0000: "Flash did not fire",
+ 0x0001: "Flash fired",
+ 0x0005: "Strobe return light not detected",
+ 0x0007: "Strobe return light detected",
+ 0x0009: "Flash fired, compulsory flash mode",
+ 0x000D: "Flash fired, compulsory flash mode, return light not detected",
+ 0x000F: "Flash fired, compulsory flash mode, return light detected",
+ 0x0010: "Flash did not fire, compulsory flash mode",
+ 0x0018: "Flash did not fire, auto mode",
+ 0x0019: "Flash fired, auto mode",
+ 0x001D: "Flash fired, auto mode, return light not detected",
+ 0x001F: "Flash fired, auto mode, return light detected",
+ 0x0020: "No flash function",
+ 0x0041: "Flash fired, red-eye reduction mode",
+ 0x0045: "Flash fired, red-eye reduction mode, return light not detected",
+ 0x0047: "Flash fired, red-eye reduction mode, return light detected",
+ 0x0049: "Flash fired, compulsory flash mode, red-eye reduction mode",
+ 0x004D: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
+ 0x004F: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
+ 0x0059: "Flash fired, auto mode, red-eye reduction mode",
+ 0x005D: "Flash fired, auto mode, return light not detected, red-eye reduction mode",
+ 0x005F: "Flash fired, auto mode, return light detected, red-eye reduction mode"
+ },
+ SensingMethod: {
+ 1: "Not defined",
+ 2: "One-chip color area sensor",
+ 3: "Two-chip color area sensor",
+ 4: "Three-chip color area sensor",
+ 5: "Color sequential area sensor",
+ 7: "Trilinear sensor",
+ 8: "Color sequential linear sensor"
+ },
+ SceneCaptureType: {
+ 0: "Standard",
+ 1: "Landscape",
+ 2: "Portrait",
+ 3: "Night scene"
+ },
+ SceneType: {
+ 1: "Directly photographed"
+ },
+ CustomRendered: {
+ 0: "Normal process",
+ 1: "Custom process"
+ },
+ WhiteBalance: {
+ 0: "Auto white balance",
+ 1: "Manual white balance"
+ },
+ GainControl: {
+ 0: "None",
+ 1: "Low gain up",
+ 2: "High gain up",
+ 3: "Low gain down",
+ 4: "High gain down"
+ },
+ Contrast: {
+ 0: "Normal",
+ 1: "Soft",
+ 2: "Hard"
+ },
+ Saturation: {
+ 0: "Normal",
+ 1: "Low saturation",
+ 2: "High saturation"
+ },
+ Sharpness: {
+ 0: "Normal",
+ 1: "Soft",
+ 2: "Hard"
+ },
+ SubjectDistanceRange: {
+ 0: "Unknown",
+ 1: "Macro",
+ 2: "Close view",
+ 3: "Distant view"
+ },
+ FileSource: {
+ 3: "DSC"
+ },
+
+ Components: {
+ 0: "",
+ 1: "Y",
+ 2: "Cb",
+ 3: "Cr",
+ 4: "R",
+ 5: "G",
+ 6: "B"
+ }
+};
+
+function addEvent(element, event, handler) {
+ if (element.addEventListener) {
+ element.addEventListener(event, handler, false);
+ } else if (element.attachEvent) {
+ element.attachEvent("on" + event, handler);
+ }
+}
+
+function imageHasData(img) {
+ return !!(img.exifdata);
+}
+
+
+function base64ToArrayBuffer(base64, contentType) {
+ contentType = contentType || base64.match(/^data\:([^\;]+)\;base64,/mi)[1] || ''; // e.g. 'data:image/jpeg;base64,...' => 'image/jpeg'
+ base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
+ var binary = atob(base64);
+ var len = binary.length;
+ var buffer = new ArrayBuffer(len);
+ var view = new Uint8Array(buffer);
+ for (var i = 0; i < len; i++) {
+ view[i] = binary.charCodeAt(i);
+ }
+ return buffer;
+}
+
+function objectURLToBlob(url, callback) {
+ var http = new XMLHttpRequest();
+ http.open("GET", url, true);
+ http.responseType = "blob";
+ http.onload = function (e) {
+ if (this.status == 200 || this.status === 0) {
+ callback(this.response);
+ }
+ };
+ http.send();
+}
+
+function getImageData(img, callback) {
+ function handleBinaryFile(binFile) {
+ var data = findEXIFinJPEG(binFile);
+ img.exifdata = data || {};
+ var iptcdata = findIPTCinJPEG(binFile);
+ img.iptcdata = iptcdata || {};
+ if (EXIF.isXmpEnabled) {
+ var xmpdata = findXMPinJPEG(binFile);
+ img.xmpdata = xmpdata || {};
+ }
+ if (callback) {
+ callback.call(img);
+ }
+ }
+
+ if (img.src) {
+ if (/^data\:/i.test(img.src)) { // Data URI
+ var arrayBuffer = base64ToArrayBuffer(img.src);
+ handleBinaryFile(arrayBuffer);
+
+ } else if (/^blob\:/i.test(img.src)) { // Object URL
+ var fileReader = new FileReader();
+ fileReader.onload = function (e) {
+ handleBinaryFile(e.target.result);
+ };
+ objectURLToBlob(img.src, function (blob) {
+ fileReader.readAsArrayBuffer(blob);
+ });
+ } else {
+ var http = new XMLHttpRequest();
+ http.onload = function () {
+ if (this.status == 200 || this.status === 0) {
+ handleBinaryFile(http.response);
+ } else {
+ throw "Could not load image";
+ }
+ http = null;
+ };
+ http.open("GET", img.src, true);
+ http.responseType = "arraybuffer";
+ http.send(null);
+ }
+ } else if (self.FileReader && (img instanceof self.Blob || img instanceof self.File)) {
+ var fileReader = new FileReader();
+ fileReader.onload = function (e) {
+ if (debug) console.log("Got file of length " + e.target.result.byteLength);
+ handleBinaryFile(e.target.result);
+ };
+
+ fileReader.readAsArrayBuffer(img);
+ }
+}
+
+function findEXIFinJPEG(file) {
+ var dataView = new DataView(file);
+
+ if (debug) console.log("Got file of length " + file.byteLength);
+ if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
+ if (debug) console.log("Not a valid JPEG");
+ return false; // not a valid jpeg
+ }
+
+ var offset = 2,
+ length = file.byteLength,
+ marker;
+
+ while (offset < length) {
+ if (dataView.getUint8(offset) != 0xFF) {
+ if (debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8(offset));
+ return false; // not a valid marker, something is wrong
+ }
+
+ marker = dataView.getUint8(offset + 1);
+ if (debug) console.log(marker);
+
+ // we could implement handling for other markers here,
+ // but we're only looking for 0xFFE1 for EXIF data
+
+ if (marker == 225) {
+ if (debug) console.log("Found 0xFFE1 marker");
+
+ return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);
+
+ // offset += 2 + file.getShortAt(offset+2, true);
+
+ } else {
+ offset += 2 + dataView.getUint16(offset + 2);
+ }
+
+ }
+
+}
+
+function findIPTCinJPEG(file) {
+ var dataView = new DataView(file);
+
+ if (debug) console.log("Got file of length " + file.byteLength);
+ if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
+ if (debug) console.log("Not a valid JPEG");
+ return false; // not a valid jpeg
+ }
+
+ var offset = 2,
+ length = file.byteLength;
+
+
+ var isFieldSegmentStart = function (dataView, offset) {
+ return (
+ dataView.getUint8(offset) === 0x38 &&
+ dataView.getUint8(offset + 1) === 0x42 &&
+ dataView.getUint8(offset + 2) === 0x49 &&
+ dataView.getUint8(offset + 3) === 0x4D &&
+ dataView.getUint8(offset + 4) === 0x04 &&
+ dataView.getUint8(offset + 5) === 0x04
+ );
+ };
+
+ while (offset < length) {
+
+ if (isFieldSegmentStart(dataView, offset)) {
+
+ // Get the length of the name header (which is padded to an even number of bytes)
+ var nameHeaderLength = dataView.getUint8(offset + 7);
+ if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1;
+ // Check for pre photoshop 6 format
+ if (nameHeaderLength === 0) {
+ // Always 4
+ nameHeaderLength = 4;
+ }
+
+ var startOffset = offset + 8 + nameHeaderLength;
+ var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength);
+
+ return readIPTCData(file, startOffset, sectionLength);
+
+ break;
+
+ }
+
+
+ // Not the marker, continue searching
+ offset++;
+
+ }
+
+}
+var IptcFieldMap = {
+ 0x78: 'caption',
+ 0x6E: 'credit',
+ 0x19: 'keywords',
+ 0x37: 'dateCreated',
+ 0x50: 'byline',
+ 0x55: 'bylineTitle',
+ 0x7A: 'captionWriter',
+ 0x69: 'headline',
+ 0x74: 'copyright',
+ 0x0F: 'category'
+};
+function readIPTCData(file, startOffset, sectionLength) {
+ var dataView = new DataView(file);
+ var data = {};
+ var fieldValue, fieldName, dataSize, segmentType, segmentSize;
+ var segmentStartPos = startOffset;
+ while (segmentStartPos < startOffset + sectionLength) {
+ if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos + 1) === 0x02) {
+ segmentType = dataView.getUint8(segmentStartPos + 2);
+ if (segmentType in IptcFieldMap) {
+ dataSize = dataView.getInt16(segmentStartPos + 3);
+ segmentSize = dataSize + 5;
+ fieldName = IptcFieldMap[segmentType];
+ fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize);
+ // Check if we already stored a value with this name
+ if (data.hasOwnProperty(fieldName)) {
+ // Value already stored with this name, create multivalue field
+ if (data[fieldName] instanceof Array) {
+ data[fieldName].push(fieldValue);
+ }
+ else {
+ data[fieldName] = [data[fieldName], fieldValue];
+ }
+ }
+ else {
+ data[fieldName] = fieldValue;
+ }
+ }
+
+ }
+ segmentStartPos++;
+ }
+ return data;
+}
+
+function readTags(file, tiffStart, dirStart, strings, bigEnd) {
+ var entries = file.getUint16(dirStart, !bigEnd),
+ tags = {},
+ entryOffset, tag,
+ i;
+
+ for (i = 0; i < entries; i++) {
+ entryOffset = dirStart + i * 12 + 2;
+ tag = strings[file.getUint16(entryOffset, !bigEnd)];
+ if (!tag && debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd));
+ tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
+ }
+ return tags;
+}
+
+function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
+ var type = file.getUint16(entryOffset + 2, !bigEnd),
+ numValues = file.getUint32(entryOffset + 4, !bigEnd),
+ valueOffset = file.getUint32(entryOffset + 8, !bigEnd) + tiffStart,
+ offset,
+ vals, val, n,
+ numerator, denominator;
+
+ switch (type) {
+ case 1: // byte, 8-bit unsigned int
+ case 7: // undefined, 8-bit byte, value depending on field
+ if (numValues == 1) {
+ return file.getUint8(entryOffset + 8, !bigEnd);
+ } else {
+ offset = numValues > 4 ? valueOffset : (entryOffset + 8);
+ vals = [];
+ for (n = 0; n < numValues; n++) {
+ vals[n] = file.getUint8(offset + n);
+ }
+ return vals;
+ }
+
+ case 2: // ascii, 8-bit byte
+ offset = numValues > 4 ? valueOffset : (entryOffset + 8);
+ return getStringFromDB(file, offset, numValues - 1);
+
+ case 3: // short, 16 bit int
+ if (numValues == 1) {
+ return file.getUint16(entryOffset + 8, !bigEnd);
+ } else {
+ offset = numValues > 2 ? valueOffset : (entryOffset + 8);
+ vals = [];
+ for (n = 0; n < numValues; n++) {
+ vals[n] = file.getUint16(offset + 2 * n, !bigEnd);
+ }
+ return vals;
+ }
+
+ case 4: // long, 32 bit int
+ if (numValues == 1) {
+ return file.getUint32(entryOffset + 8, !bigEnd);
+ } else {
+ vals = [];
+ for (n = 0; n < numValues; n++) {
+ vals[n] = file.getUint32(valueOffset + 4 * n, !bigEnd);
+ }
+ return vals;
+ }
+
+ case 5: // rational = two long values, first is numerator, second is denominator
+ if (numValues == 1) {
+ numerator = file.getUint32(valueOffset, !bigEnd);
+ denominator = file.getUint32(valueOffset + 4, !bigEnd);
+ val = new Number(numerator / denominator);
+ val.numerator = numerator;
+ val.denominator = denominator;
+ return val;
+ } else {
+ vals = [];
+ for (n = 0; n < numValues; n++) {
+ numerator = file.getUint32(valueOffset + 8 * n, !bigEnd);
+ denominator = file.getUint32(valueOffset + 4 + 8 * n, !bigEnd);
+ vals[n] = new Number(numerator / denominator);
+ vals[n].numerator = numerator;
+ vals[n].denominator = denominator;
+ }
+ return vals;
+ }
+
+ case 9: // slong, 32 bit signed int
+ if (numValues == 1) {
+ return file.getInt32(entryOffset + 8, !bigEnd);
+ } else {
+ vals = [];
+ for (n = 0; n < numValues; n++) {
+ vals[n] = file.getInt32(valueOffset + 4 * n, !bigEnd);
+ }
+ return vals;
+ }
+
+ case 10: // signed rational, two slongs, first is numerator, second is denominator
+ if (numValues == 1) {
+ return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset + 4, !bigEnd);
+ } else {
+ vals = [];
+ for (n = 0; n < numValues; n++) {
+ vals[n] = file.getInt32(valueOffset + 8 * n, !bigEnd) / file.getInt32(valueOffset + 4 + 8 * n, !bigEnd);
+ }
+ return vals;
+ }
+ }
+}
+
+/**
+* Given an IFD (Image File Directory) start offset
+* returns an offset to next IFD or 0 if it's the last IFD.
+*/
+function getNextIFDOffset(dataView, dirStart, bigEnd) {
+ //the first 2bytes means the number of directory entries contains in this IFD
+ var entries = dataView.getUint16(dirStart, !bigEnd);
+
+ // After last directory entry, there is a 4bytes of data,
+ // it means an offset to next IFD.
+ // If its value is '0x00000000', it means this is the last IFD and there is no linked IFD.
+
+ return dataView.getUint32(dirStart + 2 + entries * 12, !bigEnd); // each entry is 12 bytes long
+}
+
+function readThumbnailImage(dataView, tiffStart, firstIFDOffset, bigEnd) {
+ // get the IFD1 offset
+ var IFD1OffsetPointer = getNextIFDOffset(dataView, tiffStart + firstIFDOffset, bigEnd);
+
+ if (!IFD1OffsetPointer) {
+ // console.log('******** IFD1Offset is empty, image thumb not found ********');
+ return {};
+ }
+ else if (IFD1OffsetPointer > dataView.byteLength) { // this should not happen
+ // console.log('******** IFD1Offset is outside the bounds of the DataView ********');
+ return {};
+ }
+ // console.log('******* thumbnail IFD offset (IFD1) is: %s', IFD1OffsetPointer);
+
+ var thumbTags = readTags(dataView, tiffStart, tiffStart + IFD1OffsetPointer, IFD1Tags, bigEnd)
+
+ // EXIF 2.3 specification for JPEG format thumbnail
+
+ // If the value of Compression(0x0103) Tag in IFD1 is '6', thumbnail image format is JPEG.
+ // Most of Exif image uses JPEG format for thumbnail. In that case, you can get offset of thumbnail
+ // by JpegIFOffset(0x0201) Tag in IFD1, size of thumbnail by JpegIFByteCount(0x0202) Tag.
+ // Data format is ordinary JPEG format, starts from 0xFFD8 and ends by 0xFFD9. It seems that
+ // JPEG format and 160x120pixels of size are recommended thumbnail format for Exif2.1 or later.
+
+ if (thumbTags['Compression']) {
+ // console.log('Thumbnail image found!');
+
+ switch (thumbTags['Compression']) {
+ case 6:
+ // console.log('Thumbnail image format is JPEG');
+ if (thumbTags.JpegIFOffset && thumbTags.JpegIFByteCount) {
+ // extract the thumbnail
+ var tOffset = tiffStart + thumbTags.JpegIFOffset;
+ var tLength = thumbTags.JpegIFByteCount;
+ thumbTags['blob'] = new Blob([new Uint8Array(dataView.buffer, tOffset, tLength)], {
+ type: 'image/jpeg'
+ });
+ }
+ break;
+
+ case 1:
+ console.log("Thumbnail image format is TIFF, which is not implemented.");
+ break;
+ default:
+ console.log("Unknown thumbnail image format '%s'", thumbTags['Compression']);
+ }
+ }
+ else if (thumbTags['PhotometricInterpretation'] == 2) {
+ console.log("Thumbnail image format is RGB, which is not implemented.");
+ }
+ return thumbTags;
+}
+
+function getStringFromDB(buffer, start, length) {
+ var outstr = "";
+ for (var n = start; n < start + length; n++) {
+ outstr += String.fromCharCode(buffer.getUint8(n));
+ }
+ return outstr;
+}
+
+function readEXIFData(file, start) {
+ if (getStringFromDB(file, start, 4) != "Exif") {
+ if (debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4));
+ return false;
+ }
+
+ var bigEnd,
+ tags, tag,
+ exifData, gpsData,
+ tiffOffset = start + 6;
+
+ // test for TIFF validity and endianness
+ if (file.getUint16(tiffOffset) == 0x4949) {
+ bigEnd = false;
+ } else if (file.getUint16(tiffOffset) == 0x4D4D) {
+ bigEnd = true;
+ } else {
+ if (debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
+ return false;
+ }
+
+ if (file.getUint16(tiffOffset + 2, !bigEnd) != 0x002A) {
+ if (debug) console.log("Not valid TIFF data! (no 0x002A)");
+ return false;
+ }
+
+ var firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd);
+
+ if (firstIFDOffset < 0x00000008) {
+ if (debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset + 4, !bigEnd));
+ return false;
+ }
+
+ tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd);
+
+ if (tags.ExifIFDPointer) {
+ exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
+ for (tag in exifData) {
+ switch (tag) {
+ case "LightSource":
+ case "Flash":
+ case "MeteringMode":
+ case "ExposureProgram":
+ case "SensingMethod":
+ case "SceneCaptureType":
+ case "SceneType":
+ case "CustomRendered":
+ case "WhiteBalance":
+ case "GainControl":
+ case "Contrast":
+ case "Saturation":
+ case "Sharpness":
+ case "SubjectDistanceRange":
+ case "FileSource":
+ exifData[tag] = StringValues[tag][exifData[tag]];
+ break;
+
+ case "ExifVersion":
+ case "FlashpixVersion":
+ exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
+ break;
+
+ case "ComponentsConfiguration":
+ exifData[tag] =
+ StringValues.Components[exifData[tag][0]] +
+ StringValues.Components[exifData[tag][1]] +
+ StringValues.Components[exifData[tag][2]] +
+ StringValues.Components[exifData[tag][3]];
+ break;
+ }
+ tags[tag] = exifData[tag];
+ }
+ }
+
+ if (tags.GPSInfoIFDPointer) {
+ gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
+ for (tag in gpsData) {
+ switch (tag) {
+ case "GPSVersionID":
+ gpsData[tag] = gpsData[tag][0] +
+ "." + gpsData[tag][1] +
+ "." + gpsData[tag][2] +
+ "." + gpsData[tag][3];
+ break;
+ }
+ tags[tag] = gpsData[tag];
+ }
+ }
+
+ // extract thumbnail
+ tags['thumbnail'] = readThumbnailImage(file, tiffOffset, firstIFDOffset, bigEnd);
+
+ return tags;
+}
+
+function findXMPinJPEG(file) {
+
+ if (!('DOMParser' in self)) {
+ // console.warn('XML parsing not supported without DOMParser');
+ return;
+ }
+ var dataView = new DataView(file);
+
+ if (debug) console.log("Got file of length " + file.byteLength);
+ if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
+ if (debug) console.log("Not a valid JPEG");
+ return false; // not a valid jpeg
+ }
+
+ var offset = 2,
+ length = file.byteLength,
+ dom = new DOMParser();
+
+ while (offset < (length - 4)) {
+ if (getStringFromDB(dataView, offset, 4) == "http") {
+ var startOffset = offset - 1;
+ var sectionLength = dataView.getUint16(offset - 2) - 1;
+ var xmpString = getStringFromDB(dataView, startOffset, sectionLength)
+ var xmpEndIndex = xmpString.indexOf('xmpmeta>') + 8;
+ xmpString = xmpString.substring(xmpString.indexOf('
0) {
+ json['@attributes'] = {};
+ for (var j = 0; j < xml.attributes.length; j++) {
+ var attribute = xml.attributes.item(j);
+ json['@attributes'][attribute.nodeName] = attribute.nodeValue;
+ }
+ }
+ } else if (xml.nodeType == 3) { // text node
+ return xml.nodeValue;
+ }
+
+ // deal with children
+ if (xml.hasChildNodes()) {
+ for (var i = 0; i < xml.childNodes.length; i++) {
+ var child = xml.childNodes.item(i);
+ var nodeName = child.nodeName;
+ if (json[nodeName] == null) {
+ json[nodeName] = xml2json(child);
+ } else {
+ if (json[nodeName].push == null) {
+ var old = json[nodeName];
+ json[nodeName] = [];
+ json[nodeName].push(old);
+ }
+ json[nodeName].push(xml2json(child));
+ }
+ }
+ }
+
+ return json;
+}
+
+function xml2Object(xml) {
+ try {
+ var obj = {};
+ if (xml.children.length > 0) {
+ for (var i = 0; i < xml.children.length; i++) {
+ var item = xml.children.item(i);
+ var attributes = item.attributes;
+ for (var idx in attributes) {
+ var itemAtt = attributes[idx];
+ var dataKey = itemAtt.nodeName;
+ var dataValue = itemAtt.nodeValue;
+
+ if (dataKey !== undefined) {
+ obj[dataKey] = dataValue;
+ }
+ }
+ var nodeName = item.nodeName;
+
+ if (typeof (obj[nodeName]) == "undefined") {
+ obj[nodeName] = xml2json(item);
+ } else {
+ if (typeof (obj[nodeName].push) == "undefined") {
+ var old = obj[nodeName];
+
+ obj[nodeName] = [];
+ obj[nodeName].push(old);
+ }
+ obj[nodeName].push(xml2json(item));
+ }
+ }
+ } else {
+ obj = xml.textContent;
+ }
+ return obj;
+ } catch (e) {
+ console.log(e.message);
+ }
+}
+
+EXIF.enableXmp = function () {
+ EXIF.isXmpEnabled = true;
+}
+
+EXIF.disableXmp = function () {
+ EXIF.isXmpEnabled = false;
+}
+
+EXIF.getData = function (img, callback) {
+ if (((self.Image && img instanceof self.Image)
+ || (self.HTMLImageElement && img instanceof self.HTMLImageElement))
+ && !img.complete)
+ return false;
+
+ if (!imageHasData(img)) {
+ getImageData(img, callback);
+ } else {
+ if (callback) {
+ callback.call(img);
+ }
+ }
+ return true;
+}
+
+EXIF.getTag = function (img, tag) {
+ if (!imageHasData(img)) return;
+ return img.exifdata[tag];
+}
+
+EXIF.getIptcTag = function (img, tag) {
+ if (!imageHasData(img)) return;
+ return img.iptcdata[tag];
+}
+
+EXIF.getAllTags = function (img) {
+ if (!imageHasData(img)) return {};
+ var a,
+ data = img.exifdata,
+ tags = {};
+ for (a in data) {
+ if (data.hasOwnProperty(a)) {
+ tags[a] = data[a];
+ }
+ }
+ return tags;
+}
+
+EXIF.getAllIptcTags = function (img) {
+ if (!imageHasData(img)) return {};
+ var a,
+ data = img.iptcdata,
+ tags = {};
+ for (a in data) {
+ if (data.hasOwnProperty(a)) {
+ tags[a] = data[a];
+ }
+ }
+ return tags;
+}
+
+EXIF.pretty = function (img) {
+ if (!imageHasData(img)) return "";
+ var a,
+ data = img.exifdata,
+ strPretty = "";
+ for (a in data) {
+ if (data.hasOwnProperty(a)) {
+ if (typeof data[a] == "object") {
+ if (data[a] instanceof Number) {
+ strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n";
+ } else {
+ strPretty += a + " : [" + data[a].length + " values]\r\n";
+ }
+ } else {
+ strPretty += a + " : " + data[a] + "\r\n";
+ }
+ }
+ }
+ return strPretty;
+}
+
+EXIF.readFromBinaryFile = function (file) {
+ return findEXIFinJPEG(file);
+}
+
+export default EXIF;
diff --git a/src/helpers/markdownToText.ts b/src/helpers/markdownToText.ts
index b81d879..f4ba65d 100644
--- a/src/helpers/markdownToText.ts
+++ b/src/helpers/markdownToText.ts
@@ -1,3 +1,25 @@
+import MarkdownIt from 'markdown-it';
+const parser = new MarkdownIt();
+
export default function markdownToText(markdown: string): string {
- return markdown.replace(/#|`|\*|_|~/g, '');
+ return parser
+ .render(markdown)
+ .split('\n')
+ .map((str) => str.trim())
+ .map((str) => {
+ return str.replace(/<\/?[^>]+(>|$)/g, '').split('\n');
+ })
+ .flat()
+ .filter((str) => !str.startsWith("import")
+ && !str.startsWith("export")
+ && !str.startsWith("#")
+ && !str.startsWith("const")
+ && !str.startsWith("function")
+ && !str.startsWith("export")
+ && !str.startsWith("import")
+ && !str.startsWith("<")
+ && !str.startsWith("let")
+ && str.length > 0
+ )
+ .join(' ');
}
diff --git a/src/helpers/normalizeWheel.ts b/src/helpers/normalizeWheel.ts
new file mode 100644
index 0000000..85b65b5
--- /dev/null
+++ b/src/helpers/normalizeWheel.ts
@@ -0,0 +1,49 @@
+
+// Reasonable defaults
+var PIXEL_STEP = 10;
+var LINE_HEIGHT = 40;
+var PAGE_HEIGHT = 800;
+
+export default function normalizeWheel(/*object*/ event) /*object*/ {
+ var sX = 0, sY = 0, // spinX, spinY
+ pX = 0, pY = 0; // pixelX, pixelY
+
+ // Legacy
+ if ('detail' in event) { sY = event.detail; }
+ if ('wheelDelta' in event) { sY = -event.wheelDelta / 120; }
+ if ('wheelDeltaY' in event) { sY = -event.wheelDeltaY / 120; }
+ if ('wheelDeltaX' in event) { sX = -event.wheelDeltaX / 120; }
+
+ // side scrolling on FF with DOMMouseScroll
+ if ('axis' in event && event.axis === event.HORIZONTAL_AXIS) {
+ sX = sY;
+ sY = 0;
+ }
+
+ pX = sX * PIXEL_STEP;
+ pY = sY * PIXEL_STEP;
+
+ if ('deltaY' in event) { pY = event.deltaY; }
+ if ('deltaX' in event) { pX = event.deltaX; }
+
+ if ((pX || pY) && event.deltaMode) {
+ if (event.deltaMode == 1) { // delta in LINE units
+ pX *= LINE_HEIGHT;
+ pY *= LINE_HEIGHT;
+ } else { // delta in PAGE units
+ pX *= PAGE_HEIGHT;
+ pY *= PAGE_HEIGHT;
+ }
+ }
+
+ // Fall-back if spin cannot be determined
+ if (pX && !sX) { sX = (pX < 1) ? -1 : 1; }
+ if (pY && !sY) { sY = (pY < 1) ? -1 : 1; }
+
+ return {
+ spinX: sX,
+ spinY: sY,
+ pixelX: pX,
+ pixelY: pY
+ };
+}
diff --git a/src/i18n/ui.ts b/src/i18n/ui.ts
index 60d81a7..d5732f4 100644
--- a/src/i18n/ui.ts
+++ b/src/i18n/ui.ts
@@ -10,6 +10,11 @@ export const ui = {
en: {
"en": "English",
"de": "Deutsch",
+ "more-posts": "More Posts",
+ "more-projects": "More Projects",
+ "read-more": "Read More",
+ "latest-posts": "Latest Posts",
+ "latest-projects": "Latest Projects",
'home.title': 'Hi, I’m Max :)',
'home.subtitle': 'Trained Media Designer, Blender Nerd, Developer and Hardware Tinkerer.',
'nav.blog': 'Blog',
@@ -20,6 +25,11 @@ export const ui = {
de: {
"en": "English",
"de": "Deutsch",
+ "more-posts": "Mehr Posts",
+ "more-projects": "Mehr Projekte",
+ "latest-posts": "Neueste Posts",
+ "latest-projects": "Neueste Projekte",
+ "read-more": "weiterlesen",
'home.title': 'Hi, ich bin Max :)',
'home.subtitle': 'Ausgebildeter Mediengestalter, Blender Nerd, Entwickler und Hardware Bastler.',
'nav.blog': 'Blog',
diff --git a/src/i18n/utils.ts b/src/i18n/utils.ts
index d4f7fbf..f5c4c1c 100644
--- a/src/i18n/utils.ts
+++ b/src/i18n/utils.ts
@@ -29,5 +29,7 @@ export function filterCollection(collection: T[], loca
return collection.filter(post => {
const [_, lang] = parseSlug(post?.id);
return lang === locale;
+ }).sort((a, b) => {
+ return a.data.date > b.data.date ? -1 : 1;
});
}
diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro
index ad4ed71..bc56050 100644
--- a/src/layouts/Layout.astro
+++ b/src/layouts/Layout.astro
@@ -72,7 +72,7 @@ const { title, width = "compact" } = Astro.props;
-
+
diff --git a/src/layouts/Post.astro b/src/layouts/Post.astro
index 3b2e945..5823920 100644
--- a/src/layouts/Post.astro
+++ b/src/layouts/Post.astro
@@ -2,7 +2,6 @@
import type { CollectionEntry } from "astro:content";
import Layout from "./Layout.astro";
import { useTranslatedPath } from "@i18n/utils";
-import { getLocale } from "astro-i18n-aut";
type CustomProps = {
layout?: "normal" | "transparent";
@@ -10,52 +9,52 @@ type CustomProps = {
};
type Props = CollectionEntry<"blog">["data"] & CustomProps;
-const { title, date, _layout = "normal", backlink = "/blog" } = Astro.props;
+const { title, date, _layout, backlink = "/blog" } = Astro.props;
const path = useTranslatedPath(Astro);
---
-
-
-
← overview
-
- {
- date.toLocaleString("en-US", {
- month: "long",
- day: "numeric",
- year: "numeric",
- })
- }
-
+
+
overview
+
+ {
+ date.toLocaleString("en-US", {
+ month: "long",
+ day: "numeric",
+ year: "numeric",
+ })
+ }
-
{title}
+
+
+ {title}
diff --git a/src/pages/blog/index.astro b/src/pages/blog/index.astro
index 3908f90..a7745bd 100644
--- a/src/pages/blog/index.astro
+++ b/src/pages/blog/index.astro
@@ -2,6 +2,8 @@
import { getCollection } from "astro:content";
const pages = await getCollection("blog");
import Layout from "@layouts/Layout.astro";
+import HeroCard from "@components/HeroCard.astro";
+import SmallCard from "@components/SmallCard.astro";
import { getLocale } from "astro-i18n-aut";
import { filterCollection } from "@i18n/utils";
@@ -9,17 +11,29 @@ import { filterCollection } from "@i18n/utils";
const locale = getLocale(Astro.url);
const posts = filterCollection(pages, locale);
+
+const featuredPosts = await Promise.all(
+ posts.slice(0, 3).map(async (post) => {
+ if (!post.data.headerImg) {
+ return post;
+ }
+ const { default: image } = await import(
+ `../../content/blog/${post.slug.split("/")[0]}/${post.data.headerImg}`
+ );
+ return {
+ ...post,
+ image,
+ };
+ }),
+);
+
+const otherPosts = posts.slice(3);
---
-
- {
- posts.map((post) => (
- <>
- {post.data.title}
-
- >
- ))
- }
-
+ {featuredPosts.map((post) => )}
+
+
+ {otherPosts.map((post) => )}
+
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 4ec2523..333478e 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -1,54 +1,71 @@
---
import Layout from "@layouts/Layout.astro";
import Max from "@components/Max.astro";
-import { Card } from "@components/card";
import { getCollection } from "astro:content";
-import { filterCollection, useTranslatedPath } from "@i18n/utils";
-import markdownToText from "@helpers/markdownToText";
+import {
+ filterCollection,
+ useTranslatedPath,
+ useTranslations,
+} from "@i18n/utils";
import { getLocale } from "astro-i18n-aut";
+import HeroCard from "@components/HeroCard.astro";
+import SmallCard from "@components/SmallCard.astro";
+import LinkCard from "@components/LinkCard.astro";
+import ArrowA from "@components/arrows/ArrowA.astro";
+import ArrowB from "@components/arrows/ArrowB.astro";
const projects = filterCollection(
await getCollection("projects"),
getLocale(Astro.url),
);
-const translatePath = useTranslatedPath(Astro);
-
-const locale = getLocale(Astro.url);
-
-const projectSlides = await Promise.all(
- projects.map(async (project) => ({
- title: project.data.title,
- description: markdownToText(project.body),
- image: project.data.headerImg,
- link: translatePath(project.slug),
- })),
-);
+const t = useTranslations(Astro);
+const tp = useTranslatedPath(Astro);
const featuredProject = projects.find((project) => project.data?.featured);
+const otherProjects = projects
+ .filter((project) => featuredProject !== project)
+ .sort((a) => (a?.data?.icon ? -1 : 1))
+ .slice(0, 3);
+
+const posts = filterCollection(
+ await getCollection("blog"),
+ getLocale(Astro.url),
+);
+
+const featuredPost = posts.find((post) => post.data?.featured);
+const otherPosts = posts.filter((post) => featuredPost !== post).slice(0, 3);
---
- {
- featuredProject && (
-
-
- {featuredProject.data.title}
-
- {markdownToText(featuredProject.body).slice(0, 200)}
-
-
-
-
-
- )
- }
+
+
+
+ {featuredProject && }
+
+ {
+ otherProjects.length > 0 &&
+ otherProjects.map((project) => )
+ }
+
+
+
+
+
+ {featuredPost && }
+
+
+
+ {
+ otherPosts.length > 0 &&
+ otherPosts.map((post) => )
+ }
+
+
+
diff --git a/src/pages/photos/index.astro b/src/pages/photos/index.astro
index 01e45b2..2065ee5 100644
--- a/src/pages/photos/index.astro
+++ b/src/pages/photos/index.astro
@@ -3,6 +3,7 @@ import { getCollection } from "astro:content";
import Layout from "@layouts/Layout.astro";
import { getLocale } from "astro-i18n-aut";
import { filterCollection } from "@i18n/utils";
+import HeroCard from "@components/HeroCard.astro";
const locale = getLocale(Astro.url);
const pages = await getCollection("photos");
@@ -10,16 +11,5 @@ const posts = filterCollection(pages, locale);
---
-
- {
- posts.map((post) => (
- <>
- <>
- {post.data.title}
-
- >
- >
- ))
- }
-
+ {posts.map((post) => )}
diff --git a/src/pages/projects/index.astro b/src/pages/projects/index.astro
index a04a3fc..715e2d3 100644
--- a/src/pages/projects/index.astro
+++ b/src/pages/projects/index.astro
@@ -3,6 +3,7 @@ import { getCollection } from "astro:content";
import Layout from "@layouts/Layout.astro";
import { getLocale } from "astro-i18n-aut";
import { filterCollection } from "@i18n/utils";
+import HeroCard from "@components/HeroCard.astro";
const locale = getLocale(Astro.url);
const pages = await getCollection("projects");
@@ -10,16 +11,5 @@ const posts = filterCollection(pages, locale);
---
-
- {
- posts.map((post) => (
- <>
- <>
- {post.data.title}
-
- >
- >
- ))
- }
-
+ {posts.map((post) => )}
diff --git a/uno.config.ts b/uno.config.ts
index e31cc1c..311da6c 100644
--- a/uno.config.ts
+++ b/uno.config.ts
@@ -12,8 +12,8 @@ export default defineConfig({
"bg": ['bg-neutral-000', "dark:bg-neutral-500"],
"bg-light": ['bg-neutral-100', "dark:bg-neutral-400"],
"text-neutral": ['text-neutral-900', "dark:text-neutral-100"],
- "border-neutral": ['border-neutral-300', "dark:border-neutral-1000"],
- "border-light": "border-neutral-200 dark:border-neutral-500",
+ "border-neutral": "border-neutral-300 dark:border-neutral-1000",
+ "border-light": "border-neutral-100 dark:border-neutral-500",
"divide-x-neutral": ['divide-x-neutral-300', "dark:divide-x-neutral-1000"],
"gradient": "bg-gradient-to-br from-neutral-000 dark:from-neutral-500 to-neutral-200 dark:to-neutral-800",
},
@@ -24,6 +24,7 @@ export default defineConfig({
},
colors: {
neutral: {
+ "000": "var(--neutral-000)",
"100": "var(--neutral-100)",
"200": "var(--neutral-200)",
"300": "var(--neutral-300)",