ahnobari commited on
Commit
460c05d
1 Parent(s): b2c81b6
LInK/CAD.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ def get_3d_config(A,x0,nt,z_index):
4
+
5
+ A, x0, nt, z_index = np.array(A), np.array(x0), np.array(nt), np.array(z_index)
6
+
7
+ n_joints = (A.sum(-1)>0).sum()
8
+ A = A[:n_joints,:][:,:n_joints]
9
+ x0 = x0[:n_joints]
10
+ nt = nt[:n_joints]
11
+ n_links = int(A.sum()/2)
12
+
13
+ l1,l2 = np.where(np.triu(A))
14
+
15
+ linkages = []
16
+
17
+ max_len = 0
18
+ min_len = float(np.inf)
19
+
20
+ for j in range(n_links):
21
+ length= np.linalg.norm(x0[l1[j]]-x0[l2[j]])
22
+ if length>max_len:
23
+ max_len = float(length)
24
+ if length<min_len:
25
+ min_len = float(length)
26
+
27
+ scale_min = 0.25/min_len
28
+ scale_target = 1.0/max_len
29
+ scale = max(scale_min,scale_target)
30
+
31
+ x0 = x0*scale
32
+
33
+ for j in range(n_links):
34
+ length= np.linalg.norm(x0[l1[j]]-x0[l2[j]])
35
+ angle = np.arctan2(x0[l2[j]][1]-x0[l1[j]][1],x0[l2[j]][0]-x0[l1[j]][0])
36
+ linkages.append([length,0.1,0.05,0.03, angle, x0[l1[j]].tolist()+[float(z_index[j])*0.05]])
37
+
38
+ joints_max_z = np.zeros(x0.shape[0])
39
+ joints_min_z = np.zeros(x0.shape[0]) + np.inf
40
+
41
+ for i in range(n_links):
42
+ joints_max_z[l1[i]] = max(joints_max_z[l1[i]],z_index[i])
43
+ joints_max_z[l2[i]] = max(joints_max_z[l2[i]],z_index[i])
44
+ joints_min_z[l1[i]] = min(joints_min_z[l1[i]],z_index[i])
45
+ joints_min_z[l2[i]] = min(joints_min_z[l2[i]],z_index[i])
46
+
47
+ joints_max_z = joints_max_z*0.05
48
+ joints_min_z = joints_min_z*0.05
49
+
50
+ joints = []
51
+ for i in range(x0.shape[0]):
52
+ if nt[i] == 1:
53
+ for j in np.where(l1==i)[0]:
54
+ joints.append(x0[i].tolist()+[0.05,z_index[j]*0.05,1])
55
+ for j in np.where(l2==i)[0]:
56
+ joints.append(x0[i].tolist()+[0.05,z_index[j]*0.05,1])
57
+ else:
58
+ joints.append(x0[i].tolist()+[float(joints_max_z[i]-joints_min_z[i])+0.05,float(joints_min_z[i]+joints_max_z[i])/2,0])
59
+
60
+ return [linkages, joints], joints_max_z, scale
61
+
62
+ def get_animated_3d_config(A,x0,nt,z_index,sol, highlights = [-1]):
63
+ A, x0, nt, z_index, sol = np.array(A), np.array(x0), np.array(nt), np.array(z_index), np.array(sol)
64
+ configs = []
65
+ for i in range(sol.shape[1]):
66
+ c,z,s = get_3d_config(A,sol[:,i,:],nt,z_index)
67
+ configs.append(c)
68
+
69
+ if len(highlights) > 1:
70
+ highligh_curve = []
71
+ for i in highlights:
72
+ highligh_curve.append(np.pad(sol[i,:,:]*s,[[0,0],[0,1]],constant_values=z[i]+0.025))
73
+ highligh_curve = np.array(highligh_curve)
74
+ else:
75
+ highligh_curve = np.pad(sol[highlights[0],:,:]*s,[[0,0],[0,1]],constant_values=z[-1]+0.025)
76
+
77
+ return configs, highligh_curve.tolist()
78
+
79
+ def create_3d_html(A,x0,nt,z_index,sol, template_path='./static/animation.htm', save_path='./static/animated.html', highlights = [-1]):
80
+
81
+ res,hc = get_animated_3d_config(A,x0,nt,z_index,sol,highlights=highlights)
82
+ js_var = 'window.res = ' + str(res) + ';\n'
83
+ js_var += 'window.hc = ' + str(hc) + ';'
84
+ js_var += 'window.multi_high = ' + str(int(len(highlights)>1)) + ';'
85
+
86
+ with open(template_path, 'r') as file:
87
+ filedata = file.read()
88
+
89
+ filedata = filedata.replace('{res}',js_var)
90
+
91
+ with open(save_path, 'w') as file:
92
+ file.write(filedata)
LInK/CurveUtils.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ def uniformize(curves: torch.tensor, n: int = 200) -> torch.tensor:
4
+ with torch.no_grad():
5
+ l = torch.cumsum(torch.nn.functional.pad(torch.norm(curves[:,1:,:] - curves[:,:-1,:],dim=-1),[1,0,0,0]),-1)
6
+ l = l/l[:,-1].unsqueeze(-1)
7
+
8
+ sampling = torch.linspace(0,1,n).to(l.device).unsqueeze(0).tile([l.shape[0],1])
9
+ end_is = torch.searchsorted(l,sampling)[:,1:]
10
+ end_ids = end_is.unsqueeze(-1).tile([1,1,2])
11
+
12
+ l_end = torch.gather(l,1,end_is)
13
+ l_start = torch.gather(l,1,end_is-1)
14
+ ws = (l_end - sampling[:,1:])/(l_end-l_start)
15
+
16
+ end_gather = torch.gather(curves,1,end_ids)
17
+ start_gather = torch.gather(curves,1,end_ids-1)
18
+
19
+ uniform_curves = torch.cat([curves[:,0:1,:],(end_gather - (end_gather-start_gather)*ws.unsqueeze(-1))],1)
20
+
21
+ return uniform_curves
LInK/Solver.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ # Dyadic Solution Path Finder
4
+ def find_path(A, motor = [0,1], fixed_nodes=[0, 1]):
5
+ '''
6
+ This function finds the solution path of a dyadic mechanism.
7
+
8
+ Parameters:
9
+ A (np.array): Adjacency matrix of the mechanism.
10
+ motor (list): motor nodes.
11
+ fixed_nodes (list): List of fixed nodes.
12
+
13
+ Returns:
14
+ path (np.array): Solution path of the mechanism.
15
+ status (bool): True if the mechanism is dyadic and has a solution path, False otherwise.
16
+ '''
17
+
18
+ path = []
19
+
20
+ A,fixed_nodes,motor = np.array(A),np.array(fixed_nodes),np.array(motor)
21
+
22
+ unkowns = np.array(list(range(A.shape[0])))
23
+ knowns = np.concatenate([fixed_nodes,[motor[-1]]])
24
+
25
+ unkowns = unkowns[np.logical_not(np.isin(unkowns,knowns))]
26
+
27
+ counter = 0
28
+ while unkowns.shape[0] != 0:
29
+
30
+ if counter == unkowns.shape[0]:
31
+ # Non dyadic or DOF larger than 1
32
+ return [], False
33
+ n = unkowns[counter]
34
+ ne = np.where(A[n])[0]
35
+
36
+ kne = knowns[np.isin(knowns,ne)]
37
+ # print(kne.shape[0])
38
+
39
+ if kne.shape[0] == 2:
40
+
41
+ path.append([n,kne[0],kne[1]])
42
+ counter = 0
43
+ knowns = np.concatenate([knowns,[n]])
44
+ unkowns = unkowns[unkowns!=n]
45
+ elif kne.shape[0] > 2:
46
+ #redundant or overconstraint
47
+ return [], False
48
+ else:
49
+ counter += 1
50
+
51
+ return np.array(path), True
52
+
53
+ # Dyadic Mechanism Sorting
54
+ def get_order(A, motor = [0,1], fixed_nodes=[0, 1]):
55
+ '''
56
+ This function sorts the mechanism based on the solution path.
57
+
58
+ Parameters:
59
+ A (np.array): Adjacency matrix of the mechanism.
60
+ motor (list): motor nodes.
61
+ fixed_nodes (list): List of fixed nodes.
62
+
63
+ Returns:
64
+ joint order (np.array): Sorted order of the joints in a mechanism.
65
+ '''
66
+ path, status = find_path(A, motor, fixed_nodes)
67
+ fixed_nodes = np.array(fixed_nodes)
68
+ if status:
69
+ return np.concatenate([motor,fixed_nodes[fixed_nodes!=motor[0]],path[:,0]])
70
+ else:
71
+ raise Exception("Non Dyadic or Dof larger than 1")
72
+
73
+ def sort_mechanism(A, x0, motor = [0,1], fixed_nodes=[0, 1]):
74
+ '''
75
+ This function sorts the mechanism based on the solution path.
76
+
77
+ Parameters:
78
+ A (np.array): Adjacency matrix of the mechanism.
79
+ x0 (np.array): Initial positions of the joints.
80
+ motor (list): motor nodes.
81
+ fixed_nodes (list): List of fixed nodes.
82
+
83
+ Returns:
84
+ A_s (np.array): Sorted adjacency matrix of the mechanism.
85
+ x0 (np.array): Sorted initial positions of the joints.
86
+ motor (np.array): Motor nodes.
87
+ fixed_nodes (np.array): Fixed nodes.
88
+ ord (np.array): Sorted order of the joints in a mechanism.
89
+ '''
90
+ ord = get_order(A, motor, fixed_nodes)
91
+
92
+ n_t = np.zeros(A.shape[0])
93
+ n_t[fixed_nodes] = 1
94
+
95
+ A_s = A[ord,:][:,ord]
96
+ n_t_s = n_t[ord]
97
+
98
+ return A_s, x0[ord], np.array([0,1]), np.where(n_t_s)[0], ord
99
+
100
+ # Vectorized Dyadic Solver
101
+ def solve_rev_vectorized_batch_CPU(As,x0s,node_types,thetas):
102
+
103
+ Gs = np.square((np.expand_dims(x0s,1) - np.expand_dims(x0s,2))).sum(-1)
104
+
105
+ x = np.zeros([x0s.shape[0],x0s.shape[1],thetas.shape[0],2])
106
+
107
+ x = x + np.expand_dims(node_types * x0s,2)
108
+
109
+ m = x[:,0] + np.tile(np.expand_dims(np.swapaxes(np.concatenate([np.expand_dims(np.cos(thetas),0),np.expand_dims(np.sin(thetas),0)],0),0,1),0),[x0s.shape[0],1,1]) * np.expand_dims(np.expand_dims(np.sqrt(Gs[:,0,1]),-1),-1)
110
+
111
+ m = np.expand_dims(m,1)
112
+ m = np.pad(m,[[0,0],[1,x0s.shape[1]-2],[0,0],[0,0]],mode='constant')
113
+ x += m
114
+
115
+ for k in range(3,x0s.shape[1]):
116
+
117
+ inds = np.argsort(As[:,k,0:k])[:,-2:]
118
+
119
+ l_ijs = np.linalg.norm(x[np.arange(x0s.shape[0]),inds[:,0]] - x[np.arange(x0s.shape[0]),inds[:,1]], axis=-1)
120
+
121
+ gik = np.sqrt(np.expand_dims(Gs[np.arange(x0s.shape[0]),inds[:,0],np.ones(shape=[x0s.shape[0]],dtype=int)*k],-1))
122
+ gjk = np.sqrt(np.expand_dims(Gs[np.arange(x0s.shape[0]),inds[:,1],np.ones(shape=[x0s.shape[0]],dtype=int)*k],-1))
123
+
124
+ cosphis = (np.square(l_ijs) + np.square(gik) - np.square(gjk))/(2 * l_ijs * gik)
125
+
126
+ cosphis = np.where(np.tile(node_types[:,k],[1,thetas.shape[0]])==0.0,cosphis,np.zeros_like(cosphis))
127
+
128
+ x0i1 = x0s[np.arange(x0s.shape[0]),inds[:,0],np.ones(shape=[x0s.shape[0]]).astype(np.int32)]
129
+ x0i0 = x0s[np.arange(x0s.shape[0]),inds[:,0],np.zeros(shape=[x0s.shape[0]]).astype(np.int32)]
130
+
131
+ x0j1 = x0s[np.arange(x0s.shape[0]),inds[:,1],np.ones(shape=[x0s.shape[0]]).astype(np.int32)]
132
+ x0j0 = x0s[np.arange(x0s.shape[0]),inds[:,1],np.zeros(shape=[x0s.shape[0]]).astype(np.int32)]
133
+
134
+ x0k1 = x0s[:,k,1]
135
+ x0k0 = x0s[:,k,0]
136
+
137
+ s = np.expand_dims(np.sign((x0i1-x0k1)*(x0i0-x0j0) - (x0i1-x0j1)*(x0i0-x0k0)),-1)
138
+
139
+
140
+ phi = s * np.arccos(cosphis)
141
+
142
+ a = np.transpose(np.concatenate([np.expand_dims(np.cos(phi),0),np.expand_dims(-np.sin(phi),0)],0),axes=[1,2,0])
143
+ b = np.transpose(np.concatenate([np.expand_dims(np.sin(phi),0),np.expand_dims(np.cos(phi),0)],0),axes=[1,2,0])
144
+
145
+ R = np.einsum("ijk...->jki...", np.concatenate([np.expand_dims(a,0),np.expand_dims(b,0)],0))
146
+
147
+ xi = x[np.arange(x0s.shape[0]),inds[:,0]]
148
+ xj = x[np.arange(x0s.shape[0]),inds[:,1]]
149
+
150
+ scaled_ij = (xj-xi)/np.expand_dims(l_ijs,-1) * np.expand_dims(gik,-1)
151
+
152
+ x_k = np.squeeze(np.matmul(R, np.expand_dims(scaled_ij,-1))) + xi
153
+ x_k = np.where(np.tile(np.expand_dims(node_types[:,k],-1),[1,thetas.shape[0],2])==0.0,x_k,np.zeros_like(x_k))
154
+
155
+ x_k = np.expand_dims(x_k,1)
156
+ x_k = np.pad(x_k,[[0,0],[k,x0s.shape[1]-k-1],[0,0],[0,0]],mode='constant')
157
+
158
+ x += x_k
159
+ return x
160
+
161
+ # Solve a single mechanism
162
+ def solve_mechanism(A, x0 , motor = [0,1], fixed_nodes=[0, 1], thetas = np.linspace(0,2*np.pi,200)):
163
+
164
+ A,x0,motor,fixed_nodes,ord = sort_mechanism(A, x0, motor, fixed_nodes)
165
+ n_t = np.zeros([A.shape[0],1])
166
+ n_t[fixed_nodes] = 1
167
+
168
+ A = np.expand_dims(A,0)
169
+ x0 = np.expand_dims(x0,0)
170
+ n_t = np.expand_dims(n_t,0)
171
+
172
+ sol = solve_rev_vectorized_batch_CPU(A,x0,n_t,thetas)
173
+
174
+ return sol[0], ord
LInK/Visulization.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .Solver import solve_mechanism
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ from io import StringIO
5
+ import xml.etree.ElementTree as etree
6
+ from svgpath2mpl import parse_path
7
+
8
+ def draw_mechanism(A,x0,fixed_nodes=None,motor=[0,1],node_types=None, ax=None, highlight=None, solve=True, thetas = np.linspace(0,np.pi*2,200), def_alpha = 1.0, h_alfa =1.0, h_c = "#f15a24"):
9
+
10
+ if fixed_nodes is None and node_types is None:
11
+ raise ValueError("Either fixed_nodes or node_types should be provided")
12
+
13
+ if fixed_nodes is None:
14
+ fixed_nodes = np.where(node_types)[0]
15
+
16
+ def fetch_path():
17
+ # r = requests.get(svg_url)
18
+ root = etree.parse(StringIO('<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 620 338"><defs><style>.cls-1{fill:#1a1a1a;stroke:#1a1a1a;stroke-linecap:round;stroke-miterlimit:10;stroke-width:20px;}</style></defs><path class="cls-1" d="M45.5,358.5l70.71-70.71M46,287.5H644m-507.61,71,70.72-70.71M223,358.5l70.71-70.71m20.18,70.72,70.71-70.71m13.67,70.7,70.71-70.71m20.19,70.72,70.71-70.71m15.84,70.71,70.71-70.71M345,39.62A121.38,121.38,0,1,1,223.62,161,121.38,121.38,0,0,1,345,39.62Z" transform="translate(-35.5 -29.62)"/></svg>')).getroot()
19
+ view_box = root.attrib.get('viewBox')
20
+ if view_box is not None:
21
+ view_box = [int(x) for x in view_box.split()]
22
+ xlim = (view_box[0], view_box[0] + view_box[2])
23
+ ylim = (view_box[1] + view_box[3], view_box[1])
24
+ else:
25
+ xlim = (0, 500)
26
+ ylim = (500, 0)
27
+ path_elem = root.findall('.//{http://www.w3.org/2000/svg}path')[0]
28
+ return xlim, ylim, parse_path(path_elem.attrib['d'])
29
+ _,_,p = fetch_path()
30
+ p.vertices -= p.vertices.mean(axis=0)
31
+ p.vertices = (np.array([[np.cos(np.pi), -np.sin(np.pi)],[np.sin(np.pi), np.cos(np.pi)]])@p.vertices.T).T
32
+
33
+ if ax is None:
34
+ fig, ax = plt.subplots(figsize=(10,10))
35
+
36
+ A,x0,fixed_nodes,motor = np.array(A),np.array(x0),np.array(fixed_nodes),np.array(motor)
37
+
38
+ x = x0
39
+
40
+ N = A.shape[0]
41
+ for i in range(N):
42
+ if i in fixed_nodes:
43
+ if i == highlight:
44
+ ax.scatter(x[i,0],x[i,1],color=h_c,s=700,zorder=10,marker=p)
45
+ else:
46
+ ax.scatter(x[i,0],x[i,1],color="#1a1a1a",s=700,zorder=10,marker=p)
47
+ else:
48
+ if i == highlight:
49
+ ax.scatter(x[i,0],x[i,1],color=h_c,s=100,zorder=10,facecolors=h_c,alpha=0.7)
50
+ else:
51
+ ax.scatter(x[i,0],x[i,1],color="#1a1a1a",s=100,zorder=10,facecolors='#ffffff',alpha=0.7)
52
+
53
+ for j in range(i+1,N):
54
+ if A[i,j]:
55
+ if (motor[0] == i and motor[1] == j) or(motor[0] == j and motor[1] == i):
56
+ ax.plot([x[i,0],x[j,0]],[x[i,1],x[j,1]],color="#ffc800",linewidth=4.5)
57
+ else:
58
+ ax.plot([x[i,0],x[j,0]],[x[i,1],x[j,1]],color="#1a1a1a",linewidth=4.5,alpha=0.6)
59
+
60
+ if solve:
61
+ sol,ord = solve_mechanism(A,x0,motor,fixed_nodes,thetas)
62
+ x = sol
63
+
64
+ for i in range(A.shape[0]):
65
+ if not ord[i] in fixed_nodes:
66
+ if ord[i] == highlight:
67
+ ax.plot(x[i,:,0],x[i,:,1],'-',color=h_c,linewidth=4.5,alpha=h_alfa)
68
+ else:
69
+ ax.plot(x[i,:,0],x[i,:,1],'--',color="#0078a7",linewidth=1.5, alpha=def_alpha)
70
+ ax.axis('equal')
71
+ ax.axis('off')
72
+
73
+ return ax
LInK/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ '''Code Base For LInK Project'''
alpha_res.npy ADDED
Binary file (473 kB). View file
 
