comparison mrjunejune/src/public/dog.js @ 84:bcc76a156aea

Updated to be called src instead of pages.
author June Park <parkjune1995@gmail.com>
date Thu, 01 Jan 2026 13:01:10 -0800
parents mrjunejune/pages/public/dog.js@1ded13720541
children
comparison
equal deleted inserted replaced
83:49b611c808e7 84:bcc76a156aea
1 // -- Dog -- //
2 const SCREEN_WIDTH = 800;
3 const SCREEN_HEIGHT = 600;
4 const PIXEL_SIZE = 3;
5 const FRAME = 60;
6
7 const dog = document.getElementById("epi3D");
8 dog.width = SCREEN_WIDTH;
9 dog.height = SCREEN_HEIGHT;
10 const ctx = dog.getContext("2d");
11
12 function drawBackground() {
13 ctx.fillStyle = "black";
14 ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
15 }
16
17 function drawPixel({x, y}) {
18 ctx.fillStyle = "blue";
19 ctx.fillRect(x * SCREEN_WIDTH, y * SCREEN_HEIGHT, PIXEL_SIZE, PIXEL_SIZE);
20 }
21
22 function normalize({x, y}) {
23 return {
24 x: ((x + 1) / 2),
25 y: (1 - ((y + 1) / 2)),
26 }
27 }
28
29 function threeDtotwoD({x, y, z}) {
30 return {
31 x: x/z,
32 y: y/z
33 }
34 }
35
36 function drawLine(point1, point2) {
37 ctx.beginPath();
38 ctx.moveTo(point1.x * SCREEN_WIDTH, point1.y * SCREEN_HEIGHT);
39 ctx.lineTo(point2.x * SCREEN_WIDTH, point2.y * SCREEN_HEIGHT);
40 ctx.lineWidth = 3;
41 ctx.strokeStyle = "red";
42 ctx.stroke();
43 }
44
45 let points = [];
46 let vertices = [];
47
48 async function loadXYZModel(filepath) {
49 try {
50 const response = await fetch(filepath);
51 const xyzContent = await response.text();
52 // return parseXYZFile(xyzContent);
53 return parseSmartSTL(xyzContent);
54 } catch (error) {
55 console.error('Error loading XYZ file:', error);
56 return null;
57 }
58 }
59
60 function downsamplePoints(points, gridSize = 0.2) {
61 const grid = new Map();
62
63 for (const point of points) {
64 // Create grid cell key
65 const cellX = Math.floor(point.x / gridSize);
66 const cellY = Math.floor(point.y / gridSize);
67 const cellZ = Math.floor(point.z / gridSize);
68 const key = `${cellX},${cellY},${cellZ}`;
69
70 // Keep first point in each cell (or you could average them)
71 if (!grid.has(key)) {
72 grid.set(key, point);
73 }
74 }
75
76 return Array.from(grid.values());
77 }
78
79 async function initModel() {
80 const model = await loadXYZModel('/public/dog.xyz');
81
82 if (model) {
83 const normalizedPoints = normalizePoints(model.points, 1.0);
84 points = downsamplePoints(normalizedPoints, 0.1); // grid method
85
86 vertices = model.vertices;
87 for (let i = 0; i < points.length - 1; i++) {
88 vertices.push([i, i + 1]);
89 }
90
91 // Start the animation after loading
92 drawAnimation();
93 } else {
94 console.error('Failed to load model');
95 }
96 }
97
98 function parseSmartSTL(stlString) {
99 const lines = stlString.split('\n');
100 const points = [];
101 const vortexs = [];
102
103 let currentNormal = "";
104 let tempVertices = [];
105
106 lines.forEach((line) => {
107 const trimmed = line.trim();
108
109 // 1. Detect the "Angle" (Normal)
110 if (trimmed.startsWith('facet normal')) {
111 const normal = trimmed.replace('facet normal ', '');
112 // Only care if the normal is different from the last one (it's a new angle)
113 // Or just keep them all for now and we'll filter by position
114 currentNormal = normal;
115 tempVertices = [];
116 }
117
118 // 2. Grab the vertices
119 if (trimmed.startsWith('vertex')) {
120 const parts = trimmed.split(/\s+/);
121 tempVertices.push({
122 x: parseFloat(parts[1]),
123 y: parseFloat(parts[2]),
124 z: parseFloat(parts[3])
125 });
126 }
127
128 // 3. When the triangle ends, connect them
129 if (trimmed.startsWith('endloop')) {
130 const startIndex = points.length;
131 points.push(...tempVertices);
132
133 // Create a triangle connection (vortex)
134 vortexs.push([startIndex, startIndex + 1, startIndex + 2]);
135 }
136 });
137
138 return { points, vortexs };
139 }
140
141 // Usage:
142 // const { points, vortexs } = parseSmartSTL(yourStlString);
143
144 initModel();
145
146 function parseXYZFile(xyzFileContent) {
147 const lines = xyzFileContent.trim().split('\n').filter(line => line.trim());
148
149 const points = lines.map(line => {
150 const coords = line.trim().split(/\s+/).map(Number);
151 return {
152 x: coords[0],
153 y: coords[1],
154 z: coords[2]
155 };
156 });
157
158 return { points };
159 }
160
161 function normalizePoints(points, scale = 1.0) {
162 const xs = points.map(p => p.x);
163 const ys = points.map(p => p.y);
164 const zs = points.map(p => p.z);
165
166 const minX = Math.min(...xs), maxX = Math.max(...xs);
167 const minY = Math.min(...ys), maxY = Math.max(...ys);
168 const minZ = Math.min(...zs), maxZ = Math.max(...zs);
169
170 const centerX = (minX + maxX) / 2;
171 const centerY = (minY + maxY) / 2;
172 const centerZ = (minZ + maxZ) / 2;
173
174 const rangeX = maxX - minX;
175 const rangeY = maxY - minY;
176 const rangeZ = maxZ - minZ;
177 const maxRange = Math.max(rangeX, rangeY, rangeZ);
178
179 return points.map(p => ({
180 x: ((p.x - centerX) / maxRange) * scale,
181 y: ((p.y - centerY) / maxRange) * scale,
182 z: ((p.z - centerZ) / maxRange) * scale
183 }));
184 }
185
186 function rotate_xz({x, y, z}, angle) {
187 return {
188 x: x * Math.cos(angle) - z * Math.sin(angle),
189 y: y,
190 z: x * Math.sin(angle) + z * Math.cos(angle),
191 }
192 }
193
194 function move_point(point, { x, y, z}) {
195 return {
196 ...point,
197 x: point.x + x,
198 y: 1 - (point.y + y),
199 z: point.z + z
200 }
201 }
202
203 function move_z(point, dz) {
204 return {...point, z: point.z + dz}
205 }
206
207 let dz = 0;
208 let dt = 1/FRAME;
209 let angle = 0;
210
211 function drawAnimation() {
212 drawBackground();
213 // dz += 1 * dt;
214 angle += 1 * Math.PI * dt;
215
216 for (const point of points) {
217 drawPixel(
218 threeDtotwoD(
219 move_point(rotate_xz(point, angle), { x: 0.7, y: 0.5, z: 1.5})
220 )
221 );
222 }
223
224 for (const vertex of vertices) {
225 for (let i = 0; i < vertex.length - 1; i++) {
226 const point1 = normalize(
227 threeDtotwoD(
228 move_point(rotate_xz(points[vertex[i]], angle), { x: 0.7, y: 0.5, z: 1.5})
229 )
230 );
231 const point2 = normalize(
232 threeDtotwoD(
233 move_point(rotate_xz(points[vertex[i + 1]], angle), { x: 0.7, y: 0.5, z: 1.5})
234 )
235 );
236 drawLine(point1, point2);
237 }
238 }
239
240 setTimeout(() => drawAnimation(), 1000/60);
241 }
242 drawAnimation();