-
Notifications
You must be signed in to change notification settings - Fork 173
/
splines_example.py
executable file
·170 lines (140 loc) · 5.53 KB
/
splines_example.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
#! /usr/bin/env python
import os
import sys
from solid import *
from solid.utils import Red, right, forward, back
from solid.splines import catmull_rom_points, catmull_rom_polygon, control_points
from solid.splines import bezier_polygon, bezier_points
from euclid3 import Vector2, Vector3, Point2, Point3
def assembly():
# Catmull-Rom Splines
a = basic_catmull_rom() # Top row in OpenSCAD output
a += back(4)(catmull_rom_spline_variants()) # Row 2
a += back(12)(bottle_shape(width=2, height=6)) # Row 3, the bottle shape
# # TODO: include examples for 3D surfaces:
# a += back(16)(catmull_rom_patches())
# a += back(20)(catmull_rom_prism())
# a += back(24)(catmull_rom_prism_smooth())
# Bezier Splines
a += back(16)(basic_bezier()) # Row 4
a += back(20)(bezier_points_variants()) # Row 5
return a
def basic_catmull_rom():
points = [
Point2(0,0),
Point2(1,1),
Point2(2,1),
Point2(2,-1),
]
# In its simplest form, catmull_rom_polygon() will just make a C1-continuous
# closed shape. Easy.
shape_easy = catmull_rom_polygon(points)
# There are some other options as well...
shape = catmull_rom_polygon(points, subdivisions=20, extrude_height=5, show_controls=True)
return shape_easy + right(3)(shape)
def catmull_rom_spline_variants():
points = [
Point2(0,0),
Point2(1,1),
Point2(2,1),
Point2(2,-1),
]
controls = control_points(points)
# By default, catmull_rom_points() will return a closed smooth shape
curve_points_closed = catmull_rom_points(points, close_loop=True)
# If `close_loop` is False, it will return only points between the start and
# end control points, and make a best guess about tangents for the first and last segments
curve_points_open = catmull_rom_points(points, close_loop=False)
# By specifying start_tangent and end_tangent, you can change a shape
# significantly. This is similar to what you might do with Illustrator's Pen Tool.
# Try changing these vectors to see the effects this has on the rightmost curve in the example
start_tangent = Vector2(-2, 0)
end_tangent = Vector2(3, 0)
tangent_pts = [points[0] + start_tangent, *points, points[-1] + end_tangent]
tangent_controls = control_points(tangent_pts)
curve_points_tangents = catmull_rom_points(points, close_loop=False,
start_tangent=start_tangent, end_tangent=end_tangent)
closed = polygon(curve_points_closed) + controls
opened = polygon(curve_points_open) + controls
tangents = polygon(curve_points_tangents) + tangent_controls
a = closed + right(3)(opened) + right(10)(tangents)
return a
def catmull_rom_patches():
# TODO: write this
pass
def catmull_rom_prism():
# TODO: write this
pass
def catmull_rom_prism_smooth():
# TODO: write this
pass
def bottle_shape(width: float, height: float, neck_width:float=None, neck_height:float=None):
if neck_width == None:
neck_width = width * 0.4
if neck_height == None:
neck_height = height * 0.2
w2 = width/2
nw2 = neck_width/2
h = height
nh = neck_height
corner_rad = 0.5
# Add extra tangent points near curves to keep cubics from going crazy.
# Try taking some of these out and see how this affects the final shape
points = [
Point2(nw2, h),
Point2(nw2, h-nh + 1), # <- extra tangent
Point2(nw2, h - nh),
Point2(w2, h-nh-h/6), # <- extra tangent
Point2(w2, corner_rad + 1), # <- extra tangent
Point2(w2, corner_rad),
Point2(w2-corner_rad, 0),
Point2(0,0),
]
# Use catmull_rom_points() when you don't want all corners in a polygon
# smoothed out or want to combine the curve with other shapes.
# Extra points can then be added to the list you get back
cr_points = catmull_rom_points(points)
# Insert a point at the top center of the bottle at the beginning of the
# points list. This is how the bottle has a sharp right angle corner at the
# sides of the neck; otherwise we'd have to insert several extra control
# points to make a sharp corner
cr_points.insert(0, (0,h))
# Make OpenSCAD polygons out of the shapes once all points are calculated
a = polygon(cr_points)
a += mirror(v=(1,0))(a)
# Show control points. These aren't required for anything, but seeing them
# makes refining a curve much easier
controls = control_points(points)
a += controls
return a
def basic_bezier():
# A basic cubic Bezier curve will pass through its first and last
# points, but not through the central control points
controls = [
Point2(0, 3),
Point2(1, 1),
Point2(2, 1),
Point2(3, 3)
]
shape = bezier_polygon(controls, show_controls=True)
return shape
def bezier_points_variants():
controls = [
Point2(0,0),
Point2(1,2),
Point2(2, -1),
Point2(3,0),
]
points = bezier_points(controls, subdivisions=20)
# For non-smooth curves, add extra points
points += [
Point2(2, -2),
Point2(1, -2)
]
shape = polygon(points) + control_points(controls, extrude_height=0)
return shape
if __name__ == '__main__':
out_dir = sys.argv[1] if len(sys.argv) > 1 else os.curdir
a = assembly()
out_path = scad_render_to_file(a, out_dir=out_dir, include_orig_code=True)
print(f"{__file__}: SCAD file written to: \n{out_path}")