alpha_z.npy ADDED
Binary file (7.16 kB). View file
 
alphabet.npy ADDED
Binary file (83.3 kB). View file
 
app.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import argparse
4
+
5
+ argparser = argparse.ArgumentParser()
6
+ argparser.add_argument("--port", type=int, default=1239, help="Port number for the local server")
7
+ argparser.add_argument("--cuda_device", type=str, default='0', help="Cuda devices to use. Default is 0")
8
+ argparser.add_argument("--static_folder", type=str, default='static', help="Folder to store static files")
9
+ args = argparser.parse_args()
10
+
11
+ os.environ["CUDA_VISIBLE_DEVICES"] = ''
12
+
13
+ import gradio as gr
14
+ from pathlib import Path
15
+
16
+ import numpy as np
17
+ import torch
18
+
19
+ from LInK.CAD import create_3d_html
20
+ import numpy as np
21
+ from LInK.CurveUtils import uniformize
22
+ import torch
23
+ import matplotlib.pyplot as plt
24
+
25
+ from LInK.Solver import solve_rev_vectorized_batch_CPU
26
+
27
+ # turn off gradient computation
28
+ torch.set_grad_enabled(False)
29
+
30
+ results = np.load('alpha_res.npy',allow_pickle=True)
31
+ alphabet_test = np.load('alphabet.npy')
32
+ zs_ = np.load('alpha_z.npy',allow_pickle=True)
33
+
34
+ for i in range(zs_.shape[0]):
35
+ zs_[i] = zs_[i] - zs_[i].min()
36
+
37
+ curves__ = torch.tensor(alphabet_test).float()
38
+ curves__ = curves__ - curves__.mean(1).unsqueeze(1)
39
+ max_idx = torch.square(curves__).sum(-1).argmax(dim=1)
40
+ theta = torch.atan2(curves__[torch.arange(curves__.shape[0]),max_idx,1],curves__[torch.arange(curves__.shape[0]),max_idx,0]).numpy()
41
+
42
+ curves_ = []
43
+ for i in range(len(results)):
44
+ curves_.append(results[i][-1])
45
+
46
+ curves_ = torch.tensor(curves_).float()
47
+ curves_ = uniformize(curves_,200)
48
+ curves_ = curves_ - curves_.mean(1).unsqueeze(1)
49
+ max_idx = torch.square(curves_).sum(-1).argmax(dim=1)
50
+ theta2 = torch.atan2(curves_[torch.arange(curves_.shape[0]),max_idx,1],curves_[torch.arange(curves_.shape[0]),max_idx,0]).numpy()
51
+
52
+ alphas = []
53
+ letter_heights = []
54
+ letter_centers = []
55
+ letter_widths = []
56
+
57
+ for i in range(len(results)):
58
+ A, x0, node_type, start_theta, end_theta, tr = results[i][0]
59
+ alpha = theta[i] - theta2[i]
60
+ if i == 21:
61
+ alpha -= np.pi/2.5
62
+ if i == 7:
63
+ alpha -= np.pi/3
64
+ if i == 4:
65
+ alpha += np.pi/36
66
+ alphas.append(alpha)
67
+ R = np.array([[np.cos(alpha), -np.sin(alpha)],[np.sin(alpha), np.cos(alpha)]]).squeeze()
68
+ transformed_curve = (R@results[i][-1].T).T
69
+ CD,OD,_ = results[i][1]
70
+ sol = solve_rev_vectorized_batch_CPU(A[None],x0[None],node_type[None],np.linspace(start_theta,end_theta,2000))[0]
71
+ sol_curve = (R@sol[-1].T).T
72
+
73
+ n_left = len(results) - i
74
+ n_left_row = 10 - i%10
75
+
76
+ letter_heights.append(transformed_curve[:,1].max()-transformed_curve[:,1].min())
77
+ letter_widths.append(transformed_curve[:,0].max()-transformed_curve[:,0].min())
78
+ letter_centers.append([(transformed_curve[:,0].max() + transformed_curve[:,0].min())/2,(transformed_curve[:,1].max() + transformed_curve[:,1].min())/2])
79
+
80
+ alphas = np.array(alphas)
81
+ letter_heights = np.array(letter_heights)
82
+ letter_centers = np.array(letter_centers)
83
+ letter_widths = np.array(letter_widths)
84
+
85
+ alphabet_dict = {'A':0,'B':1,'C':2,'D':3,'E':4,'F':5,'G':6,'H':7,'I':8,'J':9,'K':10,'L':11,'M':12,'N':13,'O':14,'P':16,'Q':15,'R':17,'S':18,'T':19,'U':20,'V':21,'W':22,'X':23,'Y':24,'Z':25}
86
+
87
+ def create_mech(target_text):
88
+ target_text = target_text.replace(' ','').upper()
89
+ target_height = 1.
90
+ spacing = 0.2
91
+ letters = [alphabet_dict[l] for l in target_text]
92
+
93
+ translations = []
94
+ scaling = []
95
+
96
+ transformed_curves = []
97
+
98
+ mechs = []
99
+
100
+ total_size = 0
101
+
102
+ for i,l in enumerate(letters):
103
+ A, x0, node_type, start_theta, end_theta, tr = results[l][0]
104
+ alpha = alphas[l]
105
+ R = np.array([[np.cos(alpha), -np.sin(alpha)],[np.sin(alpha), np.cos(alpha)]]).squeeze()
106
+ transformed_curve = (R@results[l][-1].T).T
107
+
108
+ s = target_height/letter_heights[l]
109
+ scaling.append(s)
110
+
111
+ if i>0:
112
+ trans = [translations[-1][0] + letter_widths[letters[i-1]]/2 * scaling[i-1] + letter_widths[l]/2 * s + spacing , 0]
113
+ else:
114
+ trans = [letter_widths[l]/2 * s ,0]
115
+
116
+ translations.append(trans)
117
+
118
+ transformed_curves.append(s*(transformed_curve - letter_centers[l]) + trans)
119
+
120
+ mechs.append([A,s*((R@x0.T).T - letter_centers[l]) + trans,node_type,start_theta+alpha,end_theta+alpha])
121
+ total_size += A.shape[0]
122
+
123
+ A_all = np.zeros((total_size,total_size))
124
+ x0_all = np.zeros((total_size,2))
125
+ node_type_all = np.zeros((total_size,1))
126
+
127
+ current_count = 0
128
+ sols = []
129
+ highlights = []
130
+ zs = []
131
+ for i,m in enumerate(mechs):
132
+ A, x0, node_type, start_theta, end_theta = m
133
+ A_all[current_count:current_count+A.shape[0],current_count:current_count+A.shape[0]] = A
134
+ # x0_all[current_count:current_count+A.shape[0]] = x0
135
+ node_type_all[current_count:current_count+A.shape[0]] = node_type
136
+
137
+ if i ==0:
138
+ highlights.append(A.shape[0]-1)
139
+ else:
140
+ highlights.append(A.shape[0]+highlights[-1])
141
+
142
+ sol = solve_rev_vectorized_batch_CPU(A[None],x0[None],node_type[None],np.linspace(start_theta,end_theta,100))[0]
143
+ sols.append(sol.transpose(1,0,2))
144
+
145
+ x0_all[current_count:current_count+A.shape[0]] = sol[:,0,:]
146
+ current_count += A.shape[0]
147
+ z = zs_[letters[i]]
148
+ zs.append(z + zs[-1].max() + 1 if i>0 else z)
149
+
150
+ sols = np.concatenate(sols,axis=1)
151
+ zs = np.concatenate(zs)
152
+
153
+ uuid_ = str(uuid.uuid4())
154
+
155
+ create_3d_html(A_all, x0_all, node_type_all, zs, np.concatenate([sols.transpose(1,0,2),sols.transpose(1,0,2)[:,::-1,:]],1), template_path = f'./static/animation.html', save_path=f'./static/{uuid_}.html', highlights=highlights)
156
+
157
+ return gr.HTML(f'<iframe width="100%" height="800px" src="file=static/{uuid_}.html"></iframe>',label="3D Plot",elem_classes="plot3d")
158
+
159
+ gr.set_static_paths(paths=[Path(f'./{args.static_folder}')])
160
+
161
+ with gr.Blocks() as block:
162
+ with gr.Row():
163
+ with gr.Column():
164
+ text = gr.Textbox(label="Enter a word (spaces will be ignored)", value='DECODE')
165
+ btn = gr.Button(value="Create Mechanism", variant="primary")
166
+
167
+ plot_3d = gr.HTML('<iframe width="100%" height="800px" src="file=static/filler.html"></iframe>',label="3D Plot",elem_classes="plot3d")
168
+
169
+ event1 = btn.click(create_mech, inputs=[text], outputs=[plot_3d])
170
+
171
+ block.launch(server_port=args.port)
172
+
static/animation.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html>
2
+ <head>
3
+ <title>Mechanism Animation</title>
4
+ <script type="importmap">
5
+ {
6
+ "imports": {
7
+ "three": "https://cdn.jsdelivr.net/npm/three@0.164.1/build/three.module.js",
8
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.164.1/examples/jsm/"
9
+ }
10
+ }
11
+ </script>
12
+ <!--Import style.css-->
13
+ <link rel="stylesheet" type="text/css" href="style.css">
14
+
15
+ </head>
16
+ <body>
17
+ <div id="container"></div>
18
+ <script>
19
+ {res}
20
+ </script>
21
+ <script src="script.js" type="module"></script>
22
+ </body>
23
+ </html>
static/csglib.js ADDED
@@ -0,0 +1,481 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ // ## License
3
+ //
4
+ // Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license.
5
+ // THREE.js rework by thrax
6
+
7
+ // # class CSG
8
+ // Holds a binary space partition tree representing a 3D solid. Two solids can
9
+ // be combined using the `union()`, `subtract()`, and `intersect()` methods.
10
+
11
+
12
+ class CSG {
13
+ constructor() {
14
+ this.polygons = [];
15
+ }
16
+ clone() {
17
+ let csg = new CSG();
18
+ csg.polygons = this.polygons.map(p=>p.clone())
19
+ return csg;
20
+ }
21
+
22
+ toPolygons() {
23
+ return this.polygons;
24
+ }
25
+
26
+ union(csg) {
27
+ let a = new Node(this.clone().polygons);
28
+ let b = new Node(csg.clone().polygons);
29
+ a.clipTo(b);
30
+ b.clipTo(a);
31
+ b.invert();
32
+ b.clipTo(a);
33
+ b.invert();
34
+ a.build(b.allPolygons());
35
+ return CSG.fromPolygons(a.allPolygons());
36
+ }
37
+
38
+ subtract(csg) {
39
+ let a = new Node(this.clone().polygons);
40
+ let b = new Node(csg.clone().polygons);
41
+ a.invert();
42
+ a.clipTo(b);
43
+ b.clipTo(a);
44
+ b.invert();
45
+ b.clipTo(a);
46
+ b.invert();
47
+ a.build(b.allPolygons());
48
+ a.invert();
49
+ return CSG.fromPolygons(a.allPolygons());
50
+ }
51
+
52
+ intersect(csg) {
53
+ let a = new Node(this.clone().polygons);
54
+ let b = new Node(csg.clone().polygons);
55
+ a.invert();
56
+ b.clipTo(a);
57
+ b.invert();
58
+ a.clipTo(b);
59
+ b.clipTo(a);
60
+ a.build(b.allPolygons());
61
+ a.invert();
62
+ return CSG.fromPolygons(a.allPolygons());
63
+ }
64
+
65
+ // Return a new CSG solid with solid and empty space switched. This solid is
66
+ // not modified.
67
+ inverse() {
68
+ let csg = this.clone();
69
+ csg.polygons.forEach(p=>p.flip());
70
+ return csg;
71
+ }
72
+ }
73
+
74
+ // Construct a CSG solid from a list of `Polygon` instances.
75
+ CSG.fromPolygons=function(polygons) {
76
+ let csg = new CSG();
77
+ csg.polygons = polygons;
78
+ return csg;
79
+ }
80
+
81
+ // # class Vector
82
+
83
+ // Represents a 3D vector.
84
+ //
85
+ // Example usage:
86
+ //
87
+ // new CSG.Vector(1, 2, 3);
88
+
89
+
90
+
91
+ class Vector {
92
+ constructor(x=0, y=0, z=0) {
93
+ this.x=x;
94
+ this.y=y;
95
+ this.z=z;
96
+ }
97
+ copy(v){
98
+ this.x=v.x;
99
+ this.y=v.y;
100
+ this.z=v.z;
101
+ return this
102
+ }
103
+ clone() {
104
+ return new Vector(this.x,this.y,this.z)
105
+ }
106
+ negate() {
107
+ this.x*=-1;
108
+ this.y*=-1;
109
+ this.z*=-1;
110
+ return this
111
+ }
112
+ add(a) {
113
+ this.x+=a.x
114
+ this.y+=a.y
115
+ this.z+=a.z
116
+ return this;
117
+ }
118
+ sub(a) {
119
+ this.x-=a.x
120
+ this.y-=a.y
121
+ this.z-=a.z
122
+ return this
123
+ }
124
+ times(a) {
125
+ this.x*=a
126
+ this.y*=a
127
+ this.z*=a
128
+ return this
129
+ }
130
+ dividedBy(a) {
131
+ this.x/=a
132
+ this.y/=a
133
+ this.z/=a
134
+ return this
135
+ }
136
+ lerp(a, t) {
137
+ return this.add(tv0.copy(a).sub(this).times(t))
138
+ }
139
+ unit() {
140
+ return this.dividedBy(this.length())
141
+ }
142
+ length(){
143
+ return Math.sqrt((this.x**2)+(this.y**2)+(this.z**2))
144
+ }
145
+ normalize(){
146
+ return this.unit()
147
+ }
148
+ cross(b) {
149
+ let a = this;
150
+ const ax = a.x, ay = a.y, az = a.z;
151
+ const bx = b.x, by = b.y, bz = b.z;
152
+
153
+ this.x = ay * bz - az * by;
154
+ this.y = az * bx - ax * bz;
155
+ this.z = ax * by - ay * bx;
156
+
157
+ return this;
158
+ }
159
+ dot(b){
160
+ return (this.x*b.x)+(this.y*b.y)+(this.z*b.z)
161
+ }
162
+ }
163
+
164
+ //Temporaries used to avoid internal allocation..
165
+ let tv0=new Vector()
166
+ let tv1=new Vector()
167
+
168
+
169
+ // # class Vertex
170
+
171
+ // Represents a vertex of a polygon. Use your own vertex class instead of this
172
+ // one to provide additional features like texture coordinates and vertex
173
+ // colors. Custom vertex classes need to provide a `pos` property and `clone()`,
174
+ // `flip()`, and `interpolate()` methods that behave analogous to the ones
175
+ // defined by `CSG.Vertex`. This class provides `normal` so convenience
176
+ // functions like `CSG.sphere()` can return a smooth vertex normal, but `normal`
177
+ // is not used anywhere else.
178
+
179
+ class Vertex {
180
+
181
+ constructor(pos, normal, uv, color) {
182
+ this.pos = new Vector().copy(pos);
183
+ this.normal = new Vector().copy(normal);
184
+ uv && (this.uv = new Vector().copy(uv)) && (this.uv.z=0);
185
+ color && (this.color = new Vector().copy(color));
186
+ }
187
+
188
+ clone() {
189
+ return new Vertex(this.pos,this.normal,this.uv,this.color);
190
+ }
191
+
192
+ // Invert all orientation-specific data (e.g. vertex normal). Called when the
193
+ // orientation of a polygon is flipped.
194
+ flip() {
195
+ this.normal.negate();
196
+ }
197
+
198
+ // Create a new vertex between this vertex and `other` by linearly
199
+ // interpolating all properties using a parameter of `t`. Subclasses should
200
+ // override this to interpolate additional properties.
201
+ interpolate(other, t) {
202
+ return new Vertex(this.pos.clone().lerp(other.pos, t),this.normal.clone().lerp(other.normal, t),this.uv&&other.uv&&this.uv.clone().lerp(other.uv, t), this.color&&other.color&&this.color.clone().lerp(other.color,t))
203
+ }
204
+ }
205
+ ;
206
+ // # class Plane
207
+
208
+ // Represents a plane in 3D space.
209
+
210
+ class Plane {
211
+ constructor(normal, w) {
212
+ this.normal = normal;
213
+ this.w = w;
214
+ }
215
+
216
+ clone() {
217
+ return new Plane(this.normal.clone(),this.w);
218
+ }
219
+
220
+ flip() {
221
+ this.normal.negate();
222
+ this.w = -this.w;
223
+ }
224
+
225
+ // Split `polygon` by this plane if needed, then put the polygon or polygon
226
+ // fragments in the appropriate lists. Coplanar polygons go into either
227
+ // `coplanarFront` or `coplanarBack` depending on their orientation with
228
+ // respect to this plane. Polygons in front or in back of this plane go into
229
+ // either `front` or `back`.
230
+ splitPolygon(polygon, coplanarFront, coplanarBack, front, back) {
231
+ const COPLANAR = 0;
232
+ const FRONT = 1;
233
+ const BACK = 2;
234
+ const SPANNING = 3;
235
+
236
+ // Classify each point as well as the entire polygon into one of the above
237
+ // four classes.
238
+ let polygonType = 0;
239
+ let types = [];
240
+ for (let i = 0; i < polygon.vertices.length; i++) {
241
+ let t = this.normal.dot(polygon.vertices[i].pos) - this.w;
242
+ let type = (t < -Plane.EPSILON) ? BACK : (t > Plane.EPSILON) ? FRONT : COPLANAR;
243
+ polygonType |= type;
244
+ types.push(type);
245
+ }
246
+
247
+ // Put the polygon in the correct list, splitting it when necessary.
248
+ switch (polygonType) {
249
+ case COPLANAR:
250
+ (this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon);
251
+ break;
252
+ case FRONT:
253
+ front.push(polygon);
254
+ break;
255
+ case BACK:
256
+ back.push(polygon);
257
+ break;
258
+ case SPANNING:
259
+ let f = []
260
+ , b = [];
261
+ for (let i = 0; i < polygon.vertices.length; i++) {
262
+ let j = (i + 1) % polygon.vertices.length;
263
+ let ti = types[i]
264
+ , tj = types[j];
265
+ let vi = polygon.vertices[i]
266
+ , vj = polygon.vertices[j];
267
+ if (ti != BACK)
268
+ f.push(vi);
269
+ if (ti != FRONT)
270
+ b.push(ti != BACK ? vi.clone() : vi);
271
+ if ((ti | tj) == SPANNING) {
272
+ let t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(tv0.copy(vj.pos).sub(vi.pos));
273
+ let v = vi.interpolate(vj, t);
274
+ f.push(v);
275
+ b.push(v.clone());
276
+ }
277
+ }
278
+ if (f.length >= 3)
279
+ front.push(new Polygon(f,polygon.shared));
280
+ if (b.length >= 3)
281
+ back.push(new Polygon(b,polygon.shared));
282
+ break;
283
+ }
284
+ }
285
+
286
+ }
287
+
288
+ // `Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a
289
+ // point is on the plane.
290
+ Plane.EPSILON = 1e-5;
291
+
292
+ Plane.fromPoints = function(a, b, c) {
293
+ let n = tv0.copy(b).sub(a).cross(tv1.copy(c).sub(a)).normalize()
294
+ return new Plane(n.clone(),n.dot(a));
295
+ }
296
+
297
+
298
+ // # class Polygon
299
+
300
+ // Represents a convex polygon. The vertices used to initialize a polygon must
301
+ // be coplanar and form a convex loop. They do not have to be `Vertex`
302
+ // instances but they must behave similarly (duck typing can be used for
303
+ // customization).
304
+ //
305
+ // Each convex polygon has a `shared` property, which is shared between all
306
+ // polygons that are clones of each other or were split from the same polygon.
307
+ // This can be used to define per-polygon properties (such as surface color).
308
+
309
+ class Polygon {
310
+ constructor(vertices, shared) {
311
+ this.vertices = vertices;
312
+ this.shared = shared;
313
+ this.plane = Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos);
314
+ }
315
+ clone() {
316
+ return new Polygon(this.vertices.map(v=>v.clone()),this.shared);
317
+ }
318
+ flip() {
319
+ this.vertices.reverse().forEach(v=>v.flip())
320
+ this.plane.flip();
321
+ }
322
+ }
323
+
324
+ // # class Node
325
+
326
+ // Holds a node in a BSP tree. A BSP tree is built from a collection of polygons
327
+ // by picking a polygon to split along. That polygon (and all other coplanar
328
+ // polygons) are added directly to that node and the other polygons are added to
329
+ // the front and/or back subtrees. This is not a leafy BSP tree since there is
330
+ // no distinction between internal and leaf nodes.
331
+
332
+ class Node {
333
+ constructor(polygons) {
334
+ this.plane = null;
335
+ this.front = null;
336
+ this.back = null;
337
+ this.polygons = [];
338
+ if (polygons)
339
+ this.build(polygons);
340
+ }
341
+ clone() {
342
+ let node = new Node();
343
+ node.plane = this.plane && this.plane.clone();
344
+ node.front = this.front && this.front.clone();
345
+ node.back = this.back && this.back.clone();
346
+ node.polygons = this.polygons.map(p=>p.clone());
347
+ return node;
348
+ }
349
+
350
+ // Convert solid space to empty space and empty space to solid space.
351
+ invert() {
352
+ for (let i = 0; i < this.polygons.length; i++)
353
+ this.polygons[i].flip();
354
+
355
+ this.plane && this.plane.flip();
356
+ this.front && this.front.invert();
357
+ this.back && this.back.invert();
358
+ let temp = this.front;
359
+ this.front = this.back;
360
+ this.back = temp;
361
+ }
362
+
363
+ // Recursively remove all polygons in `polygons` that are inside this BSP
364
+ // tree.
365
+ clipPolygons(polygons) {
366
+ if (!this.plane)
367
+ return polygons.slice();
368
+ let front = []
369
+ , back = [];
370
+ for (let i = 0; i < polygons.length; i++) {
371
+ this.plane.splitPolygon(polygons[i], front, back, front, back);
372
+ }
373
+ if (this.front)
374
+ front = this.front.clipPolygons(front);
375
+ if (this.back)
376
+ back = this.back.clipPolygons(back);
377
+ else
378
+ back = [];
379
+ //return front;
380
+ return front.concat(back);
381
+ }
382
+
383
+ // Remove all polygons in this BSP tree that are inside the other BSP tree
384
+ // `bsp`.
385
+ clipTo(bsp) {
386
+ this.polygons = bsp.clipPolygons(this.polygons);
387
+ if (this.front)
388
+ this.front.clipTo(bsp);
389
+ if (this.back)
390
+ this.back.clipTo(bsp);
391
+ }
392
+
393
+ // Return a list of all polygons in this BSP tree.
394
+ allPolygons() {
395
+ let polygons = this.polygons.slice();
396
+ if (this.front)
397
+ polygons = polygons.concat(this.front.allPolygons());
398
+ if (this.back)
399
+ polygons = polygons.concat(this.back.allPolygons());
400
+ return polygons;
401
+ }
402
+
403
+ // Build a BSP tree out of `polygons`. When called on an existing tree, the
404
+ // new polygons are filtered down to the bottom of the tree and become new
405
+ // nodes there. Each set of polygons is partitioned using the first polygon
406
+ // (no heuristic is used to pick a good split).
407
+ build(polygons) {
408
+ if (!polygons.length)
409
+ return;
410
+ if (!this.plane)
411
+ this.plane = polygons[0].plane.clone();
412
+ let front = []
413
+ , back = [];
414
+ for (let i = 0; i < polygons.length; i++) {
415
+ this.plane.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
416
+ }
417
+ if (front.length) {
418
+ if (!this.front)
419
+ this.front = new Node();
420
+ this.front.build(front);
421
+ }
422
+ if (back.length) {
423
+ if (!this.back)
424
+ this.back = new Node();
425
+ this.back.build(back);
426
+ }
427
+ }
428
+ }
429
+
430
+ // Inflate/deserialize a vanilla struct into a CSG structure webworker.
431
+ CSG.fromJSON=function(json){
432
+ return CSG.fromPolygons(json.polygons.map(p=>new Polygon(p.vertices.map(v=> new Vertex(v.pos,v.normal,v.uv)),p.shared)))
433
+ }
434
+
435
+ export {CSG,Vertex,Vector,Polygon,Plane}
436
+
437
+
438
+
439
+ // Return a new CSG solid representing space in either this solid or in the
440
+ // solid `csg`. Neither this solid nor the solid `csg` are modified.
441
+ //
442
+ // A.union(B)
443
+ //
444
+ // +-------+ +-------+
445
+ // | | | |
446
+ // | A | | |
447
+ // | +--+----+ = | +----+
448
+ // +----+--+ | +----+ |
449
+ // | B | | |
450
+ // | | | |
451
+ // +-------+ +-------+
452
+ //
453
+ // Return a new CSG solid representing space in this solid but not in the
454
+ // solid `csg`. Neither this solid nor the solid `csg` are modified.
455
+ //
456
+ // A.subtract(B)
457
+ //
458
+ // +-------+ +-------+
459
+ // | | | |
460
+ // | A | | |
461
+ // | +--+----+ = | +--+
462
+ // +----+--+ | +----+
463
+ // | B |
464
+ // | |
465
+ // +-------+
466
+ //
467
+ // Return a new CSG solid representing space both this solid and in the
468
+ // solid `csg`. Neither this solid nor the solid `csg` are modified.
469
+ //
470
+ // A.intersect(B)
471
+ //
472
+ // +-------+
473
+ // | |
474
+ // | A |
475
+ // | +--+----+ = +--+
476
+ // +----+--+ | +--+
477
+ // | B |
478
+ // | |
479
+ // +-------+
480
+ //
481
+
static/csgworker.js ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {CSG} from "./csglib.js"
2
+
3
+
4
+ let gWorkersStarted = false;
5
+ let gWorker;
6
+ let gWorkerUrl;
7
+
8
+ let taskId = 0;
9
+ let tasks={}
10
+ let spawnWorker=()=>{
11
+ const worker = new Worker(gWorkerUrl)
12
+
13
+ worker.onmessage = function(e) {
14
+ let rslt = JSON.parse(e.data)
15
+ let task = tasks[rslt.taskId]
16
+ delete tasks[rslt.taskId]
17
+ task.resolve(CSG.fromJSON(rslt.result))
18
+ //console.log('Message received from worker');
19
+ gWorker.busy = false;
20
+ }
21
+ return gWorker = {worker,busy:false};
22
+ }
23
+
24
+ let getWorker=()=>{
25
+ if(!gWorkersStarted){
26
+ gWorkersStarted = true;
27
+ return fetch('../csg-lib.js').then(function(response) {
28
+ return response.text().then(function(text) {
29
+ text = text.slice(0, text.lastIndexOf('export'));
30
+ const code = text + `
31
+ self.onmessage=(message)=>{
32
+ let task = JSON.parse(message.data)
33
+ //console.log("Got task:"+task.op+' '+task.taskId)
34
+ postMessage(JSON.stringify({
35
+ taskId:task.taskId,
36
+ result : CSG.fromJSON(task.a)[task.op](CSG.fromJSON(task.b))
37
+ }))
38
+ }
39
+ console.log('CSG worker started!')`
40
+ const blob = new Blob([code],{
41
+ type: 'application/javascript'
42
+ })
43
+ gWorkerUrl = URL.createObjectURL(blob);
44
+
45
+
46
+ }).then(()=>{
47
+ return spawnWorker()
48
+ })
49
+ });
50
+ }
51
+ if(gWorker && (!gWorker.busy)){
52
+ gWorker.busy = true;
53
+
54
+ return {then:(fn)=>{return fn(gWorker);}}
55
+ }
56
+ return{
57
+ then:function(){return this}
58
+ }
59
+ }
60
+
61
+ CSG.doAsync=(a,op,b)=>{
62
+ return getWorker().then((worker)=>{
63
+ let task={a,op,b,taskId}
64
+ tasks[taskId]=task;
65
+ taskId++;
66
+ task.result = new Promise((resolve,reject)=>{
67
+ task.resolve = resolve;
68
+ //console.log("posting to worker:")
69
+ worker.busy = true;
70
+ worker.worker.postMessage(JSON.stringify(task))
71
+ })
72
+ return task.result
73
+ })
74
+ }
75
+
76
+ export default {}
static/filler.html ADDED
The diff for this file is too large to render. See raw diff
 
