Convert projects to content collections
This commit is contained in:
parent
c7e8edfbf4
commit
b20139cb60
35 changed files with 402 additions and 472 deletions
143
src/content.config.ts
Normal file
143
src/content.config.ts
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { defineCollection, reference } from 'astro:content';
|
||||||
|
import type { SchemaContext } from 'astro:content';
|
||||||
|
import { glob } from 'astro/loaders';
|
||||||
|
import { z } from 'astro/zod';
|
||||||
|
|
||||||
|
function isRootRelativeUrl(url: string) {
|
||||||
|
return url.startsWith('/') && !url.startsWith('//');
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkUrl = z.string().refine(
|
||||||
|
(url) => {
|
||||||
|
if (isRootRelativeUrl(url)) return true;
|
||||||
|
try {
|
||||||
|
const parsed = new URL(url);
|
||||||
|
return ['https:', 'mailto:'].includes(parsed.protocol);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ message: 'URL must be an absolute https/mailto URL or a root-relative path.' }
|
||||||
|
);
|
||||||
|
|
||||||
|
const mediaUrl = z.string().refine(
|
||||||
|
(url) => {
|
||||||
|
if (isRootRelativeUrl(url)) return true;
|
||||||
|
try {
|
||||||
|
return new URL(url).protocol === 'https:';
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ message: 'Media URL must be an absolute https URL or a root-relative path.' }
|
||||||
|
);
|
||||||
|
|
||||||
|
const linkSchema = z.object({
|
||||||
|
label: z.string(),
|
||||||
|
url: linkUrl,
|
||||||
|
download: z.boolean().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const thumbnailSchema = ({ image }: SchemaContext) =>
|
||||||
|
z.object({
|
||||||
|
src: image(),
|
||||||
|
alt: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mediaSchema = ({ image }: SchemaContext) =>
|
||||||
|
z
|
||||||
|
.discriminatedUnion('type', [
|
||||||
|
z.object({
|
||||||
|
type: z.enum(['image', 'diagram']),
|
||||||
|
src: image(),
|
||||||
|
alt: z.string().optional(),
|
||||||
|
decorative: z.boolean().optional(),
|
||||||
|
caption: z.string().optional(),
|
||||||
|
transcript: z.string().optional(),
|
||||||
|
}),
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
type: z.literal('video'),
|
||||||
|
poster: image().optional(),
|
||||||
|
mp4: mediaUrl.optional(),
|
||||||
|
webm: mediaUrl.optional(),
|
||||||
|
captions: mediaUrl.optional(),
|
||||||
|
captionsLabel: z.string().default('English captions'),
|
||||||
|
alt: z.string().optional(),
|
||||||
|
decorative: z.boolean().optional(),
|
||||||
|
caption: z.string().optional(),
|
||||||
|
transcript: z.string().optional(),
|
||||||
|
})
|
||||||
|
.refine((item) => Boolean(item.mp4) || Boolean(item.webm), {
|
||||||
|
message: 'Video media needs at least one mp4 or webm source.',
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
.refine((item) => item.decorative || (Boolean(item.alt) && Boolean(item.caption)), {
|
||||||
|
message: 'Meaningful media needs both alt text and a caption.',
|
||||||
|
})
|
||||||
|
.refine(
|
||||||
|
(item) => item.type !== 'video' || item.decorative || Boolean(item.captions),
|
||||||
|
{
|
||||||
|
message: 'Meaningful video needs captions.',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.refine(
|
||||||
|
(item) => item.type !== 'video' || item.decorative || Boolean(item.transcript),
|
||||||
|
{
|
||||||
|
message: 'Meaningful video needs a transcript.',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const posts = defineCollection({
|
||||||
|
loader: glob({ pattern: '**/*.md', base: './src/content/posts' }),
|
||||||
|
schema: ({ image }) =>
|
||||||
|
z.object({
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string().max(160),
|
||||||
|
date: z.coerce.date(),
|
||||||
|
updated: z.coerce.date().optional(),
|
||||||
|
draft: z.boolean().default(false),
|
||||||
|
thumbnail: thumbnailSchema({ image }),
|
||||||
|
tags: z.array(
|
||||||
|
z.enum([
|
||||||
|
'ai',
|
||||||
|
'systems',
|
||||||
|
'graphics',
|
||||||
|
'simulation',
|
||||||
|
'embedded',
|
||||||
|
'web',
|
||||||
|
'tools',
|
||||||
|
'games',
|
||||||
|
])
|
||||||
|
),
|
||||||
|
featuredOrder: z.number().optional(),
|
||||||
|
projectPeriod: z.string().optional(),
|
||||||
|
role: z.string().optional(),
|
||||||
|
stack: z.array(z.string()).optional(),
|
||||||
|
scale: z.string().optional(),
|
||||||
|
outcome: z.string().optional(),
|
||||||
|
audience: z
|
||||||
|
.enum(['general', 'technical', 'recruiter-relevant'])
|
||||||
|
.default('technical'),
|
||||||
|
links: z.array(linkSchema).default([]),
|
||||||
|
media: z.array(mediaSchema({ image })).default([]),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const projects = defineCollection({
|
||||||
|
loader: glob({ pattern: '**/*.md', base: './src/content/projects' }),
|
||||||
|
schema: ({ image }) =>
|
||||||
|
z.object({
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string().max(160),
|
||||||
|
thumbnail: thumbnailSchema({ image }),
|
||||||
|
period: z.string(),
|
||||||
|
sortDate: z.coerce.date(),
|
||||||
|
technologies: z.array(z.string()).default([]),
|
||||||
|
selected: z.boolean().default(false),
|
||||||
|
essay: reference('posts').optional(),
|
||||||
|
links: z.array(linkSchema).default([]),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const collections = { posts, projects };
|
||||||
15
src/content/projects/ad-astra.md
Normal file
15
src/content/projects/ad-astra.md
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
title: Ad Astra
|
||||||
|
description: A tiny embedded game engine and custom PCB built around an ATtiny85V.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/ad-astra.jpg
|
||||||
|
alt: The Ad Astra handheld game running on its OLED display.
|
||||||
|
period: 'Spring 2020'
|
||||||
|
sortDate: 2020-04-01
|
||||||
|
technologies: ['C', 'ATtiny85V', 'OLED', 'EEPROM', 'PCB design']
|
||||||
|
selected: true
|
||||||
|
essay: ad-astra-attiny85-game-engine
|
||||||
|
links:
|
||||||
|
- label: Source
|
||||||
|
url: https://github.com/schmelczer/ad_astra
|
||||||
|
---
|
||||||
12
src/content/projects/avoid.md
Normal file
12
src/content/projects/avoid.md
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
title: Avoid
|
||||||
|
description: A small early web game, kept as an archive of first experiments on the web.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/avoid.jpg
|
||||||
|
alt: Screenshot of the Avoid canvas game.
|
||||||
|
period: 'January 2018'
|
||||||
|
sortDate: 2018-01-01
|
||||||
|
technologies: ['JavaScript', 'Canvas']
|
||||||
|
selected: false
|
||||||
|
essay: avoid-early-web-game
|
||||||
|
---
|
||||||
13
src/content/projects/city-simulation.md
Normal file
13
src/content/projects/city-simulation.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: City Simulation
|
||||||
|
description: A Unity traffic simulation where REST-controlled traffic lights could produce visible consequences for a cybersecurity challenge.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/city-simulation.jpg
|
||||||
|
alt: Screenshot of a Unity city traffic simulation.
|
||||||
|
period: 'July-August 2018'
|
||||||
|
sortDate: 2018-08-01
|
||||||
|
technologies: ['Unity', 'C#', 'REST API', 'Blender']
|
||||||
|
selected: false
|
||||||
|
essay: city-simulation-unity-traffic
|
||||||
|
links: []
|
||||||
|
---
|
||||||
13
src/content/projects/colors.md
Normal file
13
src/content/projects/colors.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: Photo Colour Grader
|
||||||
|
description: A proof-of-concept colour grading UI based on selecting colours and transforming nearby colour ranges.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/photo-colour-grader.jpg
|
||||||
|
alt: Screenshot of a colour grading interface applied to a photograph.
|
||||||
|
period: 'June 2018'
|
||||||
|
sortDate: 2018-06-01
|
||||||
|
technologies: ['JavaScript', 'Canvas', 'Image processing']
|
||||||
|
selected: false
|
||||||
|
essay: photo-colour-grader
|
||||||
|
links: []
|
||||||
|
---
|
||||||
18
src/content/projects/declared.md
Normal file
18
src/content/projects/declared.md
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
title: decla.red
|
||||||
|
description: A team-based mobile multiplayer browser game with shared client/server game logic.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/declared.jpg
|
||||||
|
alt: The decla.red browser game interface showing a space scene.
|
||||||
|
period: 'Autumn-Winter 2020'
|
||||||
|
sortDate: 2020-11-01
|
||||||
|
technologies: ['TypeScript', 'Node.js', 'WebSockets', 'Firebase', 'WebGL']
|
||||||
|
selected: true
|
||||||
|
essay: declared-shared-simulation-code
|
||||||
|
links:
|
||||||
|
- label: Source
|
||||||
|
url: https://github.com/schmelczer/decla.red
|
||||||
|
- label: BSc thesis
|
||||||
|
url: /media/downloads/sdf2d-andras-schmelczer.pdf
|
||||||
|
download: true
|
||||||
|
---
|
||||||
17
src/content/projects/fleeting-garden.md
Normal file
17
src/content/projects/fleeting-garden.md
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
title: Fleeting Garden
|
||||||
|
description: A WebGPU drawing toy where coloured strokes spawn agents that follow them, branch off, and slowly rewrite the patch you laid down.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/fleeting-garden.jpg
|
||||||
|
alt: A kaleidoscopic Fleeting Garden snapshot of cyan, violet, and yellow agent trails radiating from a central knot.
|
||||||
|
period: '2026'
|
||||||
|
sortDate: 2026-05-01
|
||||||
|
technologies: ['TypeScript', 'WebGPU', 'WGSL', 'Compute shaders', 'Vite', 'Physarum']
|
||||||
|
selected: true
|
||||||
|
essay: fleeting-garden-webgpu-drawing
|
||||||
|
links:
|
||||||
|
- label: Demo
|
||||||
|
url: /fleeting/
|
||||||
|
- label: Source
|
||||||
|
url: https://home.schmelczer.dev/git/andras/webgpu
|
||||||
|
---
|
||||||
13
src/content/projects/forex.md
Normal file
13
src/content/projects/forex.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: Foreign Exchange Prediction Experiment
|
||||||
|
description: A frequency-domain prediction experiment using smoothing, differentiation, STFT, extrapolation, and inverse transforms.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/forex.jpg
|
||||||
|
alt: Chart from a foreign exchange prediction experiment.
|
||||||
|
period: 'Autumn 2019'
|
||||||
|
sortDate: 2019-10-01
|
||||||
|
technologies: ['Python', 'NumPy', 'SciPy', 'Flask', 'MQL4']
|
||||||
|
selected: false
|
||||||
|
essay: foreign-exchange-prediction-experiment
|
||||||
|
links: []
|
||||||
|
---
|
||||||
20
src/content/projects/great-ai.md
Normal file
20
src/content/projects/great-ai.md
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
title: GreatAI
|
||||||
|
description: A Python framework and research project for making AI deployment best practices easier to adopt.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/great-ai.png
|
||||||
|
alt: Example Python code using the GreatAI API.
|
||||||
|
period: '2022'
|
||||||
|
sortDate: 2022-01-01
|
||||||
|
technologies: ['Python', 'ML deployment', 'API design']
|
||||||
|
selected: true
|
||||||
|
essay: greatai-ai-deployment-api
|
||||||
|
links:
|
||||||
|
- label: PyPI
|
||||||
|
url: https://pypi.org/project/great-ai/
|
||||||
|
- label: Project site
|
||||||
|
url: https://great-ai.scoutinscience.com
|
||||||
|
- label: MSc thesis
|
||||||
|
url: /media/downloads/great-ai-andras-schmelczer.pdf
|
||||||
|
download: true
|
||||||
|
---
|
||||||
13
src/content/projects/leds.md
Normal file
13
src/content/projects/leds.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: Lights Synchronized to Music
|
||||||
|
description: A Raspberry Pi music player that drove RGB LED strips from audio analysis.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/leds.jpg
|
||||||
|
alt: RGB LED strips glowing from a music synchronization project.
|
||||||
|
period: 'Spring 2016'
|
||||||
|
sortDate: 2016-04-01
|
||||||
|
technologies: ['Python', 'NumPy', 'FFT', 'Raspberry Pi', 'Vanilla web']
|
||||||
|
selected: false
|
||||||
|
essay: lights-synchronized-to-music
|
||||||
|
links: []
|
||||||
|
---
|
||||||
15
src/content/projects/my-notes.md
Normal file
15
src/content/projects/my-notes.md
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
title: My Notes
|
||||||
|
description: A minimalist Android markdown note organizer and editor powered by Markwon.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/my-notes.png
|
||||||
|
alt: Screenshot of the My Notes Android markdown app.
|
||||||
|
period: 'November 2019'
|
||||||
|
sortDate: 2019-11-01
|
||||||
|
technologies: ['Android', 'Kotlin/Java', 'Markdown', 'Markwon']
|
||||||
|
selected: false
|
||||||
|
essay: my-notes-android-markdown-app
|
||||||
|
links:
|
||||||
|
- label: Source
|
||||||
|
url: https://github.com/schmelczer/my-notes
|
||||||
|
---
|
||||||
13
src/content/projects/nuclear-editor.md
Normal file
13
src/content/projects/nuclear-editor.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: Graph Editor
|
||||||
|
description: A JavaFX editor for creating and editing input graphs for the cooling system simulator.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/process-simulator-input.jpg
|
||||||
|
alt: JavaFX editor interface for the cooling system simulator input graph.
|
||||||
|
period: 'October-November 2018'
|
||||||
|
sortDate: 2018-10-15
|
||||||
|
technologies: ['JavaFX', 'JSON', 'REST API']
|
||||||
|
selected: false
|
||||||
|
essay: graph-editor-javafx-simulation-input
|
||||||
|
links: []
|
||||||
|
---
|
||||||
13
src/content/projects/nuclear-simulation.md
Normal file
13
src/content/projects/nuclear-simulation.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: Cooling System Simulation
|
||||||
|
description: A graph-based process simulation with a monitoring client and JavaFX input editor.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/nuclear-simulation.jpg
|
||||||
|
alt: Cooling system simulator interface with pipes, pumps, and temperature values.
|
||||||
|
period: 'October-November 2018'
|
||||||
|
sortDate: 2018-11-01
|
||||||
|
technologies: ['Python', 'Flask', 'NumPy', 'HTML canvas', 'JavaFX']
|
||||||
|
selected: true
|
||||||
|
essay: nuclear-cooling-simulation
|
||||||
|
links: []
|
||||||
|
---
|
||||||
13
src/content/projects/photos.md
Normal file
13
src/content/projects/photos.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: Photo Site Generator
|
||||||
|
description: A static photo site generated from a directory of images, with automatic resizing to multiple quality settings.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/photos.jpg
|
||||||
|
alt: Screenshot of a generated photography site.
|
||||||
|
period: 'Summer 2016'
|
||||||
|
sortDate: 2016-07-01
|
||||||
|
technologies: ['Webpack', 'Image processing', 'Static site generation']
|
||||||
|
selected: false
|
||||||
|
essay: photo-site-generator
|
||||||
|
links: []
|
||||||
|
---
|
||||||
13
src/content/projects/platform-game.md
Normal file
13
src/content/projects/platform-game.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
title: Platform Game
|
||||||
|
description: An early 3D game in C with SDL 1.2, random maps, destructible voxels, enemies, powerups, and time slowdown.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/platform-game.jpg
|
||||||
|
alt: Screenshot from an early 3D platform game.
|
||||||
|
period: 'Autumn 2017'
|
||||||
|
sortDate: 2017-10-01
|
||||||
|
technologies: ['C', 'SDL 1.2', 'Voxel terrain']
|
||||||
|
selected: false
|
||||||
|
essay: platform-game-c-sdl
|
||||||
|
links: []
|
||||||
|
---
|
||||||
23
src/content/projects/reconcile.md
Normal file
23
src/content/projects/reconcile.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
title: reconcile-text
|
||||||
|
description: A Rust library that auto-resolves conflicting text edits without conflict markers, with WebAssembly and Python bindings.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/reconcile.png
|
||||||
|
alt: The reconcile-text logo and tagline "Conflict-free 3-way text merging".
|
||||||
|
period: '2025'
|
||||||
|
sortDate: 2025-05-01
|
||||||
|
technologies: ['Rust', 'WebAssembly', 'Python', 'pyo3', 'Operational Transformation']
|
||||||
|
selected: true
|
||||||
|
essay: reconcile-text-3-way-merge
|
||||||
|
links:
|
||||||
|
- label: Demo
|
||||||
|
url: /reconcile/
|
||||||
|
- label: Source
|
||||||
|
url: https://github.com/schmelczer/reconcile
|
||||||
|
- label: crates.io
|
||||||
|
url: https://crates.io/crates/reconcile-text
|
||||||
|
- label: npm
|
||||||
|
url: https://www.npmjs.com/package/reconcile-text
|
||||||
|
- label: PyPI
|
||||||
|
url: https://pypi.org/project/reconcile-text/
|
||||||
|
---
|
||||||
20
src/content/projects/sdf-2d.md
Normal file
20
src/content/projects/sdf-2d.md
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
title: SDF-2D
|
||||||
|
description: A browser rendering library for optimized 2D ray tracing with signed distance fields.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/sdf2d.jpg
|
||||||
|
alt: SDF-2D browser demo with soft lighting effects.
|
||||||
|
period: 'Autumn-Winter 2020'
|
||||||
|
sortDate: 2020-12-01
|
||||||
|
technologies: ['TypeScript', 'WebGL', 'WebGL2', 'SDF rendering']
|
||||||
|
selected: true
|
||||||
|
essay: sdf-2d-ray-tracing
|
||||||
|
links:
|
||||||
|
- label: NPM package
|
||||||
|
url: https://www.npmjs.com/package/sdf-2d
|
||||||
|
- label: Video
|
||||||
|
url: https://www.youtube.com/watch?v=K3cEtnZUNR0
|
||||||
|
- label: BSc thesis
|
||||||
|
url: /media/downloads/sdf2d-andras-schmelczer.pdf
|
||||||
|
download: true
|
||||||
|
---
|
||||||
15
src/content/projects/towers.md
Normal file
15
src/content/projects/towers.md
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
title: Life Towers
|
||||||
|
description: A multi-device goal tracker where the lasting idea was syncing state with immutable tries.
|
||||||
|
thumbnail:
|
||||||
|
src: ./_assets/towers.jpg
|
||||||
|
alt: Life Towers goal tracking interface with tower-like visual structures.
|
||||||
|
period: 'August-September 2019'
|
||||||
|
sortDate: 2019-09-01
|
||||||
|
technologies: ['Python', 'Angular', 'State synchronization']
|
||||||
|
selected: true
|
||||||
|
essay: life-towers-immutable-tries
|
||||||
|
links:
|
||||||
|
- label: Source
|
||||||
|
url: https://github.com/schmelczer/life-towers/
|
||||||
|
---
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
import { Contact } from '../page/contact/contact.html';
|
|
||||||
import { Header } from '../page/header/header';
|
|
||||||
import { ImageViewer } from '../page/image-viewer/image-viewer';
|
|
||||||
import { Link } from '../page/link/link.html';
|
|
||||||
import { Main } from '../page/main/main';
|
|
||||||
import { PageElement } from '../page/page-element';
|
|
||||||
import { TimelineElement } from '../page/timeline-element/timeline-element';
|
|
||||||
import { UpArrowButton } from '../page/up-arrow-button/up-arrow-button';
|
|
||||||
import cvEnglish from './media/cv-andras-schmelczer.pdf';
|
|
||||||
import me from './media/me.jpg';
|
|
||||||
import { adAstra } from './projects/ad-astra';
|
|
||||||
import { avoid } from './projects/avoid';
|
|
||||||
import { citySimulation } from './projects/city-simulation';
|
|
||||||
import { declared } from './projects/declared';
|
|
||||||
import { forex } from './projects/forex';
|
|
||||||
import { greatAi } from './projects/great-ai';
|
|
||||||
import { leds } from './projects/leds';
|
|
||||||
import { myNotes } from './projects/my-notes';
|
|
||||||
import { nuclear } from './projects/nuclear';
|
|
||||||
import { nuclearEditor } from './projects/nuclear-editor';
|
|
||||||
import { photos } from './projects/photos';
|
|
||||||
import { platformGame } from './projects/platform-game';
|
|
||||||
import { sdf2d } from './projects/sdf2d';
|
|
||||||
import { towers } from './projects/towers';
|
|
||||||
import { CV, Email, GitHubLink, LinkedIn } from './shared';
|
|
||||||
|
|
||||||
const imageViewer = new ImageViewer();
|
|
||||||
const contact = new PageElement(
|
|
||||||
Contact({
|
|
||||||
title: 'Get in touch',
|
|
||||||
links: [
|
|
||||||
CV(cvEnglish),
|
|
||||||
Email('mailto:andras@schmelczer.dev'),
|
|
||||||
LinkedIn('https://www.linkedin.com/in/andras-schmelczer'),
|
|
||||||
GitHubLink('https://github.com/schmelczer'),
|
|
||||||
],
|
|
||||||
lastEditText: 'Last modified on ',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const main = new Main(
|
|
||||||
new Header({
|
|
||||||
name: 'Andras Schmelczer',
|
|
||||||
image: me,
|
|
||||||
imageAltText: 'a picture of me',
|
|
||||||
imageViewer,
|
|
||||||
about: [
|
|
||||||
'With an MSc in Computer Science and more than six years of professional software engineering experience, I can confidently undertake any challenge. My interests span diverse areas, allowing me to design complex — even multidisciplinary — systems with a clear understanding.',
|
|
||||||
|
|
||||||
"I'm passionate about architecting and building large-scale systems, especially in the context of AI/ML. However, in my free time, I also enjoy working with shaders, data visualisation, and sometimes even microcontrollers.",
|
|
||||||
|
|
||||||
`Discover some of my more exciting projects below. And if you'd like to reach out to me, you can find my ${Link(
|
|
||||||
'CV and contact details',
|
|
||||||
'#contact'
|
|
||||||
)} at the bottom of the page.`,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
|
|
||||||
...[
|
|
||||||
greatAi,
|
|
||||||
declared,
|
|
||||||
sdf2d,
|
|
||||||
adAstra,
|
|
||||||
forex,
|
|
||||||
myNotes,
|
|
||||||
towers,
|
|
||||||
nuclear,
|
|
||||||
nuclearEditor,
|
|
||||||
citySimulation,
|
|
||||||
avoid,
|
|
||||||
platformGame,
|
|
||||||
photos,
|
|
||||||
leds,
|
|
||||||
].map((p) => new TimelineElement(p, 'Show details', 'Show less', imageViewer)),
|
|
||||||
|
|
||||||
contact
|
|
||||||
);
|
|
||||||
|
|
||||||
export const portfolio: Array<PageElement> = [
|
|
||||||
main,
|
|
||||||
new UpArrowButton(main, contact, 'go up'),
|
|
||||||
imageViewer,
|
|
||||||
];
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import { Video } from '../../page/figure/video/video';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import adAstraPoster from '../media/ad_astra.jpg';
|
|
||||||
import adAstraMp4 from '../media/mp4/ad_astra.mp4';
|
|
||||||
import adAstraWebM from '../media/webm/ad_astra.webm';
|
|
||||||
import { GitHub, videoPosterAltText } from '../shared';
|
|
||||||
|
|
||||||
export const adAstra: TimelineElementParameters = {
|
|
||||||
title: 'Embedded game engine',
|
|
||||||
date: 'Spring 2020',
|
|
||||||
figure: new Video({
|
|
||||||
poster: adAstraPoster,
|
|
||||||
mp4: adAstraMp4,
|
|
||||||
webm: adAstraWebM,
|
|
||||||
altText: videoPosterAltText,
|
|
||||||
}),
|
|
||||||
description:
|
|
||||||
"I've always wanted to combine graphics and microcontrollers, so the obvious next step was making a game engine for the ATTiny85. The video shows how I created an enjoyable game using my engine, literally, from scratch.",
|
|
||||||
more: [
|
|
||||||
"Besides overcoming the hardware's minimal resources, the greatest challenge was designing and manufacturing the PCB; this was also the most rewarding part. The hardware setup is straightforward. Aside from the ATtiny85V, I used a D096-12864-SPI7 OLED display as output and a TSOP4838 IR receiver as input. The circuit runs on 3.3V, so a regulator is also needed. The system is very low-power, with its peak consumption at around 31mW on full brightness, and there is also a standby mode with a current draw of just 1.5mA.",
|
|
||||||
|
|
||||||
"Even though I used C for programming, I tried striking a balance between object-oriented and structured programming to reduce complexity while maintaining performance. For example, I used prototype-based inheritance for the game objects, allowing easy code reuse. Meanwhile, I created a high-performance driver for the display, which uses SIMD techniques to process 4 pixels at once (it's an 8-bit ALU). Thanks to this, the maximum frame times are between 15 and 20 milliseconds at a clock speed of 8 MHz. This means that the lowest FPS during gameplay never dips below 50 FPS.",
|
|
||||||
|
|
||||||
'There is also fault-tolerant persistent data storage (with atomic commit) utilising the built-in EEPROM for storing game state. To create sprites (which are also stored in the EEPROM), I made a tool that converts PNG-s into C array definitions. These scripts can also be found on GitHub, along with the entire project.',
|
|
||||||
],
|
|
||||||
links: [GitHub('https://github.com/schmelczer/ad_astra')],
|
|
||||||
};
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import { Preview } from '../../page/figure/preview/preview';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import avoidPoster from '../media/avoid.png';
|
|
||||||
import { Open } from '../shared';
|
|
||||||
|
|
||||||
export const avoid: TimelineElementParameters = {
|
|
||||||
title: 'Avoid',
|
|
||||||
date: 'January 2018',
|
|
||||||
figure: new Preview(
|
|
||||||
avoidPoster,
|
|
||||||
'https://schmelczer.dev/avoid',
|
|
||||||
'A webpage showcasing the SDF-2D project.'
|
|
||||||
),
|
|
||||||
description:
|
|
||||||
"I recently found my first-ever web game. It's incredibly simple but I killed some time with it, so feel free to try it out but don't judge too harshly.",
|
|
||||||
|
|
||||||
links: [Open('https://schmelczer.dev/avoid')],
|
|
||||||
};
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import { Video } from '../../page/figure/video/video';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import citySimulationMp4 from '../media/mp4/simulation.mp4';
|
|
||||||
import citySimulationPoster from '../media/simulation.jpg';
|
|
||||||
import citySimulationWebM from '../media/webm/simulation.webm';
|
|
||||||
import { videoPosterAltText } from '../shared';
|
|
||||||
|
|
||||||
export const citySimulation: TimelineElementParameters = {
|
|
||||||
title: 'City simulation — Unity',
|
|
||||||
date: 'July - August 2018',
|
|
||||||
figure: new Video({
|
|
||||||
poster: citySimulationPoster,
|
|
||||||
mp4: citySimulationMp4,
|
|
||||||
webm: citySimulationWebM,
|
|
||||||
altText: videoPosterAltText,
|
|
||||||
}),
|
|
||||||
description: 'I simulated a city where car crashes are more frequent than usual.',
|
|
||||||
more: [
|
|
||||||
'The state of the traffic lights can be changed through a REST API. Drivers follow the instructions of the traffic lights, so if a mistake is made, there will be collisions. There is also support for displaying tweets on a HUD. This was created as the context for a cybersecurity challenge about PLCs. With the help of this program, the contestants could instantly see the effect of their work.',
|
|
||||||
|
|
||||||
'An exciting aspect of the project was building it in a server-client architecture. Every decision of the agents is calculated server-side. The real challenge was broadcasting these decisions in a fault-tolerant way using minimal bandwidth.',
|
|
||||||
|
|
||||||
'It is made with Unity using C# as the scripting language. The models and animations were also made by me using Blender.',
|
|
||||||
],
|
|
||||||
links: [],
|
|
||||||
};
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { BorderedImage } from '../../page/figure/bordered-image/bordered-image';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import colorsPoster from '../media/color.jpg';
|
|
||||||
|
|
||||||
export const colors: TimelineElementParameters = {
|
|
||||||
title: 'Photo colour grader',
|
|
||||||
date: 'June 2018',
|
|
||||||
figure: new BorderedImage({
|
|
||||||
image: colorsPoster,
|
|
||||||
alt: 'a picture of the app',
|
|
||||||
}),
|
|
||||||
description: 'An innovative (at least I thought so) colour grader web application.',
|
|
||||||
more: [
|
|
||||||
'The most noteworthy feature of this application is the colour selector UI. This program is only intended as a proof-of-concept, I would have liked to experiment with some ideas and this was the outcome.',
|
|
||||||
|
|
||||||
'You can select some colours and then apply transformations to the other colours as a function of their distance to the selected colour.',
|
|
||||||
|
|
||||||
'By clicking on a coloured circle you can change its settings. New circles can be created by clicking in the large circle (and they can also be moved by drag & drop).',
|
|
||||||
],
|
|
||||||
links: [],
|
|
||||||
};
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import { Preview } from '../../page/figure/preview/preview';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import declaredPoster from '../media/decla-red.png';
|
|
||||||
import bscThesis from '../media/sdf2d-andras-schmelczer.pdf';
|
|
||||||
import { GitHub, Open, Thesis } from '../shared';
|
|
||||||
|
|
||||||
export const declared: TimelineElementParameters = {
|
|
||||||
title: 'Multiplayer mobile game',
|
|
||||||
date: 'Autumn - Winter 2020',
|
|
||||||
figure: new Preview(declaredPoster, 'https://decla.red', 'The UI of the video game'),
|
|
||||||
description:
|
|
||||||
'I created a conquest-style online multiplayer browser game using my ray-tracing library (see below). It even runs on mobiles.',
|
|
||||||
more: [
|
|
||||||
'The scene is set in space. Two large teams have to conquer tiny planets, while they can also shoot at the other team. Points are given based on the number of planets controlled, and the first team which reaches a predefined score wins.',
|
|
||||||
|
|
||||||
"The architecture consists of multiple servers, each of which communicates with 16-32 clients over WebSockets; Firebase is used to reach consensus on the set of active servers. The project uses TypeScript compiled into a website and a Node application. There is a shared library which contains the game logic. This way, both the client and server can link to this library, allowing to use of the same code for calculating the actual next state on the server and client-side-predicting it on the users' devices.",
|
|
||||||
|
|
||||||
'My favourite part of the project was handling the increasingly complex and heavy-weight game logic. To tackle the former, I decided to borrow inspiration from Smalltalk\'s message passing, including the concept of "messageNotUnderstood". To improve the performance, I implemented k-d trees to decrease the spatial operations\' complexity.',
|
|
||||||
|
|
||||||
'This game (along with SDF-2D) was my BSc thesis project, so more in-depth information about them can be found in my thesis linked below.',
|
|
||||||
],
|
|
||||||
links: [
|
|
||||||
GitHub('https://github.com/schmelczer/decla.red'),
|
|
||||||
Thesis(bscThesis),
|
|
||||||
Open('https://decla.red'),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import { Video } from '../../page/figure/video/video';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import forexPoster from '../media/forex.jpg';
|
|
||||||
import forexMp4 from '../media/mp4/forex.mp4';
|
|
||||||
import forexWebM from '../media/webm/forex.webm';
|
|
||||||
import { videoPosterAltText } from '../shared';
|
|
||||||
|
|
||||||
export const forex: TimelineElementParameters = {
|
|
||||||
title: 'Predicting foreign exchange rates',
|
|
||||||
date: 'Autumn 2019',
|
|
||||||
figure: new Video({
|
|
||||||
poster: forexPoster,
|
|
||||||
mp4: forexMp4,
|
|
||||||
webm: forexWebM,
|
|
||||||
invertButton: true,
|
|
||||||
altText: videoPosterAltText,
|
|
||||||
}),
|
|
||||||
description:
|
|
||||||
"The animation shows that my implementation does a somewhat good job predicting (blue graph) the EUR/USD rates (green chart). Of course, I wouldn't trust it with my money.",
|
|
||||||
more: [
|
|
||||||
'The algorithm is a fancy linear regression in the frequency domain. The steps are the following: smoothing the input values, differentiating, applying a short-time Fourier transformation with overlapped (and Hanning-windowed) windows, extrapolating and then applying the inverse of these transformations to the resulting values.',
|
|
||||||
|
|
||||||
'This prediction server, written in Python using NumPy, SciPy, and Flask, communicates with an MQL4 client responsible for handling the financial transaction based on this data.',
|
|
||||||
|
|
||||||
"Of course, there is still plenty of room for improvement, but even with this simple algorithm, a sometimes profitable trading strategy is viable. Nonetheless, the project gave me an exciting insight into the world of trading algorithms, the field's complexity, and the fierce competition surrounding it.",
|
|
||||||
],
|
|
||||||
links: [],
|
|
||||||
};
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import { BorderedImage } from '../../page/figure/bordered-image/bordered-image';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import mscThesis from '../media/great-ai-andras-schmelczer.pdf';
|
|
||||||
import greatAiPoster from '../media/great-ai.png';
|
|
||||||
import { Open, PyPi, Thesis } from '../shared';
|
|
||||||
|
|
||||||
export const greatAi: TimelineElementParameters = {
|
|
||||||
title: 'GreatAI — AI deployment framework',
|
|
||||||
date: '2022',
|
|
||||||
figure: new BorderedImage({
|
|
||||||
image: greatAiPoster,
|
|
||||||
alt: 'some example code using GreatAI',
|
|
||||||
isEagerLoaded: true,
|
|
||||||
}),
|
|
||||||
description:
|
|
||||||
'I investigated an approach for increasing the adoption rate of ML deployment libraries and hence the overall quality of industrial deployments. I did this by simultaneously focusing on providing robust, automated implementations of best practices and an accessible API. One of the outcomes of my research is the GreatAI framework.',
|
|
||||||
more: [
|
|
||||||
'Applying AI is becoming increasingly more accessible, but many case studies have shown that these applications are often deployed poorly. This may lead to suboptimal performance and the introduction of unintended biases.',
|
|
||||||
|
|
||||||
'My work presents 33 AI/ML deployment best practices (while introducing six new ones), the difficulties of implementing them, and ways to overcome these challenges. GreatAI helps implement these through an accessible interface.',
|
|
||||||
|
|
||||||
'Feedback from professional data scientists and software engineers showed that ease of use and functionality are equally important in deciding to adopt deployment technologies, and the proposed framework was rated positively in both dimensions.',
|
|
||||||
|
|
||||||
'For more details, visit the GitHub page or the paper.',
|
|
||||||
],
|
|
||||||
links: [
|
|
||||||
PyPi('https://pypi.org/project/great-ai/'),
|
|
||||||
Thesis(mscThesis),
|
|
||||||
Open('https://great-ai.scoutinscience.com'),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
import { Video } from '../../page/figure/video/video';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import ledPoster from '../media/led.jpg';
|
|
||||||
import ledMp4 from '../media/mp4/led.mp4';
|
|
||||||
import ledWebM from '../media/webm/led.webm';
|
|
||||||
import { videoPosterAltText } from '../shared';
|
|
||||||
|
|
||||||
export const leds: TimelineElementParameters = {
|
|
||||||
title: 'Lights synchronised to music',
|
|
||||||
date: 'Spring 2016',
|
|
||||||
figure: new Video({
|
|
||||||
poster: ledPoster,
|
|
||||||
mp4: ledMp4,
|
|
||||||
webm: ledWebM,
|
|
||||||
altText: videoPosterAltText,
|
|
||||||
}),
|
|
||||||
description:
|
|
||||||
'A full-stack application with a built-in music player, the output of which controls the colour of a couple of RGB LED strips through a Raspberry Pi and some MOSFET-s.',
|
|
||||||
more: [
|
|
||||||
'This was my first non-trivial project which got finished. Obviously, it is rather far from perfect, but I am still proud that I was able to build it on my own.',
|
|
||||||
|
|
||||||
'The backend logic is written in Python, and the FFT implementation is provided by NumPy. I also built a simple frontend for accessing the music player and changing the settings using vanilla web development technologies.',
|
|
||||||
],
|
|
||||||
links: [],
|
|
||||||
};
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import { BorderedImage } from '../../page/figure/bordered-image/bordered-image';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import myNotesPoster from '../media/my-notes.png';
|
|
||||||
import { GitHub } from '../shared';
|
|
||||||
|
|
||||||
export const myNotes: TimelineElementParameters = {
|
|
||||||
title: 'My Notes — Android app',
|
|
||||||
date: 'November 2019',
|
|
||||||
figure: new BorderedImage({
|
|
||||||
image: myNotesPoster,
|
|
||||||
alt: 'two screenshots of the application',
|
|
||||||
}),
|
|
||||||
description: 'A minimalist Android note organiser and editor powered by Markwon.',
|
|
||||||
more: [
|
|
||||||
'It is a basic app for creating and filtering markdown notes (based on #hashtags). It was my first exposure to Android development.',
|
|
||||||
|
|
||||||
"All in all, it's not a unique idea, but at least it's functional and has exposed me to a wildly different paradigm than I was used to with full-stack web development. Thus, the knowledge I gained while working on it made its development a worthwhile adventure.",
|
|
||||||
],
|
|
||||||
links: [GitHub('https://github.com/schmelczer/my-notes')],
|
|
||||||
};
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import { BorderedImage } from '../../page/figure/bordered-image/bordered-image';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import nuclearEditorPoster from '../media/process-simulator-input.jpg';
|
|
||||||
|
|
||||||
export const nuclearEditor: TimelineElementParameters = {
|
|
||||||
title: 'Graph editor — JavaFX',
|
|
||||||
date: 'October - November 2018',
|
|
||||||
figure: new BorderedImage({
|
|
||||||
image: nuclearEditorPoster,
|
|
||||||
alt: "a picture of the simulator's UI",
|
|
||||||
}),
|
|
||||||
description:
|
|
||||||
'An intuitive editor to create and edit input for the nuclear facility simulator (see above).',
|
|
||||||
more: [
|
|
||||||
'Nodes can be moved with drag & drop gestures. Editing the parameters of elements can be done on the right panel.',
|
|
||||||
|
|
||||||
'The UI is built with JavaFX. The output can be exported as JSON or directly uploaded to the simulation backend.',
|
|
||||||
],
|
|
||||||
links: [],
|
|
||||||
};
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import { BorderedImage } from '../../page/figure/bordered-image/bordered-image';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import processSimulatorPoster from '../media/process-simulator.jpg';
|
|
||||||
|
|
||||||
export const nuclear: TimelineElementParameters = {
|
|
||||||
title: 'Simulating the cooling system of a nuclear facility',
|
|
||||||
date: 'October - November 2018',
|
|
||||||
figure: new BorderedImage({
|
|
||||||
image: processSimulatorPoster,
|
|
||||||
alt: 'a screenshot of the simulator',
|
|
||||||
}),
|
|
||||||
description:
|
|
||||||
'The temperatures and flow volumes are dynamically calculated by two graph models on a remote server while multiple "monitoring" clients update in real-time.',
|
|
||||||
more: [
|
|
||||||
'The simulated system is easily extensible and, by default, can contain reactors (heaters), coolers, pumps, heat exchangers, drains, sources, and of course, pipes. With these, simple yet believable configurations can be dynamically defined.',
|
|
||||||
|
|
||||||
'My project aimed to create a cheaply calculated and, for the average person, convincing simulation which is simply scalable and has a clean GUI. The algorithm takes advantage of graphs and matrices to get to update the state iteratively.',
|
|
||||||
|
|
||||||
'First, water is distributed by traversing the graph of pipes and according to the pressures generated by the pumps. Then, an adjacency matrix is populated with the relations of the nodes (based on the water flow between them). After considering the base temperatures, heaters, and heat exchangers, the matrix is solved, resulting in the current temperature of each node. This can be repeated many times, and coming from the operations semantic, time-travel is also straightforward to implement.',
|
|
||||||
|
|
||||||
'Python is used for the backend, along with Flask and NumPy. A REST API facilitates communication between the layers. For rendering the front end, an HTML5 canvas is utilised.',
|
|
||||||
],
|
|
||||||
links: [],
|
|
||||||
};
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import { BorderedImage } from '../../page/figure/bordered-image/bordered-image';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import photosPoster from '../media/photos.jpg';
|
|
||||||
import { Open } from '../shared';
|
|
||||||
|
|
||||||
export const photos: TimelineElementParameters = {
|
|
||||||
title: 'Photos',
|
|
||||||
date: 'Summer 2016',
|
|
||||||
figure: new BorderedImage({
|
|
||||||
image: photosPoster,
|
|
||||||
alt: 'a picture of the website',
|
|
||||||
}),
|
|
||||||
description: 'A simple webpage where you can view my photos.',
|
|
||||||
more: [
|
|
||||||
"Taking time to appreciate the world around us fills me with joy. That's why I like to go on walks with a camera. I might not end up with great photos. Nonetheless, I usually end up with some inspiration regarding my current or next project.",
|
|
||||||
|
|
||||||
'As for the webpage, a Webpack script generates the site from the photos in a directory, automatic resizing to multiple quality settings is also part of the pipeline.',
|
|
||||||
],
|
|
||||||
links: [Open('https://photo.schmelczer.dev')],
|
|
||||||
};
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
import { Video } from '../../page/figure/video/video';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import platformMp4 from '../media/mp4/platform.mp4';
|
|
||||||
import platformPoster from '../media/platform.png';
|
|
||||||
import platformWebM from '../media/webm/platform.webm';
|
|
||||||
import { videoPosterAltText } from '../shared';
|
|
||||||
|
|
||||||
export const platformGame: TimelineElementParameters = {
|
|
||||||
title: 'Platform game',
|
|
||||||
date: 'Autumn 2017',
|
|
||||||
figure: new Video({
|
|
||||||
poster: platformPoster,
|
|
||||||
mp4: platformMp4,
|
|
||||||
webm: platformWebM,
|
|
||||||
altText: videoPosterAltText,
|
|
||||||
}),
|
|
||||||
description:
|
|
||||||
'This was my first proper project. I created an actually fun, 3D game written in pure C with the help of SDL 1.2',
|
|
||||||
more: [
|
|
||||||
'The maps are randomly generated and fully destroyable voxel-by-voxel. This also allows for creating structures for hiding from flying enemies who chase the player and also can destroy the terrain after merging together and growing larger. After collecting enough powerups, they can shoot and even slow down time in exchange for losing some points.',
|
|
||||||
|
|
||||||
'I did this as my final project for my Basics of Programming course. Through making this, I learned a lot about pointers after an adequate number of segmentation faults. But it also made me realise my passion for programming.',
|
|
||||||
],
|
|
||||||
links: [],
|
|
||||||
};
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import { Preview } from '../../page/figure/preview/preview';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import sdf2dPoster from '../media/sdf2d.png';
|
|
||||||
import { NPM, Open, Youtube } from '../shared';
|
|
||||||
|
|
||||||
export const sdf2d: TimelineElementParameters = {
|
|
||||||
title: 'Optimising 2D ray tracing',
|
|
||||||
date: 'Autumn - Winter 2020',
|
|
||||||
figure: new Preview(
|
|
||||||
sdf2dPoster,
|
|
||||||
'https://sdf2d.schmelczer.dev',
|
|
||||||
'A webpage showcasing the SDF-2D project.'
|
|
||||||
),
|
|
||||||
description:
|
|
||||||
'I created the SDF-2D library for efficiently rendering 2D scenes using ray tracing. My solution relies on signed distance fields (SDF-s) and a novel optimisation for 2D SDF rendering inspired by tiled renderers. It even works great on mobiles.',
|
|
||||||
more: [
|
|
||||||
"A multitude of optimisations was needed to achieve compatibility and real-time performance, even on low-end devices. Next to tile-based rendering, these include deferred shading and dynamic shader generation to eliminate unnecessary instructions based on the device's capabilities.",
|
|
||||||
|
|
||||||
'The result is a reusable library written in TypeScript with a — subjectively — simple and elegant API. The library supports WebGL and WebGL2 and is an easily reusable and extensible NPM package.',
|
|
||||||
|
|
||||||
'Please check out the GitHub repository, the NPM package, or my thesis (linked above) for more information. Or simply enjoy the mesmerising demo scenes.',
|
|
||||||
],
|
|
||||||
links: [
|
|
||||||
NPM('https://www.npmjs.com/package/sdf-2d'),
|
|
||||||
Youtube('https://www.youtube.com/watch?v=K3cEtnZUNR0'),
|
|
||||||
Open('https://sdf2d.schmelczer.dev'),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { BorderedImage } from '../../page/figure/bordered-image/bordered-image';
|
|
||||||
import { TimelineElementParameters } from '../../page/timeline-element/timeline-element-parameters';
|
|
||||||
import towersPoster from '../media/towers.png';
|
|
||||||
import { GitHub, Open } from '../shared';
|
|
||||||
|
|
||||||
export const towers: TimelineElementParameters = {
|
|
||||||
title: 'Multi-device life tracking',
|
|
||||||
date: 'August - September 2019',
|
|
||||||
figure: new BorderedImage({
|
|
||||||
image: towersPoster,
|
|
||||||
alt: 'a picture of the website',
|
|
||||||
}),
|
|
||||||
description: 'An aesthetic representation of your previous and current goals/tasks.',
|
|
||||||
more: [
|
|
||||||
'This project allowed me to deepen my Python & Angular knowledge. The most exciting part of it — apart from designing and implementing the pleasing visuals — was coming up with its data structure and method of synchronising state between the clients and servers.',
|
|
||||||
|
|
||||||
"In the end, I decided on using a trie. Its properties make it reasonably simple to find the difference between the server's and client's stored versions, reconcile the differences and then only send this delta through the network. Additionally, its immutable nature helped simplify much of the logic.",
|
|
||||||
],
|
|
||||||
links: [
|
|
||||||
GitHub('https://github.com/schmelczer/life-towers/'),
|
|
||||||
Open('https://towers.schmelczer.dev'),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import cvIcon from '../../static/icons/cv.svg';
|
|
||||||
import emailIcon from '../../static/icons/email.svg';
|
|
||||||
import gitHubIcon from '../../static/icons/github.svg';
|
|
||||||
import linkedInIcon from '../../static/icons/linkedin.svg';
|
|
||||||
import openIcon from '../../static/icons/open.svg';
|
|
||||||
import packageIcon from '../../static/icons/package.svg';
|
|
||||||
import pythonIcon from '../../static/icons/python.svg';
|
|
||||||
import youtubeIcon from '../../static/icons/youtube.svg';
|
|
||||||
import { ImageAnchorFactory } from '../page/image-anchor/image-anchor.html';
|
|
||||||
import { ImageButtonFactory } from '../page/image-button/image-button.html';
|
|
||||||
|
|
||||||
export const GitHub = ImageButtonFactory(gitHubIcon, 'Open on GitHub');
|
|
||||||
export const NPM = ImageButtonFactory(packageIcon, 'Open on NPM');
|
|
||||||
export const PyPi = ImageButtonFactory(pythonIcon, 'Open on PyPI');
|
|
||||||
export const Open = ImageButtonFactory(openIcon, 'Open in new tab');
|
|
||||||
export const Thesis = ImageButtonFactory(cvIcon, 'Download thesis', {
|
|
||||||
shouldDownload: true,
|
|
||||||
});
|
|
||||||
export const Youtube = ImageButtonFactory(youtubeIcon, 'Open on YouTube');
|
|
||||||
|
|
||||||
export const CV = ImageAnchorFactory(cvIcon, 'Download my CV', { shouldDownload: true });
|
|
||||||
export const GitHubLink = ImageAnchorFactory(gitHubIcon, 'Find me on GitHub');
|
|
||||||
export const LinkedIn = ImageAnchorFactory(linkedInIcon, 'Find me on LinkedIn');
|
|
||||||
export const Email = ImageAnchorFactory(emailIcon, 'andras@schmelczer.dev');
|
|
||||||
|
|
||||||
export const videoPosterAltText = 'thumbnail for the video';
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue