-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
contribution-model.tsx
101 lines (94 loc) · 3.45 KB
/
contribution-model.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"use client";
// This file is only responsible for rendering the 3D model, data should be passed in as props
import { type GitContribution } from "@git-skyline/common";
import { OrbitControls, PerspectiveCamera } from "@react-three/drei";
import { Canvas, useThree } from "@react-three/fiber";
import { useEffect } from "react";
import { useSceneStore } from "@/app/lib/store";
import { useSearchParams } from "next/navigation";
function normalize(count: number, minHeight = 4, base = 4, offset = 2): number {
switch (true) {
case count === 0:
return minHeight;
case count > 40:
return count;
default:
return count * (base + offset);
}
}
interface PropType {
data: GitContribution;
}
function Skyline({ data, base }: PropType & { base: boolean }): JSX.Element {
const sceneStore = useSceneStore();
const numRows = Math.floor(data.days.length / 7);
const xOffset = (numRows * 12) / 2;
const scene = useThree((state) => state.scene);
const yOffset = 0;
const baseHeight = 10;
useEffect(() => {
setTimeout(() => {
sceneStore.setScene(scene.clone());
}, 1000);
}, []);
return (
<>
{data.days.map((dayData) => {
const height = normalize(dayData.count, base ? 0 : 4); // if model base added, set minimum height to 0
const { year, month, day } = dayData.date;
return (
<mesh
key={`${year}-${month}-${day}`}
position={[
12 * dayData.week - xOffset,
height / 2 + yOffset,
dayData.weekday * 12 - 12 * 3, // center the model in weekday-axis
]}
>
<boxGeometry args={[10, height, 10]} />
<meshStandardMaterial color={dayData.color} />
</mesh>
);
})}
{base && (
<mesh position={[0, -baseHeight / 2 + yOffset + 1, 0]}>
<boxGeometry args={[12 * 54, baseHeight, 12 * 8]} />
<meshStandardMaterial color="#ffffff" />
</mesh>
)}
</>
);
}
export default function ContributionModel({ data }: PropType): JSX.Element {
const searchParams = useSearchParams();
const enableZoom = searchParams.get("enableZoom") === "false" ? false : true; // default to true
const enablePan = searchParams.get("enablePan") === "false" ? false : true; // default to true
const enableBase = searchParams.get("base") === "true" ? true : false;
const enableDamping =
searchParams.get("enableDamping") === "false" ? false : true; // default to true
const autoRotate = searchParams.get("autoRotate");
const autoRotateSpeed = searchParams.get("autoRotateSpeed");
const parsedAutoRotateSpeed = autoRotateSpeed
? parseFloat(autoRotateSpeed)
: 0.5;
const parsedAutoRotate =
autoRotate && parsedAutoRotateSpeed ? autoRotate !== "false" : true;
return (
<Canvas shadows>
<PerspectiveCamera fov={60} makeDefault position={[10, 400, 500]}>
<OrbitControls
autoRotate={parsedAutoRotate}
autoRotateSpeed={parsedAutoRotateSpeed}
enableZoom={enableZoom}
enablePan={enablePan}
enableDamping={enableDamping}
/>
</PerspectiveCamera>
<directionalLight color="#fff" position={[0, 200, 200]} />
<directionalLight color="#fff" position={[-100, 100, 100]} />
<directionalLight color="#fff" position={[200, 100, 100]} />
<directionalLight color="#fff" position={[0, 200, -200]} />
<Skyline data={data} base={enableBase} />
</Canvas>
);
}