Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zemn.me: 3d scene renderer #7588

Open
wants to merge 1 commit into
base: pr7664
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions project/zemn.me/app/experiments/3d/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
load("//bzl:rules.bzl", "bazel_lint")
load("//ts:rules.bzl", "ts_project")

ts_project(
name = "3d",
srcs = [
"client.tsx",
"page.tsx",
],
visibility = ["//project/zemn.me/app/experiments:__subpackages__"],
deps = [
"//:node_modules/@types/react",
"//:node_modules/@types/react-dom",
"//:node_modules/d3-array",
"//:node_modules/next",
"//:node_modules/react",
"//:node_modules/react-dom",
"//ts/math",
"//ts/math/mesh",
],
)

bazel_lint(
name = "bazel_lint",
srcs = ["BUILD.bazel"],
)
142 changes: 142 additions & 0 deletions project/zemn.me/app/experiments/3d/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"use client";
import { extent } from "d3-array";
import React, { Fragment, useId, useState } from "react";

import { Iterable } from "#root/ts/iter/index.js";
import { camera } from "#root/ts/math/camera.js";
import { clamp3DTo2D, Line2D, Line3D, point, Point3D, x, y } from "#root/ts/math/cartesian.js";
import { Centre, cube, Mesh, mesh2Edges } from "#root/ts/math/mesh/mesh.js";

interface Camera {
position: Point3D
lookAt: Point3D
}

interface EdgesRendererProps {
readonly edges: Line2D<2>[]
}

function EdgesRenderer(props: EdgesRendererProps) {
const { edges } = props;

// Compute the viewBox from the extents of the edges
const [xmin, xmax] = extent(edges.map(l => l.map(pt => x(pt))).flat(2))!;
const [ymin, ymax] = extent(edges.map(l => l.map(pt => y(pt))).flat(2))!;

return (
<>
{Object.entries({ xmin, xmax, ymin, ymax }).map(([k, v]) => <>
{k}: {v}{" "}</>)}
<svg height={200} viewBox={[xmin, ymin, xmax, ymax].join(" ")} width={200}>
{edges.map((edge, i) => {
const [start, end] = edge;
return (
<line
key={i}
stroke="black" strokeWidth={(xmax! - xmin!) / 100}
x1={x(start)} x2={x(end)}
y1={y(start)}
y2={y(end)}
/>
);
})}
</svg>
</>
);
}

interface Scene<Object> {
readonly objects: Object[]
readonly edges: (v: Object) => globalThis.Iterable<Line3D<2>>
readonly camera: Camera
}

interface MapProps<Object> {
className?: string
scene: Scene<Object>
}

function Map<Object>(p: MapProps<Object>) {
const cameraViewLine: Line3D<2> = [
p.scene.camera.position,
p.scene.camera.lookAt
];

const meshesEdges =
Iterable(p.scene.objects)
.map(p.scene.edges)
.flatten();

const lines: Line3D<2>[] = [
cameraViewLine,
...meshesEdges.to_array()
];

// Convert 3D lines to 2D
const edges2D: EdgesRendererProps["edges"] = lines.map(([s, e]) => [
clamp3DTo2D(s), clamp3DTo2D(e)
]) as Line2D<2>[];

return <EdgesRenderer edges={edges2D} />;
}

function Projection<Object>(p: MapProps<Object>) {
return <EdgesRenderer edges={
Iterable(p.scene.objects)
.map(p.scene.edges)
.flatten()
.map(([start, end]): Line2D<2> =>
[
camera(
p.scene.camera.position,
p.scene.camera.lookAt,
start,
4,
),
camera(
p.scene.camera.position,
p.scene.camera.lookAt,
end,
4,
),
]).to_array()
} />

}


export function SceneRenderer<Object>(props: Scene<Object>) {
return <div style={{ grid: "" }}>
{/* top down map */}
<Map scene={props} />
{/* projection */}
<Projection scene={props} />
</div>
}

export function ThreeDClient() {
const subject = cube(point<3>(0, 0, 0), 1);
const xyz = [1, 5, 2].map(v => useState<number>(v));

return <>
{xyz.map(([g, s], i) => <Fragment key={`axis${i}`}>

<input
id={useId()} key={`axis${i}`}
max="50" min="-50" onChange={t => s(+t.target.value)}
type="range" value={g} />

{g}

</Fragment>)}
<SceneRenderer
camera={{
lookAt: subject[Centre],
position: point<3>(
...xyz.map(([g]) => g) as [number, number, number]
)
}}
edges={(v: Mesh<8>) => mesh2Edges(v)}
objects={[subject]}
/></>;
}
15 changes: 15 additions & 0 deletions project/zemn.me/app/experiments/3d/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Metadata } from 'next/types';

import { ThreeDClient } from '#root/project/zemn.me/app/experiments/3d/client.js';



export default function Page() {
return <ThreeDClient/>
}

export const metadata: Metadata = {
title: "test 3d renderer"
}


1 change: 1 addition & 0 deletions project/zemn.me/app/experiments/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ts_project(
"//:node_modules/nuqs",
"//:node_modules/react",
"//:node_modules/seedrandom",
"//project/zemn.me/app/experiments/3d",
"//project/zemn.me/app/experiments/article",
"//project/zemn.me/app/experiments/cultist",
"//project/zemn.me/app/experiments/factorio",
Expand Down
4 changes: 4 additions & 0 deletions ts/math/cartesian.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export function length(l: Line2D<2>): number {
return magnitude(Matrix.sub<1, 2>(l[1], l[0]));
}

export function clamp3DTo2D(pt: Point<3>): Point<2> {
return point<2>(x(pt), y(pt))
}

export function magnitude(l: Point): number {
return vecMag(l.map(([v]) => v))
}
Expand Down
4 changes: 2 additions & 2 deletions ts/math/mesh/mesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export interface Mesh<N extends number = number> {
}

export function mesh2Edges<N extends number>(m: Mesh<N>) {
return map(m[Edges], ([start, end]) =>
[m[Verticies][start], m[Verticies][end]] as const
return map(m[Edges], ([start, end]): Cartesian.Line3D<2> =>
[m[Verticies][start]!, m[Verticies][end]!]
)
}

Expand Down
Loading