@@ -77,14 +77,16 @@ def catmull_rom_points( points: Sequence[Point23Input],
77
77
points_list = list ([euclidify (p , Point3 ) for p in points ])
78
78
79
79
if close_loop :
80
- cat_points = euclidify ([points_list [- 1 ]] + points_list + [ points_list [0 ] ], Point3 )
80
+ cat_points = euclidify ([points_list [- 1 ]] + points_list + points_list [0 : 2 ], Point3 )
81
81
else :
82
82
# Use supplied tangents or just continue the ends of the supplied points
83
83
start_tangent = start_tangent or (points_list [1 ] - points_list [0 ])
84
+ start_tangent = euclidify (start_tangent , Vector3 )
84
85
end_tangent = end_tangent or (points_list [- 2 ] - points_list [- 1 ])
86
+ end_tangent = euclidify (end_tangent , Vector3 )
85
87
cat_points = [points_list [0 ]+ start_tangent ] + points_list + [points_list [- 1 ] + end_tangent ]
86
88
87
- last_point_range = len (cat_points ) - 2 if close_loop else len (cat_points ) - 3
89
+ last_point_range = len (cat_points ) - 3 if close_loop else len (cat_points ) - 3
88
90
89
91
for i in range (0 , last_point_range ):
90
92
include_last = True if i == last_point_range - 1 else False
@@ -159,7 +161,10 @@ def catmull_rom_patch(patch:Tuple[PointInputs, PointInputs], subdivisions:int =
159
161
def catmull_rom_prism ( control_curves :Sequence [PointInputs ],
160
162
subdivisions :int = DEFAULT_SUBDIVISIONS ,
161
163
closed_ring :bool = True ,
162
- add_caps :bool = True ) -> polyhedron :
164
+ add_caps :bool = True ,
165
+ smooth_edges : bool = False ) -> polyhedron :
166
+ if smooth_edges :
167
+ return catmull_rom_prism_smooth_edges (control_curves , subdivisions , closed_ring , add_caps )
163
168
164
169
verts : List [Point3 ] = []
165
170
faces : List [FaceTrio ] = []
@@ -187,22 +192,62 @@ def catmull_rom_prism( control_curves:Sequence[PointInputs],
187
192
if closed_ring and add_caps :
188
193
bot_indices = range (0 , len (verts ), curve_length )
189
194
top_indices = range (curve_length - 1 , len (verts ), curve_length )
190
- bot_points = [verts [i ] for i in bot_indices ]
191
- top_points = [verts [i ] for i in top_indices ]
192
-
193
- # FIXME: This won't work, since it assumes that the points making
194
- # up the two end caps are all in order. In fact, that's not the case;
195
- # the indexes of the points at the base cap are 0, 41, 82, etc. on a curve
196
- # with 5 points and 10 subdivisions.
197
- bot_centroid , bot_faces = centroid_endcap (bot_points , bot_indices , len (verts ))
198
- top_centroid , top_faces = centroid_endcap (top_points , top_indices , len (verts ) + 1 , invert = True )
199
- verts += [bot_centroid , top_centroid ]
195
+
196
+ bot_centroid , bot_faces = centroid_endcap (verts , bot_indices )
197
+ verts .append (bot_centroid )
200
198
faces += bot_faces
199
+ # Note that bot_centroid must be added to verts before creating the
200
+ # top endcap; otherwise both endcaps would point to the same centroid point
201
+ top_centroid , top_faces = centroid_endcap (verts , top_indices , invert = True )
202
+ verts .append (top_centroid )
201
203
faces += top_faces
202
204
203
205
p = polyhedron (faces = faces , points = verts , convexity = 3 )
204
206
return p
205
207
208
+ def catmull_rom_prism_smooth_edges ( control_curves :Sequence [PointInputs ],
209
+ subdivisions :int = DEFAULT_SUBDIVISIONS ,
210
+ closed_ring :bool = True ,
211
+ add_caps :bool = True ) -> polyhedron :
212
+
213
+ verts : List [Point3 ] = []
214
+ faces : List [FaceTrio ] = []
215
+
216
+ # TODO: verify that each control_curve has the same length
217
+
218
+ curves = list ([euclidify (c ) for c in control_curves ])
219
+
220
+ expanded_curves = [catmull_rom_points (c , subdivisions , close_loop = False ) for c in curves ]
221
+ expanded_length = len (expanded_curves [0 ])
222
+ for i in range (expanded_length ):
223
+ contour_controls = [c [i ] for c in expanded_curves ]
224
+ contour = catmull_rom_points (contour_controls , subdivisions , close_loop = closed_ring )
225
+ verts += contour
226
+
227
+ contour_length = len (contour )
228
+ # generate the face triangles between the last two rows of vertices
229
+ if i > 0 :
230
+ a_start = len (verts ) - 2 * contour_length
231
+ b_start = len (verts ) - contour_length
232
+ new_faces = face_strip_list (a_start , b_start , length = contour_length , close_loop = closed_ring )
233
+ faces += new_faces
234
+
235
+ if closed_ring and add_caps :
236
+ bot_indices = range (0 , contour_length )
237
+ top_indices = range (len (verts ) - contour_length , len (verts ))
238
+
239
+ bot_centroid , bot_faces = centroid_endcap (verts , bot_indices )
240
+ verts .append (bot_centroid )
241
+ faces += bot_faces
242
+ # Note that bot_centroid must be added to verts before creating the
243
+ # top endcap; otherwise both endcaps would point to the same centroid point
244
+ top_centroid , top_faces = centroid_endcap (verts , top_indices , invert = True )
245
+ verts .append (top_centroid )
246
+ faces += top_faces
247
+
248
+ p = polyhedron (faces = faces , points = verts , convexity = 3 )
249
+ return p
250
+
206
251
# ==================
207
252
# = BEZIER SPLINES =
208
253
# ==================
@@ -215,7 +260,7 @@ def bezier_polygon( controls: FourPoints,
215
260
show_controls : bool = False ,
216
261
center : bool = True ) -> OpenSCADObject :
217
262
'''
218
- Return an OpenSCAD representing a closed quadratic Bezier curve.
263
+ Return an OpenSCAD object representing a closed quadratic Bezier curve.
219
264
If extrude_height == 0, return a 2D `polygon()` object.
220
265
If extrude_height > 0, return a 3D extrusion of specified height.
221
266
Note that OpenSCAD won't render 2D & 3D objects together correctly, so pick
@@ -356,22 +401,21 @@ def fan_endcap_list(cap_points:int=3, index_start:int=0) -> List[FaceTrio]:
356
401
faces .append ((index_start , i , i + 1 ))
357
402
return faces
358
403
359
- def centroid_endcap (points :Sequence [Point3 ], indices :Sequence [int ], total_vert_count :int , invert :bool = False ) -> Tuple [Point3 , List [FaceTrio ]]:
360
- # Given a list of points at one end of a polyhedron tube, and their
361
- # accompanying indices, make a centroid point, and return all the triangle
362
- # information needed to make an endcap polyhedron.
363
-
364
- # `total_vert_count` should be the number of vertices in the existing shape
365
- # *before* calling this function; we'll return a point that should be appended
366
- # to the total vertex list for the polyhedron-to-be
367
-
404
+ def centroid_endcap (tube_points :Sequence [Point3 ], indices :Sequence [int ], invert :bool = False ) -> Tuple [Point3 , List [FaceTrio ]]:
405
+ # tube_points: all points in a polyhedron tube
406
+ # indices: the indexes of the points at the desired end of the tube
407
+ # invert: if True, invert the order of the generated faces. One endcap in
408
+ # each pair should be inverted
409
+ #
410
+ # Return all the triangle information needed to make an endcap polyhedron
411
+ #
368
412
# This is sufficient for some moderately concave polygonal endcaps,
369
413
# (a star shape, say), but wouldn't be enough for more irregularly convex
370
414
# polygons (anyplace where a segment from the centroid to a point on the
371
415
# polygon crosses an edge of the polygon)
372
416
faces : List [FaceTrio ] = []
373
- center = centroid (points )
374
- centroid_index = total_vert_count
417
+ center = centroid ([ tube_points [ i ] for i in indices ] )
418
+ centroid_index = len ( tube_points )
375
419
376
420
for a ,b in zip (indices [:- 1 ], indices [1 :]):
377
421
faces .append ((centroid_index , a , b ))
0 commit comments