Skip to content

Commit

Permalink
feat: more controls, add stl export
Browse files Browse the repository at this point in the history
  • Loading branch information
Battlesquid committed Dec 18, 2024
1 parent 358a655 commit b7cab4e
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 67 deletions.
4 changes: 2 additions & 2 deletions packages/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import tunnel from "tunnel-rat";
import { useQuery } from "urql";
import { ContributionQuery } from "./api/query";
import "./App.css";
import { Skyline } from "./components/three/skyline";
import { Sidebar } from "./components/ui/sidebar";
import { Skyline } from "./components/skyline";
import { Sidebar } from "./components/sidebar";

export const t = tunnel();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { useState } from "react";
import * as THREE from "three";
import { Card, Text } from "@mantine/core";
import { ResultOf } from "gql.tada";
import { ContributionQuery } from "../../api/query";
import { t } from "../../App";
import { ContributionQuery } from "../api/query";
import { t } from "../App";

interface ContributionTowerProps extends MeshProps {
x: number;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { AppShell, Button, Checkbox, ColorInput, Divider, NumberInput, Stack, TextInput, Tooltip } from "@mantine/core";
import { SkylineModelParameters } from "../../App";
import { SkylineModelParameters } from "../App";
import { useState } from "react";
import { useSceneStore } from "../scene";
import { STLExporter } from "three/examples/jsm/Addons.js";

export interface GenerateOptions {
name: string;
Expand All @@ -9,16 +11,16 @@ export interface GenerateOptions {

interface SidebarProps {
ready: boolean;
// onSubmit(options: GenerateOptions): void;
setParameters: React.Dispatch<React.SetStateAction<SkylineModelParameters>>;
parameters: SkylineModelParameters;
onExport(): void;
}

export function Sidebar(props: SidebarProps) {
const { ready, parameters, setParameters, onExport } = props;
const { ready, parameters, setParameters } = props;
const [name, setName] = useState(parameters.name);
const [year, setYear] = useState(parameters.year);
const { scene, dirty } = useSceneStore();

if (!ready) {
return (
Expand Down Expand Up @@ -57,13 +59,8 @@ export function Sidebar(props: SidebarProps) {
/>
<Button
fullWidth
onClick={() => {
setParameters({
...parameters,
name,
year
})
}}>
onClick={() => setParameters({ ...parameters, name, year })}
>
Generate
</Button>
<Divider />
Expand All @@ -83,6 +80,14 @@ export function Sidebar(props: SidebarProps) {
value={parameters.towerDampening}
onChange={(value) => setParameters({ ...parameters, towerDampening: parseInt(`${value}`) })}
/>
<NumberInput
label="Base Padding"
placeholder="Base Padding"
min={0}
step={0.5}
value={parameters.padding}
onChange={(value) => setParameters({ ...parameters, padding: parseFloat(`${value}`) })}
/>
<Divider />
<ColorInput
label="Render Color"
Expand All @@ -99,9 +104,26 @@ export function Sidebar(props: SidebarProps) {
</AppShell.Section>
<Divider mb={10} />
<AppShell.Section>
<Tooltip label="Work In Progress">
<Button fullWidth>Export</Button>
</Tooltip>
<Button
disabled={scene === null || dirty}
fullWidth
onClick={() => {
if (scene === null) {
return;
}
const clone = scene.clone();
clone.rotation.set(Math.PI / 2, 0, 0);
clone.updateMatrixWorld();
const exporter = new STLExporter();
const data = exporter.parse(clone, { binary: false });
const link = document.createElement("a");
link.href = URL.createObjectURL(new Blob([data], { type: "text/plain" }));
link.download = `${parameters.name}_${parameters.year}_contribution.stl`;
link.click();
}}
>
Export
</Button>
</AppShell.Section>
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function Skyline(props: SkylineModelProps) {
<spotLight position={[10, 100, 40]} angle={0.55} penumbra={0.1} decay={0.4} intensity={Math.PI} />
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
<directionalLight color="#fff" position={[13, 100, 100]} />
<OrbitControls makeDefault minPolarAngle={0} maxPolarAngle={Math.PI / 2.5} />
<OrbitControls makeDefault minPolarAngle={0} maxPolarAngle={Math.PI / 2.5} autoRotate autoRotateSpeed={0.5} />
</Canvas>
);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Center, Text3D, useBounds } from "@react-three/drei";
import { useFrame, useThree } from "@react-three/fiber";
import { useThree } from "@react-three/fiber";
import { ResultOf } from "gql.tada";
import { useEffect, useRef, useState } from "react";
import { Group } from "three";
import { ContributionQuery } from "../../api/query";
import { SkylineModelParameters } from "../../App";
import { useSceneStore } from "../../scene";
import { useEffect } from "react";
import { ContributionQuery } from "../api/query";
import { SkylineModelParameters } from "../App";
import { useSceneStore } from "../scene";
import { ContributionTower } from "./contribution_tower";

export interface SkylineModelProps {
Expand All @@ -22,45 +21,28 @@ export function SkylineModel(props: SkylineModelProps) {
const scene = useThree((state) => state.scene);
const sceneStore = useSceneStore();

const [usingControls, setUsingControls] = useState(false);
const group = useRef<Group>(null!);


const bounds = useBounds();
let boundsTimeout: NodeJS.Timeout | undefined = undefined;
useEffect(() => {
if (boundsTimeout !== undefined) {
clearTimeout(boundsTimeout)
}
sceneStore.setDirty(true);
boundsTimeout = setTimeout(() => {
sceneStore.setScene(scene.clone());
bounds.refresh().clip().fit();
}, 2000);
sceneStore.setDirty(false);
}, 1500);

return () => {
if (boundsTimeout !== undefined) {
clearTimeout(boundsTimeout)
}
}
}, [props.parameters.towerSize, props.parameters.towerDampening])

useFrame((state, delta) => (group.current.rotation.y += 0.001))
// useEffect(() => {
// setInterval(() => {
// sceneStore.setScene(scene.clone());
// }, 1000)
// }, [])
// useEffect(() => {

// setInterval(() => {
// const exporter = new STLExporter();
// const clone = scene.clone();
// clone.rotation.set(Math.PI / 2, 0, 0);
// clone.updateMatrixWorld();
// }, 1000 * 5);
// }, [])
}, [props.parameters.towerSize, props.parameters.towerDampening, props.parameters.name, props.parameters.year, props.parameters.padding]);

return (
<group ref={group}>
<group>
{weeks.map((week, i) =>
week.contributionDays.map((day, j) => (
<ContributionTower
Expand Down
11 changes: 0 additions & 11 deletions packages/app/src/components/ui/controls.tsx

This file was deleted.

8 changes: 0 additions & 8 deletions packages/app/src/components/ui/header.tsx

This file was deleted.

6 changes: 5 additions & 1 deletion packages/app/src/scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { create } from "zustand";

export interface SceneStore {
scene: Scene | null;
dirty: boolean;
setScene(scene: Scene): void;
setDirty(dirty: boolean): void;
}

export const useSceneStore = create<SceneStore>(set => ({
scene: null,
setScene: (scene) => set((state) => ({ scene }))
dirty: false,
setScene: (scene) => set(_ => ({ scene })),
setDirty: (dirty) => set(_ => ({ dirty }))
}));

0 comments on commit b7cab4e

Please sign in to comment.