static/imports.js ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as THREE from 'three';
2
+
3
+ import Stats from 'three/addons/libs/stats.module.js';
4
+ import { GPUStatsPanel } from 'three/addons/utils/GPUStatsPanel.js';
5
+
6
+ import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
7
+ import 'three/addons/controls/OrbitControls.js';
8
+ import { Line2 } from 'three/addons/lines/Line2.js';
9
+ import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
10
+ import { LineGeometry } from 'three/addons/lines/LineGeometry.js';
11
+ import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js';
static/script.js ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as THREE from 'three';
2
+
3
+ import Stats from 'three/addons/libs/stats.module.js';
4
+ import { GPUStatsPanel } from 'three/addons/utils/GPUStatsPanel.js';
5
+
6
+ import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
7
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
8
+ import { Line2 } from 'three/addons/lines/Line2.js';
9
+ import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
10
+ import { LineGeometry } from 'three/addons/lines/LineGeometry.js';
11
+ import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js';
12
+
13
+ import {CSG} from "./threecsg.js"
14
+
15
+ var res = window.res;
16
+ var hc = window.hc;
17
+ var multi_high = window.multi_high;
18
+ var current_frame = 0;
19
+ var meshes = [];
20
+ var joint_meshes = [];
21
+
22
+ function make_linkage(x_length, y_length, z_length, hole_r) {
23
+ var link = new THREE.BoxGeometry( x_length, y_length, z_length );
24
+ var end_1 = new THREE.CylinderGeometry(y_length/2, y_length/2, z_length, 32);
25
+ var end_2 = new THREE.CylinderGeometry(y_length/2, y_length/2, z_length, 32);
26
+ var hole_1 = new THREE.CylinderGeometry(hole_r, hole_r, z_length, 32);
27
+ var hole_2 = new THREE.CylinderGeometry(hole_r, hole_r, z_length, 32);
28
+ end_1.rotateX(Math.PI/2).translate(-x_length/2, 0, 0);
29
+ end_2.rotateX(Math.PI/2).translate(x_length/2, 0, 0);
30
+ hole_1.rotateX(Math.PI/2).translate(-x_length/2, 0, 0);
31
+ hole_2.rotateX(Math.PI/2).translate(x_length/2, 0, 0);
32
+
33
+ var bsp_link = CSG.fromMesh(new THREE.Mesh(link));
34
+ var hole_1 = CSG.fromMesh(new THREE.Mesh(hole_1));
35
+ var hole_2 = CSG.fromMesh(new THREE.Mesh(hole_2));
36
+ var bsp_end_1 = CSG.fromMesh(new THREE.Mesh(end_1)).subtract(bsp_link).subtract(hole_1);
37
+ var bsp_end_2 = CSG.fromMesh(new THREE.Mesh(end_2)).subtract(bsp_link).subtract(hole_2);
38
+ bsp_link = bsp_link.subtract(hole_1).subtract(hole_2);
39
+
40
+ var full_link = CSG.toMesh(bsp_link.union(bsp_end_1).union(bsp_end_2));
41
+
42
+ return full_link;
43
+ }
44
+
45
+ function make_link(x_length, y_length, z_length, hole_r, Theta_z, translation) {
46
+ var full_link = make_linkage(x_length, y_length, z_length * 0.9, hole_r);
47
+ full_link.rotation.z = Theta_z;
48
+ full_link.position.set(translation[0] + x_length/2 * Math.cos(Theta_z), translation[1] + x_length/2 * Math.sin(Theta_z), translation[2]);
49
+ return full_link;
50
+ }
51
+
52
+ function make_joint(x, y, z, height, radius=0.03) {
53
+ var joint = new THREE.CylinderGeometry(radius, radius, height * 0.9, 32);
54
+ joint.rotateX(Math.PI/2);
55
+ joint = new THREE.Mesh(joint);
56
+ joint.position.set(x, y, z);
57
+ return joint;
58
+ }
59
+
60
+ (async function onLoad() {
61
+ var res = window.res;
62
+ var hc = window.hc;
63
+ var multi_high = window.multi_high;
64
+ // find the mean value of the res array
65
+ var max_x = 0;
66
+ var max_y = 0;
67
+ var min_x = Infinity;
68
+ var min_y = Infinity;
69
+ for(let i = 0; i < res.length; i++) {
70
+ for(let j = 0; j < res[i][1].length; j++) {
71
+ if (res[i][1][j][0] > max_x) {
72
+ max_x = res[i][1][j][0];
73
+ }
74
+ if (res[i][1][j][0] < min_x) {
75
+ min_x = res[i][1][j][0];
76
+ }
77
+ if (res[i][1][j][1] > max_y) {
78
+ max_y = res[i][1][j][1];
79
+ }
80
+ if (res[i][1][j][1] < min_y) {
81
+ min_y = res[i][1][j][1];
82
+ }
83
+ }
84
+ }
85
+ var mean_x = (max_x + min_x) / 2;
86
+ var mean_y = (max_y + min_y) / 2;
87
+
88
+ var container, camera, scene, renderer, controls, last_frame_time;
89
+
90
+ init();
91
+ animate();
92
+ controls.update();
93
+ function init() {
94
+ container = document.getElementById('container');
95
+
96
+ renderer = new THREE.WebGLRenderer({
97
+ antialias: true
98
+ });
99
+ renderer.setPixelRatio(window.devicePixelRatio);
100
+ renderer.setSize(window.innerWidth, window.innerHeight);
101
+ renderer.shadowMap.enabled = true;
102
+ container.appendChild(renderer.domElement);
103
+
104
+ scene = new THREE.Scene();
105
+ scene.background = new THREE.Color(0xfafafa);
106
+
107
+ camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000);
108
+ camera.position.set(mean_x,mean_y, 20);
109
+ scene.add(camera);
110
+ window.onresize = function() {
111
+ renderer.setSize(window.innerWidth, window.innerHeight);
112
+ camera.aspect = window.innerWidth / window.innerHeight;
113
+ camera.updateProjectionMatrix();
114
+ }
115
+
116
+ var ambientLight = new THREE.AmbientLight(0xffffff, 2.0);
117
+ scene.add(ambientLight);
118
+
119
+ // var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
120
+ // directionalLight.position.x = 4;
121
+ // directionalLight.position.y = 1;
122
+ // directionalLight.position.z = -2;
123
+ // scene.add( directionalLight );
124
+
125
+
126
+ controls = new OrbitControls(camera, renderer.domElement);
127
+ controls.target.set(mean_x,mean_y,0);
128
+
129
+ // addGridHelper();
130
+ createModel(0);
131
+ last_frame_time = Date.now();
132
+ }
133
+
134
+
135
+ function createModel(j) {
136
+ var results = res[j];
137
+ //iterate through the results and create the links
138
+ meshes = []
139
+ for (let i = 0; i < results[0].length; i++) {
140
+ var link = results[0][i];
141
+ meshes.push(make_link(link[0], link[1], link[2], link[3], link[4], link[5]));
142
+ if (i == 0){
143
+ //yellow
144
+ meshes[i].material = new THREE.MeshStandardMaterial({ color: 0xff8c00, opacity: 0.5, transparent: true , side: THREE.DoubleSide, depthWrite: true});
145
+ }
146
+ else
147
+ meshes[i].material = new THREE.MeshStandardMaterial({ color: 0x3b3b3b, opacity: 0.5, transparent: true , side: THREE.DoubleSide, depthWrite: true});
148
+ scene.add(meshes[i]);
149
+ }
150
+ joint_meshes = []
151
+ for (let i = 0; i < results[1].length; i++) {
152
+ var joint = results[1][i];
153
+ joint_meshes.push(make_joint(joint[0], joint[1], joint[3], joint[2]));
154
+ if(joint[4] == 1)//brown
155
+ joint_meshes[i].material = new THREE.MeshStandardMaterial({ color: 0xff4c00, side: THREE.DoubleSide, depthWrite: false});
156
+ else
157
+ joint_meshes[i].material = new THREE.MeshStandardMaterial({ color: 0x121212, side: THREE.DoubleSide, depthWrite: false});
158
+ scene.add(joint_meshes[i]);
159
+ }
160
+
161
+ if (multi_high) {
162
+ var n = hc.length;
163
+
164
+ for (let j = 0; j < n; j++) {
165
+ var points = [];
166
+ var colors = [];
167
+ for (let i = 0; i <hc[j].length; i++) {
168
+ points.push(hc[j][i][0], hc[j][i][1], hc[j][i][2]);
169
+ }
170
+
171
+ var geometry = new LineGeometry();
172
+ geometry.setPositions( points );
173
+ var material = new LineMaterial( {
174
+ color: 0xff4c00,
175
+ linewidth: 0.002, // in world units with size attenuation, pixels otherwise
176
+ vertexColors: false,
177
+
178
+ //resolution: // to be set by renderer, eventually
179
+ dashed: false,
180
+ alphaToCoverage: false,
181
+ depthWrite: false,
182
+ depthTest: false,
183
+ transparent: true,
184
+
185
+ });
186
+ material.worldUnits = false;
187
+ const line = new Line2(geometry, material);
188
+ line.computeLineDistances();
189
+ line.scale.set( 1, 1, 1 );
190
+ line.renderOrder= 9999;
191
+ scene.add(line);
192
+ }
193
+ }
194
+ else{
195
+ var points = [];
196
+ var colors = [];
197
+ for (let i = 0; i <hc.length; i++) {
198
+ points.push(hc[i][0], hc[i][1], hc[i][2]);
199
+ }
200
+
201
+ var geometry = new LineGeometry();
202
+ geometry.setPositions( points );
203
+ var material = new LineMaterial( {
204
+ color: 0xff4c00,
205
+ linewidth: 0.002, // in world units with size attenuation, pixels otherwise
206
+ vertexColors: false,
207
+
208
+ //resolution: // to be set by renderer, eventually
209
+ dashed: false,
210
+ alphaToCoverage: false,
211
+ depthWrite: false,
212
+ depthTest: false,
213
+ transparent: true,
214
+
215
+ });
216
+ material.worldUnits = false;
217
+ const line = new Line2(geometry, material);
218
+ line.computeLineDistances();
219
+ line.scale.set( 1, 1, 1 );
220
+ line.renderOrder= 9999;
221
+ scene.add(line);
222
+ }
223
+
224
+
225
+ }
226
+
227
+ function addGridHelper() {
228
+
229
+ var helper = new THREE.GridHelper(10, 10);
230
+ helper.material.opacity = 0.25;
231
+ helper.material.transparent = true;
232
+ scene.add(helper);
233
+
234
+ var axis = new THREE.AxesHelper(100);
235
+ scene.add(axis);
236
+ }
237
+
238
+ function set_idx(){
239
+ if(current_frame < res.length-1) {
240
+ // createModel(current_frame);
241
+ current_frame += 1;
242
+ }
243
+ else {
244
+ current_frame = 0;
245
+ }
246
+ }
247
+
248
+ function animate() {
249
+
250
+ if (Date.now() - last_frame_time > 1000/30) {
251
+ set_idx();
252
+ last_frame_time = Date.now();
253
+ }
254
+
255
+ for (let i = 0; i < meshes.length; i++) {
256
+ meshes[i].rotation.z = res[current_frame][0][i][4];
257
+ meshes[i].position.set(res[current_frame][0][i][5][0] + res[current_frame][0][i][0]/2 * Math.cos(res[current_frame][0][i][4]), res[current_frame][0][i][5][1] + res[current_frame][0][i][0]/2 * Math.sin(res[current_frame][0][i][4]), res[current_frame][0][i][5][2]);
258
+ }
259
+
260
+ for (let i = 0; i < joint_meshes.length; i++) {
261
+ joint_meshes[i].position.set(res[current_frame][1][i][0], res[current_frame][1][i][1], res[current_frame][1][i][3]);
262
+ }
263
+
264
+ requestAnimationFrame(animate);
265
+ render();
266
+ }
267
+
268
+ function render() {
269
+ renderer.clearDepth();
270
+ renderer.render(scene, camera);
271
+ }
272
+ })();
static/style.css ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ body{
2
+ margin: 0;
3
+ }
static/threecsg.js ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ //import*as THREE from "./lib/three.module.js";
3
+
4
+ import * as THREE from "three" //"https://threejs.org/build/three.module.js";
5
+
6
+ let { BufferGeometry, Vector3, Vector2} = THREE;
7
+ import {CSG, Vertex, Vector, Polygon} from "./csglib.js";
8
+ //import {Geometry} from "../three.js-dev/examples/jsm/deprecated/Geometry.js";
9
+
10
+ CSG.fromGeometry = function(geom,objectIndex) {
11
+ let polys = []
12
+ if (geom.isGeometry) {
13
+ let fs = geom.faces;
14
+ let vs = geom.vertices;
15
+ let fm = ['a', 'b', 'c']
16
+ for (let i = 0; i < fs.length; i++) {
17
+ let f = fs[i];
18
+ let vertices = []
19
+ for (let j = 0; j < 3; j++)
20
+ vertices.push(new Vertex(vs[f[fm[j]]],f.vertexNormals[j],geom.faceVertexUvs[0][i][j]))
21
+ polys.push(new Polygon(vertices, objectIndex))
22
+ }
23
+ } else if (geom.isBufferGeometry) {
24
+ let vertices, normals, uvs
25
+ let posattr = geom.attributes.position
26
+ let normalattr = geom.attributes.normal
27
+ let uvattr = geom.attributes.uv
28
+ let colorattr = geom.attributes.color
29
+ let index;
30
+ if (geom.index)
31
+ index = geom.index.array;
32
+ else {
33
+ index = new Array((posattr.array.length / posattr.itemSize) | 0);
34
+ for (let i = 0; i < index.length; i++)
35
+ index[i] = i
36
+ }
37
+ let triCount = (index.length / 3) | 0
38
+ polys = new Array(triCount)
39
+ for (let i = 0, pli = 0, l = index.length; i < l; i += 3,
40
+ pli++) {
41
+ let vertices = new Array(3)
42
+ for (let j = 0; j < 3; j++) {
43
+ let vi = index[i + j]
44
+ let vp = vi * 3;
45
+ let vt = vi * 2;
46
+ let x = posattr.array[vp]
47
+ let y = posattr.array[vp + 1]
48
+ let z = posattr.array[vp + 2]
49
+ let nx = normalattr.array[vp]
50
+ let ny = normalattr.array[vp + 1]
51
+ let nz = normalattr.array[vp + 2]
52
+ //let u = uvattr.array[vt]
53
+ //let v = uvattr.array[vt + 1]
54
+ vertices[j] = new Vertex({
55
+ x,
56
+ y,
57
+ z
58
+ },{
59
+ x: nx,
60
+ y: ny,
61
+ z: nz
62
+ },uvattr&&{
63
+ x: uvattr.array[vt],
64
+ y: uvattr.array[vt+1],
65
+ z: 0
66
+ },colorattr&&{x:colorattr.array[vt],y:colorattr.array[vt+1],z:colorattr.array[vt+2]});
67
+ }
68
+ polys[pli] = new Polygon(vertices,objectIndex)
69
+ }
70
+ } else
71
+ console.error("Unsupported CSG input type:" + geom.type)
72
+ return CSG.fromPolygons(polys)
73
+ }
74
+
75
+ let ttvv0 = new THREE.Vector3()
76
+ let tmpm3 = new THREE.Matrix3();
77
+ CSG.fromMesh = function(mesh,objectIndex) {
78
+ let csg = CSG.fromGeometry(mesh.geometry,objectIndex)
79
+ tmpm3.getNormalMatrix(mesh.matrix);
80
+ for (let i = 0; i < csg.polygons.length; i++) {
81
+ let p = csg.polygons[i]
82
+ for (let j = 0; j < p.vertices.length; j++) {
83
+ let v = p.vertices[j]
84
+ v.pos.copy(ttvv0.copy(v.pos).applyMatrix4(mesh.matrix));
85
+ v.normal.copy(ttvv0.copy(v.normal).applyMatrix3(tmpm3))
86
+ }
87
+ }
88
+ return csg;
89
+ }
90
+
91
+ let nbuf3=(ct)=>{
92
+ return{
93
+ top:0,
94
+ array:new Float32Array(ct),
95
+ write:function(v){(this.array[this.top++]=v.x);(this.array[this.top++]=v.y);(this.array[this.top++]=v.z);}
96
+ }
97
+ }
98
+ let nbuf2=(ct)=>{
99
+ return{
100
+ top:0,
101
+ array:new Float32Array(ct),
102
+ write:function(v){(this.array[this.top++]=v.x);(this.array[this.top++]=v.y)}
103
+ }
104
+ }
105
+
106
+ CSG.toGeometry = function(csg, buffered=true) {
107
+ let ps = csg.polygons;
108
+ let geom;
109
+ let g2;
110
+ if(!buffered) //Old geometry path...
111
+ {
112
+ geom = new Geometry();
113
+ let vs = geom.vertices;
114
+ let fvuv = geom.faceVertexUvs[0]
115
+ for (let i = 0; i < ps.length; i++) {
116
+ let p = ps[i]
117
+ let pvs = p.vertices;
118
+ let v0 = vs.length;
119
+ let pvlen = pvs.length
120
+
121
+ for (let j = 0; j < pvlen; j++)
122
+ vs.push(new THREE.Vector3().copy(pvs[j].pos))
123
+
124
+ for (let j = 3; j <= pvlen; j++) {
125
+ let fc = new THREE.Face3();
126
+ let fuv = []
127
+ fvuv.push(fuv)
128
+ let fnml = fc.vertexNormals;
129
+ fc.a = v0;
130
+ fc.b = v0 + j - 2;
131
+ fc.c = v0 + j - 1;
132
+
133
+ fnml.push(new THREE.Vector3().copy(pvs[0].normal))
134
+ fnml.push(new THREE.Vector3().copy(pvs[j - 2].normal))
135
+ fnml.push(new THREE.Vector3().copy(pvs[j - 1].normal))
136
+ fuv.push(new THREE.Vector3().copy(pvs[0].uv))
137
+ fuv.push(new THREE.Vector3().copy(pvs[j - 2].uv))
138
+ fuv.push(new THREE.Vector3().copy(pvs[j - 1].uv))
139
+
140
+ fc.normal = new THREE.Vector3().copy(p.plane.normal)
141
+ geom.faces.push(fc)
142
+ }
143
+ }
144
+ geom = new THREE.BufferGeometry().fromGeometry(geom)
145
+ geom.verticesNeedUpdate = geom.elementsNeedUpdate = geom.normalsNeedUpdate = true;
146
+ }else { //BufferGeometry path
147
+ let triCount = 0;
148
+ ps.forEach(p=>triCount += (p.vertices.length - 2))
149
+ geom = new THREE.BufferGeometry()
150
+
151
+ let vertices = nbuf3(triCount * 3 * 3)
152
+ let normals = nbuf3(triCount * 3 * 3)
153
+ let uvs; // = nbuf2(triCount * 2 * 3)
154
+ let colors;
155
+ let grps=[]
156
+ ps.forEach(p=>{
157
+ let pvs = p.vertices
158
+ let pvlen = pvs.length
159
+ if(p.shared!==undefined){
160
+ if(!grps[p.shared])grps[p.shared]=[]
161
+ }
162
+ if(pvlen){
163
+ if(pvs[0].color!==undefined){
164
+ if(!colors)colors = nbuf3(triCount*3*3);
165
+ }
166
+ if(pvs[0].uv!==undefined){
167
+ if(!uvs)uvs = nbuf2(triCount * 2 * 3)
168
+ }
169
+ }
170
+ for (let j = 3; j <= pvlen; j++) {
171
+ (p.shared!==undefined) && (grps[p.shared].push(vertices.top/3,(vertices.top/3)+1,(vertices.top/3)+2));
172
+ vertices.write(pvs[0].pos)
173
+ vertices.write(pvs[j-2].pos)
174
+ vertices.write(pvs[j-1].pos)
175
+ normals.write(pvs[0].normal)
176
+ normals.write(pvs[j-2].normal)
177
+ normals.write(pvs[j-1].normal);
178
+ uvs&&(pvs[0].uv)&&(uvs.write(pvs[0].uv)||uvs.write(pvs[j-2].uv)||uvs.write(pvs[j-1].uv));
179
+ colors&&(colors.write(pvs[0].color)||colors.write(pvs[j-2].color)||colors.write(pvs[j-1].color))
180
+ }
181
+ }
182
+ )
183
+ geom.setAttribute('position', new THREE.BufferAttribute(vertices.array,3));
184
+ geom.setAttribute('normal', new THREE.BufferAttribute(normals.array,3));
185
+ uvs && geom.setAttribute('uv', new THREE.BufferAttribute(uvs.array,2));
186
+ colors && geom.setAttribute('color', new THREE.BufferAttribute(colors.array,3));
187
+ if(grps.length){
188
+ let index = []
189
+ let gbase=0;
190
+ for(let gi=0;gi<grps.length;gi++){
191
+ geom.addGroup(gbase,grps[gi].length,gi)
192
+ gbase+=grps[gi].length
193
+ index=index.concat(grps[gi]);
194
+ }
195
+ geom.setIndex(index)
196
+ }
197
+ g2 = geom;
198
+ }
199
+ return geom;
200
+ }
201
+
202
+ CSG.toMesh = function(csg, toMatrix=new THREE.Matrix4(), toMaterial) {
203
+ let geom = CSG.toGeometry(csg);
204
+ let inv = new THREE.Matrix4().copy(toMatrix).invert();
205
+ geom.applyMatrix4(inv);
206
+ geom.computeBoundingSphere();
207
+ geom.computeBoundingBox();
208
+ let m = new THREE.Mesh(geom,toMaterial);
209
+ m.matrix.copy(toMatrix);
210
+ m.matrix.decompose(m.position, m.quaternion, m.scale)
211
+ m.rotation.setFromQuaternion(m.quaternion)
212
+ m.updateMatrixWorld();
213
+ m.castShadow = m.receiveShadow = true;
214
+ return m
215
+ }
216
+
217
+ import "./csgworker.js";
218
+
219
+ export {CSG}