@tscircuit/3d-viewer 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/tsconfig.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Base Options recommended for all projects
4
+ "esModuleInterop": true,
5
+ "skipLibCheck": true,
6
+ "target": "es2022",
7
+ "baseUrl": ".",
8
+ "allowJs": true,
9
+ "resolveJsonModule": true,
10
+ "moduleDetection": "force",
11
+ "moduleResolution": "Bundler",
12
+ "isolatedModules": true,
13
+ "verbatimModuleSyntax": false,
14
+ "allowImportingTsExtensions": true,
15
+ // Enable strict type checking so you can catch bugs early
16
+ "strict": true,
17
+ "noUncheckedIndexedAccess": true,
18
+ "noImplicitOverride": true,
19
+ "noImplicitAny": false,
20
+ // We are not transpiling, so preserve our source code and do not emit files
21
+ "module": "preserve",
22
+ "jsx": "react-jsx",
23
+ "noEmit": true,
24
+ // Include the DOM types
25
+ "lib": [
26
+ "es2022",
27
+ "dom",
28
+ "dom.iterable"
29
+ ]
30
+ },
31
+ // Include the necessary files for your project
32
+ "include": [
33
+ "**/*.ts",
34
+ "**/*.tsx"
35
+ ],
36
+ "exclude": [
37
+ "node_modules"
38
+ ]
39
+ }
@@ -0,0 +1,252 @@
1
+ // @ts-nocheck
2
+ export function CommonToThree({
3
+ MeshPhongMaterial,
4
+ LineBasicMaterial,
5
+ BufferGeometry,
6
+ BufferAttribute,
7
+ Mesh,
8
+ InstancedMesh,
9
+ Line,
10
+ LineSegments,
11
+ Color,
12
+ Vector3,
13
+ }) {
14
+ const flatShading = false
15
+ const materials: any = {
16
+ mesh: {
17
+ def: new MeshPhongMaterial({ color: 0x0084d1, flatShading }),
18
+ make: (params) => new MeshPhongMaterial({ flatShading, ...params }),
19
+ },
20
+ line: {
21
+ def: new LineBasicMaterial({ color: 0x0000ff }),
22
+ make: (params) => new LineBasicMaterial(params),
23
+ },
24
+ lines: null,
25
+ }
26
+ materials.lines = materials.line
27
+ materials.instance = materials.mesh // todo support instances for lines
28
+
29
+ function _CSG2Three(obj, { smooth = false }) {
30
+ const {
31
+ vertices,
32
+ indices,
33
+ normals,
34
+ color,
35
+ colors,
36
+ isTransparent = false,
37
+ opacity,
38
+ } = obj
39
+ let { transforms } = obj
40
+ const objType = obj.type || "mesh"
41
+
42
+ const materialDef = materials[objType]
43
+ if (!materialDef) {
44
+ console.error(`material not found for type ${objType}`, obj)
45
+ return
46
+ }
47
+ let material = materialDef.def
48
+ const isInstanced = obj.type === "instance"
49
+ if ((color || colors) && !isInstanced) {
50
+ const c = color || colors
51
+ const opts: any = {
52
+ vertexColors: !!colors,
53
+ opacity: c[3] === undefined ? 1 : c[3],
54
+ transparent:
55
+ (color && c[3] !== 1 && c[3] !== undefined) || isTransparent,
56
+ }
57
+ if (opacity) opts.opacity = opacity
58
+ if (!colors) opts.color = _CSG2Three.makeColor(color)
59
+ material = materialDef.make(opts)
60
+ if (opacity) {
61
+ material.transparent = true
62
+ material.opacity = opacity
63
+ }
64
+ }
65
+
66
+ let geo = new BufferGeometry()
67
+ geo.setAttribute("position", new BufferAttribute(vertices, 3))
68
+ if (indices) geo.setIndex(new BufferAttribute(indices, 1))
69
+ if (normals) geo.setAttribute("normal", new BufferAttribute(normals, 3))
70
+ if (smooth)
71
+ geo = toCreasedNormals({ Vector3, BufferAttribute }, geo, Math.PI / 10)
72
+ if (colors)
73
+ geo.setAttribute(
74
+ "color",
75
+ new BufferAttribute(colors, isTransparent ? 4 : 3)
76
+ )
77
+
78
+ let mesh
79
+ switch (objType) {
80
+ case "mesh":
81
+ mesh = new Mesh(geo, material)
82
+ break
83
+ case "instance":
84
+ const { list } = obj
85
+ mesh = new InstancedMesh(
86
+ geo,
87
+ materials.mesh.make({ color: 0x0084d1 }),
88
+ list.length
89
+ )
90
+ list.forEach((item, i) => {
91
+ copyTransformToArray(
92
+ item.transforms,
93
+ mesh.instanceMatrix.array,
94
+ i * 16
95
+ )
96
+ })
97
+ transforms = null
98
+ break
99
+ case "line":
100
+ mesh = new Line(geo, material)
101
+ break
102
+ case "lines":
103
+ // https://threejs.org/docs/#api/en/materials/LineBasicMaterial
104
+ mesh = new LineSegments(geo, material)
105
+ break
106
+ }
107
+ if (transforms && !isInstanced) mesh.applyMatrix4({ elements: transforms })
108
+ return mesh
109
+ }
110
+
111
+ // shortcut for setMatrixAt for InstancedMesh
112
+ function copyTransformToArray(te, array: any[] = [], offset = 0) {
113
+ array[offset] = te[0]
114
+ array[offset + 1] = te[1]
115
+ array[offset + 2] = te[2]
116
+ array[offset + 3] = te[3]
117
+
118
+ array[offset + 4] = te[4]
119
+ array[offset + 5] = te[5]
120
+ array[offset + 6] = te[6]
121
+ array[offset + 7] = te[7]
122
+
123
+ array[offset + 8] = te[8]
124
+ array[offset + 9] = te[9]
125
+ array[offset + 10] = te[10]
126
+ array[offset + 11] = te[11]
127
+
128
+ array[offset + 12] = te[12]
129
+ array[offset + 13] = te[13]
130
+ array[offset + 14] = te[14]
131
+ array[offset + 15] = te[15]
132
+
133
+ return array
134
+ }
135
+
136
+ _CSG2Three.makeColor = (c) => new Color(c[0], c[1], c[2])
137
+ _CSG2Three.materials = materials
138
+ _CSG2Three.setDefColor = (c) => {
139
+ materials.mesh.def = new MeshPhongMaterial({
140
+ color: _CSG2Three.makeColor(c),
141
+ flatShading,
142
+ })
143
+ }
144
+
145
+ return _CSG2Three
146
+ }
147
+
148
+ /** from threejs examples BufferGeometryUtils
149
+ * @param {BufferGeometry} geometry
150
+ * @param {number} tolerance
151
+ * @return {BufferGeometry>}
152
+ */
153
+
154
+ /**
155
+ * Modifies the supplied geometry if it is non-indexed, otherwise creates a new,
156
+ * non-indexed geometry. Returns the geometry with smooth normals everywhere except
157
+ * faces that meet at an angle greater than the crease angle.
158
+ *
159
+ * @param {BufferGeometry} geometry
160
+ * @param {number} [creaseAngle]
161
+ * @return {BufferGeometry}
162
+ */
163
+ function toCreasedNormals(
164
+ { Vector3, BufferAttribute },
165
+ geometry,
166
+ creaseAngle = Math.PI / 3 /* 60 degrees */
167
+ ) {
168
+ const creaseDot = Math.cos(creaseAngle)
169
+ const hashMultiplier = (1 + 1e-10) * 1e2
170
+
171
+ // reusable vectors
172
+ const verts = [new Vector3(), new Vector3(), new Vector3()]
173
+ const tempVec1 = new Vector3()
174
+ const tempVec2 = new Vector3()
175
+ const tempNorm = new Vector3()
176
+ const tempNorm2 = new Vector3()
177
+
178
+ // hashes a vector
179
+ function hashVertex(v) {
180
+ const x = ~~(v.x * hashMultiplier)
181
+ const y = ~~(v.y * hashMultiplier)
182
+ const z = ~~(v.z * hashMultiplier)
183
+ return `${x},${y},${z}`
184
+ }
185
+
186
+ // BufferGeometry.toNonIndexed() warns if the geometry is non-indexed
187
+ // and returns the original geometry
188
+ const resultGeometry = geometry.index ? geometry.toNonIndexed() : geometry
189
+ const posAttr = resultGeometry.attributes.position
190
+ const vertexMap = {}
191
+
192
+ // find all the normals shared by commonly located vertices
193
+ for (let i = 0, l = posAttr.count / 3; i < l; i++) {
194
+ const i3 = 3 * i
195
+ const a = verts[0].fromBufferAttribute(posAttr, i3 + 0)
196
+ const b = verts[1].fromBufferAttribute(posAttr, i3 + 1)
197
+ const c = verts[2].fromBufferAttribute(posAttr, i3 + 2)
198
+
199
+ tempVec1.subVectors(c, b)
200
+ tempVec2.subVectors(a, b)
201
+
202
+ // add the normal to the map for all vertices
203
+ const normal = new Vector3().crossVectors(tempVec1, tempVec2).normalize()
204
+ for (let n = 0; n < 3; n++) {
205
+ const vert = verts[n]
206
+ const hash = hashVertex(vert)
207
+ if (!(hash in vertexMap)) {
208
+ vertexMap[hash] = []
209
+ }
210
+
211
+ vertexMap[hash].push(normal)
212
+ }
213
+ }
214
+
215
+ // average normals from all vertices that share a common location if they are within the
216
+ // provided crease threshold
217
+ const normalArray = new Float32Array(posAttr.count * 3)
218
+ const normAttr = new BufferAttribute(normalArray, 3, false)
219
+ for (let i = 0, l = posAttr.count / 3; i < l; i++) {
220
+ // get the face normal for this vertex
221
+ const i3 = 3 * i
222
+ const a = verts[0].fromBufferAttribute(posAttr, i3 + 0)
223
+ const b = verts[1].fromBufferAttribute(posAttr, i3 + 1)
224
+ const c = verts[2].fromBufferAttribute(posAttr, i3 + 2)
225
+
226
+ tempVec1.subVectors(c, b)
227
+ tempVec2.subVectors(a, b)
228
+
229
+ tempNorm.crossVectors(tempVec1, tempVec2).normalize()
230
+
231
+ // average all normals that meet the threshold and set the normal value
232
+ for (let n = 0; n < 3; n++) {
233
+ const vert = verts[n]
234
+ const hash = hashVertex(vert)
235
+ const otherNormals = vertexMap[hash]
236
+ tempNorm2.set(0, 0, 0)
237
+
238
+ for (let k = 0, lk = otherNormals.length; k < lk; k++) {
239
+ const otherNorm = otherNormals[k]
240
+ if (tempNorm.dot(otherNorm) > creaseDot) {
241
+ tempNorm2.add(otherNorm)
242
+ }
243
+ }
244
+
245
+ tempNorm2.normalize()
246
+ normAttr.setXYZ(i3 + n, tempNorm2.x, tempNorm2.y, tempNorm2.z)
247
+ }
248
+ }
249
+
250
+ resultGeometry.setAttribute("normal", normAttr)
251
+ return resultGeometry
252
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from "vite"
2
+ import react from "@vitejs/plugin-react"
3
+ import tsconfigPaths from "vite-tsconfig-paths"
4
+
5
+ // https://vitejs.dev/config/
6
+ export default defineConfig({
7
+ plugins: [react(), tsconfigPaths()],
8
+ server: {
9
+ proxy: {
10
+ "/easyeda-models": {
11
+ target: "https://modules.easyeda.com/3dmodel/",
12
+ changeOrigin: true,
13
+ rewrite: (path: string) => path.replace(/^\/easyeda-models/, ""),
14
+ },
15
+ },
16
+ },
17
+ })