@@ -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)
-
+
-![](./images/Poster_var2.jpg)
+
## Super old renders:
-![](./images/Cabin_old.jpg)
+
-![](./images/Cabin_new.png)
+
-![](./images/Home.png)
+
+
+
-![](./images/Workroom.png)
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 f9d5c17..dd3d292 100644
--- a/src/content/photos/bigge-changes/index.mdx
+++ b/src/content/photos/bigge-changes/index.mdx
@@ -1,7 +1,7 @@
---
title: "Bilder Bigge Changes"
date: 2020-11-23
-headerImg: "images/Image6.jpg"
+headerImg: "images/6_1.jpg"
---
import Image from "@components/Image.astro"
@@ -21,22 +21,13 @@ Der Sommer '18 war einer der trockensten der letzten Jahre _(irgendwie scheint d
Zurück zur Trockenheit, durch den fehlenden Regen ist der Zulauf der Bigge fast komplett versiegt; und so war der See bist fast auf den Grund ausgetrocknet. Das war ein seltsamer trauriger und schöner Anblick zugleich. Im Laufe der Zeit habe ich viele Bilder gemacht, viele auch bei einer Wanderung mit meinem Papa zusammen.
-
-
-
-
-
-
-
-
-
diff --git a/src/content/photos/bilder-caen-ogrove/index.mdx b/src/content/photos/bilder-caen-ogrove/index.mdx
index 9e99a82..2fce26c 100644
--- a/src/content/photos/bilder-caen-ogrove/index.mdx
+++ b/src/content/photos/bilder-caen-ogrove/index.mdx
@@ -1,10 +1,23 @@
---
title: "Bilder Caen-OGrove"
date: 2017-04-07
-headerImg: "images/Thumb.jpg"
+headerImg: "images/HameuxDeLaMer_03.jpg"
---
import Image from "@components/Image.astro"
+
+
+import Zumaia_01 from "./images/Zumaia_01.jpg";
+import HameuxDeLaMer_04 from "./images/HameuxDeLaMer_04.jpg";
+import Barqueiro_01 from "./images/Barqueiro_01.jpg";
+import untitled_panorama1 from "./images/untitled_panorama1.jpg";
+import untitled_panorama5 from "./images/untitled_panorama5.jpg";
+import pointedupenhir_02 from "./images/pointedupenhir_02.jpg";
+import Lannion_01 from "./images/Lannion_01.jpg";
+import untitled_panorama2 from "./images/untitled_panorama2.jpg";
+import HameuxDeLaMer_03 from "./images/HameuxDeLaMer_03.jpg";
+import PointeDuPenhir_01 from "./images/PointeDuPenhir_01.jpg";
+import RandomHotel from "./images/RandomHotel.jpg";
import SoulacSurMer01 from "./images/SoulacSurMer_01.jpg"
import UntitledChairs1 from "./images/Untitled_Chairs1.jpg"
import Untitled_Panorama10 from "./images/Untitled_Panorama10.jpg"
@@ -25,18 +38,6 @@ Ich fand die Fassade amüsierend, da sie offensichtlich gedacht ist um dem Kohle
Zumaia in Spanien, die Felsformation heißt Flysch und angeblich können Geologen hier das Aussterben der durch einen Meteorit begründen, und die Erderwärmung in ganz großen Masstab verfolgen. Außerdem soll hier das Intro für die neue Staffel Game of Thrones gedreht worden sein.
-import Zumaia_01 from "./images/Zumaia_01.jpg";
-import HameuxDeLaMer_04 from "./images/HameuxDeLaMer_04.jpg";
-import Barqueiro_01 from "./images/Barqueiro_01.jpg";
-import untitled_panorama1 from "./images/untitled_panorama1.jpg";
-import untitled_panorama5 from "./images/untitled_panorama5.jpg";
-import pointedupenhir_02 from "./images/pointedupenhir_02.jpg";
-import Lannion_01 from "./images/Lannion_01.jpg";
-import untitled_panorama2 from "./images/untitled_panorama2.jpg";
-import HameuxDeLaMer_03 from "./images/HameuxDeLaMer_03.jpg";
-import PointeDuPenhir_01 from "./images/PointeDuPenhir_01.jpg";
-import RandomHotel from "./images/RandomHotel.jpg";
-
Eigentlich gehört das Foto eher in den letzten Blogbeitrag aber irgendwie habe ich es damals nicht dazugefügt. Die Nachbearbeitung ist sehr von dem MadMax Grading inspiriert. Der Ort ist Les Hameux de la Mer ein super schöner Ort.
diff --git a/src/content/photos/erasmus-valencia/index.en.mdx b/src/content/photos/erasmus-valencia/index.en.mdx
index 400603c..fc4efd3 100644
--- a/src/content/photos/erasmus-valencia/index.en.mdx
+++ b/src/content/photos/erasmus-valencia/index.en.mdx
@@ -1,85 +1,87 @@
---
title: "Erasmus Valencia"
date: 2022-09-02
+headerImg: "images/MAX_8218 - MAX_8230.jpg"
---
-import Image from "@components/Image.astro";
+import Image from "@components/Image.astro"
+import MAX_8126 from "./images/MAX_8126.jpg"
+import MAX_8133 from "./images/MAX_8133.jpg"
+import MAX_8154 from "./images/MAX_8154.jpg"
+import MAX_8194 from "./images/MAX_8194.jpg"
+import MAX_8416_NEF_shotwell from "./images/MAX_8416_NEF_shotwell.jpg"
+import MAX_8197_NEF_shotwell from "./images/MAX_8197_NEF_shotwell.jpg"
+import MAX_8212_NEF_shotwell from "./images/MAX_8212_NEF_shotwell.jpg"
+import MAX_8404_NEF_shotwell from "./images/MAX_8404_NEF_shotwell.jpg"
+import MAX_8249 from "./images/MAX_8249.jpg"
+import MAX_8310_NEF_shotwell from "./images/MAX_8310_NEF_shotwell.jpg"
+import MAX_8342 from "./images/MAX_8342.jpg"
+import MAX_8360 from "./images/MAX_8360.jpg"
+import MAX_8361 from "./images/MAX_8361.jpg"
+import MAX_8366 from "./images/MAX_8366.jpg"
+import MAX_8386 from "./images/MAX_8386.jpg"
+import MAX_8396 from "./images/MAX_8396.jpg"
+import MAX_8551_NEF_shotwell from "./images/MAX_8551_NEF_shotwell.jpg";
+import MAX_8218_MAX_8230 from "./images/MAX_8218 - MAX_8230.jpg";
+import MAX_8276 from "./images/MAX_8276.jpg";
+import MAX_8426 from "./images/MAX_8426.jpg";
+import MAX_8527 from "./images/MAX_8527.jpg";
+import MAX_8547_NEF_shotwell from "./images/MAX_8547_NEF_shotwell.jpg";
+import MAX_8561 from "./images/MAX_8561.jpg";
+import MAX_8570 from "./images/MAX_8570.jpg";
+import MAX_8582 from "./images/MAX_8582.jpg";
+import MAX_8781 from "./images/MAX_8781.jpg";
+import MAX_8791 from "./images/MAX_8791.jpg";
+import MAX_8794 from "./images/MAX_8794.jpg";
+import MAX_8887 from "./images/MAX_8887.jpg";
+import Panorama_01 from "./images/Panorama_01.jpg";
+import Panorama_02 from "./images/Panorama_02.jpg";
In 2022 I had the privilege of studying in Valencia for 5 months with the support of Erasmus, here are the pictures that were taken.
## Hiking in Chullila
-import MAX_8126 from "./images/MAX_8126.jpg";
-
-import MAX_8133 from "./images/MAX_8133.jpg";
-
-import MAX_8154 from "./images/MAX_8154.jpg";
-
+
+
+
## Valencia Harbour
-import MAX_8194 from "./images/MAX_8194.jpg";
-
-import MAX_8416_NEF_shotwell from "./images/MAX_8416_NEF_shotwell.jpg";
-
+
+
## Dogs
-import MAX_8197_NEF_shotwell from "./images/MAX_8197_NEF_shotwell.jpg";
-
-import MAX_8212_NEF_shotwell from "./images/MAX_8212_NEF_shotwell.jpg";
-
-import MAX_8404_NEF_shotwell from "./images/MAX_8404_NEF_shotwell.jpg";
-
+
+
+
## Botanical
-import MAX_8249 from "./images/MAX_8249.jpg";
-
-import MAX_8310_NEF_shotwell from "./images/MAX_8310_NEF_shotwell.jpg";
-
-import MAX_8342 from "./images/MAX_8342.jpg";
-
-import MAX_8360 from "./images/MAX_8360.jpg";
-
-import MAX_8361 from "./images/MAX_8361.jpg";
-
-import MAX_8366 from "./images/MAX_8366.jpg";
-
-import MAX_8386 from "./images/MAX_8386.jpg";
-
-import MAX_8396 from "./images/MAX_8396.jpg";
-
-import MAX_8551_NEF_shotwell from "./images/MAX_8551_NEF_shotwell.jpg";
-
+
+
+
+
+
+
+
+
+
+
+## Landscapes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-## landscapes
-import MAX_8218_MAX_8230 from "./images/MAX_8218 - MAX_8230.jpg";
-
-import MAX_8276 from "./images/MAX_8276.jpg";
-
-import MAX_8426 from "./images/MAX_8426.jpg";
-
-import MAX_8527 from "./images/MAX_8527.jpg";
-
-import MAX_8547_NEF_shotwell from "./images/MAX_8547_NEF_shotwell.jpg";
-
-import MAX_8561 from "./images/MAX_8561.jpg";
-
-import MAX_8570 from "./images/MAX_8570.jpg";
-
-import MAX_8582 from "./images/MAX_8582.jpg";
-
-import MAX_8781 from "./images/MAX_8781.jpg";
-
-import MAX_8791 from "./images/MAX_8791.jpg";
-
-import MAX_8794 from "./images/MAX_8794.jpg";
-
-import MAX_8887 from "./images/MAX_8887.jpg";
-
-import Panorama_01 from "./images/Panorama_01.jpg";
-
-import Panorama_02 from "./images/Panorama_02.jpg";
-
diff --git a/src/content/photos/erasmus-valencia/index.mdx b/src/content/photos/erasmus-valencia/index.mdx
index 66eb11d..3eea0f9 100644
--- a/src/content/photos/erasmus-valencia/index.mdx
+++ b/src/content/photos/erasmus-valencia/index.mdx
@@ -41,7 +41,6 @@ import Panorama_02 from "./images/Panorama_02.jpg";
## Wandern in Chullila
-
@@ -70,19 +69,18 @@ import Panorama_02 from "./images/Panorama_02.jpg";
## Landschaften
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/content/photos/point-de-lisle/index.en.mdx b/src/content/photos/point-de-lisle/index.en.mdx
index c509e9d..5900101 100644
--- a/src/content/photos/point-de-lisle/index.en.mdx
+++ b/src/content/photos/point-de-lisle/index.en.mdx
@@ -7,10 +7,7 @@ headerImg: "images/PointDeLIsle.jpg"
import Image from "@components/Image.astro";
import PointDeLIsle from "./images/PointDeLIsle.jpg";
-import svelte from "./images/svelte.jpg";
This is currently my absolute favourite picture. The weather was especially interesting this morning, fast clouds sometimes the sunlight breaking through forming spots of light running across the ocean. In stark contrast to the last days, which where just sunshine a pleasant change. I find it very hard to capture such a dramatic lighting in a way that doesnt make it look dull and lose all its depth. So i tried to edit this picture just in B/W, my poor laptop really had to work hard because the picture is with 18000x8000 Pixel the largest one i took. But i am very happy with the result.
-
-
diff --git a/src/content/photos/point-de-lisle/index.mdx b/src/content/photos/point-de-lisle/index.mdx
index 05e0180..9ed5f68 100644
--- a/src/content/photos/point-de-lisle/index.mdx
+++ b/src/content/photos/point-de-lisle/index.mdx
@@ -4,13 +4,14 @@ date: 2017-03-29
headerImg: "images/PointDeLIsle.jpg"
---
+import ImageGallery from "@components/ImageGallery.svelte";
import Image from "@components/Image.astro";
import PointDeLIsle from "./images/PointDeLIsle.jpg";
-import svelte from "./images/svelte.jpg";
+
+
Das ist bis jetzt mein absolutes Lieblingsbild. Besonders an diesem morgen war dass Wetter, besonders spannend, rasend schnelle Wolken die von der Sonne durchbrochen wurden. Im Gegensatz zu den letzten Tagen voller Sonnenschein eine wilkommende Abwechslung. Ich finde es immer schwer eine dramatische Lichtstimmung so einzufangen dass sie nicht ihren Reitz verliert und "flach" wirkt. Also habe ich diesmal dass Foto einfach in schwarz weiß bearbeitet, mein Laptop musste ganz schön ächzen das Foto ist mit 18000x8000 Pixel bis jetzt mein größtes Panorama, aber mit dem Endergebnis bin ich mehr als froh.
-
diff --git a/src/content/photos/portugal-2021/index.mdx b/src/content/photos/portugal-2021/index.mdx
index cdd4c31..f5c0f8e 100644
--- a/src/content/photos/portugal-2021/index.mdx
+++ b/src/content/photos/portugal-2021/index.mdx
@@ -5,42 +5,44 @@ headerImg: "images/Unbenanntes_Panorama-3.jpg"
---
import Image from "@components/Image.astro";
-
+import ImageGallery from "@components/ImageGallery.svelte";
import MAX_6400 from "./images/MAX_6400.jpg";
-
-
import MAX_6406 from "./images/MAX_6406.jpg";
-
+import MAX_6529 from "./images/MAX_6529.jpg";
+import MAX_6530 from "./images/MAX_6530.jpg";
+import MAX_6536 from "./images/MAX_6536.jpg";
+import MAX_6538 from "./images/MAX_6538.jpg";
+import MAX_6550 from "./images/MAX_6550.jpg";
+import MAX_6552 from "./images/MAX_6552.jpg";
+import MAX_6553 from "./images/MAX_6553.jpg";
+import MAX_6554 from "./images/MAX_6554.jpg";
+import MAX_6556 from "./images/MAX_6556.jpg";
+import MAX_6571 from "./images/MAX_6571.jpg";
+import Unbenanntes_Panorama_1 from "./images/Unbenanntes_Panorama-1.jpg";
+import Unbenanntes_Panorama_2 from "./images/Unbenanntes_Panorama-2.jpg";
+import Unbenanntes_Panorama_3 from "./images/Unbenanntes_Panorama-3.jpg";
+import Unbenanntes_Panorama_6 from "./images/Unbenanntes_Panorama-6.jpg";
+
+
+
+
+
## Plants
-
-import MAX_6529 from "./images/MAX_6529.jpg";
-
-import MAX_6530 from "./images/MAX_6530.jpg";
-
-import MAX_6536 from "./images/MAX_6536.jpg";
-
-import MAX_6538 from "./images/MAX_6538.jpg";
-
-import MAX_6550 from "./images/MAX_6550.jpg";
-
-import MAX_6552 from "./images/MAX_6552.jpg";
-
-import MAX_6553 from "./images/MAX_6553.jpg";
-
-import MAX_6554 from "./images/MAX_6554.jpg";
-
-import MAX_6556 from "./images/MAX_6556.jpg";
-
+
+
+
+
+
+
+
+
+
## Panoramas
-
-import MAX_6571 from "./images/MAX_6571.jpg";
-
-import Unbenanntes_Panorama_1 from "./images/Unbenanntes_Panorama-1.jpg";
-
-import Unbenanntes_Panorama_3 from "./images/Unbenanntes_Panorama-3.jpg";
-
-import Unbenanntes_Panorama_6 from "./images/Unbenanntes_Panorama-6.jpg";
-
+
+
+
+
+
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)",