-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathscaffold_sketch.py
335 lines (246 loc) · 10.2 KB
/
scaffold_sketch.py
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
from __future__ import print_function, division
from os import stat
import numpy as np
import fit_line
import fit_point
import fit_curve
'''
This module stores the state for the scaffold sketching program.
The state is a dictionary with certain keys.
Call `make_new_program_state()` to create one.
'''
def make_new_program_state():
'''
Returns a dictionary for storing the state of a scaffold sketching
program.
The dictionary has keys value pairs
'construction_lines' : [ list of ndarray as construction lines ]
'shape_curves' : [ list of points ]
'raw_construction_lines' : [ list of tuple as raw points]
'raw_shape_curves' : [ list of ndarray as raw shape points ]
'line_points': [ np.array([x,y,z]) ] points attach to lines
'line_directions' : [ np.array([0, 1, 0]) ] # start with vertical direction
'line_lengths': [ float as line_length ]
'points_info': [ points_idx: [ a list of ndarray as direction at that point] ]
'''
state = { 'construction_lines' : [],
'shape_curves' : [],
'raw_construction_lines': [], # for debug
'raw_shape_curves' : [], # for debug
'line_points' : [],
'line_directions' : [ np.array([0, 1, 0]) ],
'line_lengths' : [],
'points_info' : {}
}
return state
def make_line_thresholds( scale ):
'''
init fit_line thresholds with the scale factor
'''
thresholds = {
'point_point_distance2': 0.05 ** 2, # 0.05 **2
'point_line_distance2': 0.05 ** 2, # 0.05 **2
'line_vec_parallel2': (1 - np.cos(10/180*np.pi)) ** 2, # 0.015 **2
'line_vec_perpendicular2': np.cos(80/180*np.pi) ** 2, # 0.17 ** 2
'line_length_ratio2': 0.25 ** 2 # 75% ~125%?
}
thresholds['point_point_distance2'] /= scale
thresholds['point_line_distance2'] /= scale
return thresholds
def line_or_point(state, data ):
'''
Given:
state: a state as returned by `make_new_program_state`
data: from html side, a dictionary, have 'scale' and 'pts', example below:
{'scale': {'x': 1, 'y': 1, 'z': 1},
'pts': [{'x': 0, 'y': 0, 'z': 0},
{'x': 0, 'y': 1, 'z': 0}]}
Returns:
use this function to decide whether it's a line or point
'''
### Use the scale to set up thresholds
scale = data['scale']
pts = data['pts']
# print('scale', scale)
# print('data', data)
thresholds = make_line_thresholds( scale['x'] )
### 1 Get startpoint and endpoint of line
### 2 Depend on how many constraints, how long the line is, let it be a point or line
x0 = pts[0]['x']
y0 = pts[0]['y']
z0 = pts[0]['z']
x1 = pts[1]['x']
y1 = pts[1]['y']
z1 = pts[1]['z']
startpoint = (x0, y0, z0)
endpoint = (x1, y1, z1)
# print('startpoint', startpoint)
# print('endpoint', endpoint)
# print('start_end_dist', start_end_dist)
constraints = fit_line.build_energy_for_line_segment(startpoint, endpoint, thresholds, state)
start_end_dist = np.linalg.norm( np.asarray(startpoint) - np.asarray(endpoint) )
# less than 4 constraints
# The point is just projection, so we don't need scale factor
if start_end_dist < 0.03: # less than 0.03, by no means make it a point?
result = incorporate_new_raw_point( state, pts)
print('point')
elif len(constraints) <= 4 and start_end_dist < 0.08: # 8cm
result = incorporate_new_raw_point( state, pts )
print('point')
else:
result = incorporate_new_raw_construction_line( state, ( startpoint, endpoint), thresholds )
print('line')
return result
def incorporate_new_raw_construction_line( state, line , thresholds):
'''
Given:
state: a state as returned by `make_new_program_state`
raw_line: startpoint and endpoint of raw construction line
thresholds: current thresholds under scale factor
Returns:
line: a part of points which are the start and end of the new construction line
Modified `state` to add a new construction line based off
of the raw GUI input `pts`.
Called by line_or_point function.
'''
### 1 Store the raw construction line to state.
### 2 Snap the fit line to existing construction lines.
### 3 Add line, line direction, points, length to state if not current exist.
### 1
state['raw_construction_lines'].append( line )
### 2
snapped_line = fit_line.snap_line_to_other_lines( line, state, thresholds )
### 3
fit_line.add_new_line_info( snapped_line, state )
return snapped_line
def incorporate_new_raw_point( state, pts ):
'''
Given:
state: a state as returned by `make_new_program_state`
pts: raw points from the GUI as a sequence of (x,y,z?) triplets.
Returns:
point
Modified `state` to add a new point based off
of the raw GUI input `pts`.
Called by line_or_point function.
'''
# find the center of pts, project it to the nearest line
point = fit_point.point_fitting(state, pts)
return point
def make_curve_thresholds( scale ):
'''
init fit_curve thresholds with the scale factor
'''
thresholds = {
'point_point_distance' : 0.03, # 3cm
'same_direction_threshold' : (1 - np.cos(25/180*np.pi)) # 25 degree
}
thresholds['point_point_distance'] /= scale
return thresholds
def incorporate_new_raw_shape_line( state, data ):
'''
Given:
state: a state as returned by `make_new_program_state`
data: from html side
'scale' : scale vector
'pts': pts
Returns:
curve: curve points
Modified `state` to add a new construction line based off
of the raw GUI input `pts`.
'''
### Use the scale to set up thresholds
scale = data['scale']
pts = data['pts']
thresholds = make_curve_thresholds( scale['x'] )
### 1 Fit a curve to the points.
### 2 Store all curve points in state.
curve = fit_curve.shape_line_from_keypoints( state, pts, thresholds )
if curve:
state['shape_curves'].append( curve )
return curve
def prepare_state_for_UI( state ):
"""
construction_lines: list of ndarray
shape_lines: list of ndarray
prepare them to json so it is easier for javascript to parse
used for undo and redo
to display, html/js only need to know lines, points and curves
"""
current_state = { 'lines' : [], 'points': [], 'curves' : [] }
for construction_line in state['construction_lines']:
current_state['lines'].append( construction_line.tolist() )
for point in state['line_points']:
current_state['points'].append( point.tolist() )
for shape_curve in state['shape_curves']:
current_state['curves'].append( shape_curve )
return current_state
def prepare_state_for_save_all_info( state ):
'''
Given:
state
Return:
serializable of state
Object of type ndarray is not JSON serializable
Used to save all information
'''
current_state = { 'construction_lines' : [], # list of ndarray
'shape_curves' : [], # list of list
'raw_construction_lines': [], # list of ndarray
'raw_shape_curves': [],
'line_points' : [],
'line_directions': [], # list of ndarray
'line_lengths': [],
'points_info': {}
}
for construction_line in state['construction_lines']:
current_state['construction_lines'].append( construction_line.tolist() )
for shape_curve in state['shape_curves']:
current_state['shape_curves'].append( shape_curve )
for raw_construction_line in state['raw_construction_lines']:
current_state['raw_construction_lines'].append( raw_construction_line )
for raw_shape_curve in state['raw_shape_curves']:
current_state['raw_shape_curves'].append( raw_shape_curve.tolist() )
for point in state['line_points']:
current_state['line_points'].append( point.tolist() )
for dir in state['line_directions']:
current_state['line_directions'].append( dir.tolist() )
for length in state['line_lengths']:
current_state['line_lengths'].append( length )
for point, infos in state['points_info'].items():
current_state['points_info'][point] = []
for info in infos:
current_state['points_info'][point].append( info.tolist() )
return current_state
def load_state_from_json( state ):
'''
opposite of prepare_state_for_save_all_info
'''
current_state = { 'construction_lines' : [], # list of ndarray
'shape_curves' : [], # list of list
'raw_construction_lines': [], # list of ndarray
'raw_shape_curves': [],
'line_points' : [],
'line_directions': [], # list of ndarray
'line_lengths': [],
'points_info': {}
}
for construction_line in state['construction_lines']:
current_state['construction_lines'].append( np.asarray(construction_line) )
for raw_construction_line in state['raw_construction_lines']:
current_state['raw_construction_lines'].append( raw_construction_line )
for shape_curve in state['shape_curves']:
current_state['shape_curves'].append( shape_curve )
for raw_shape_curve in state['raw_shape_curves']:
current_state['raw_shape_curves'].append( np.asarray(raw_shape_curve) )
for point in state['line_points']:
current_state['line_points'].append( np.asarray(point) )
for dir in state['line_directions']:
current_state['line_directions'].append( np.asarray(dir) )
for length in state['line_lengths']:
current_state['line_lengths'].append( length )
for point, infos in state['points_info'].items():
current_state['points_info'][int(point)] = []
for info in infos:
current_state['points_info'][int(point)].append( np.asarray(info) )
return current_state