-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbezier.py
320 lines (258 loc) · 9.42 KB
/
bezier.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
'''
only need to install the following modules, code should run
pip install PyOpenGL
pip install glfw
'''
from OpenGL import GL as gl
import glfw
from OpenGL.GLU import *
# Rendering-related Code ------------------------------------------------------------
'''for capture screen shots'''
def dump_framebuffer_to_ppm(ppm_name, fb_width, fb_height):
pixelChannel = 3
pixels = gl.glReadPixels(0, 0, fb_width, fb_height, gl.GL_RGB, gl.GL_UNSIGNED_BYTE)
fout = open(ppm_name, "w")
fout.write('P3\n{} {}\n255\n'.format(int(fb_width), int(fb_height)))
for i in range(0, fb_height):
for j in range(0, fb_width):
cur = pixelChannel * ((fb_height - i - 1) * fb_width + j)
fout.write('{} {} {} '.format(int(pixels[cur]), int(pixels[cur+1]), int(pixels[cur+2])))
fout.write('\n')
fout.flush()
fout.close()
screen_width, screen_height = 1024, 512
x_mid = x_size = screen_width/2
y_mid = y_size = screen_height/2
ss_id = 0 # screenshot id
''' create window'''
if not glfw.init():
sys.exit(1)
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.window_hint(glfw.RESIZABLE, glfw.FALSE)
title = 'Curve Canvas'
window = glfw.create_window(screen_width, screen_height, title, None, None)
if not window:
print('GLFW Window Failed')
sys.exit(2)
glfw.make_context_current(window)
gl.glClearColor(0.3, 0.4, 0.5, 0)
shaders = {
gl.GL_VERTEX_SHADER: '''\
#version 330 core
layout(location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos, 1);
}
''',
gl.GL_FRAGMENT_SHADER: '''\
#version 330 core
out vec3 color;
void main() {
color = vec3(0.9,0.8,0.7);
}
'''}
program_id = gl.glCreateProgram()
shader_ids = []
for shader_type, shader_src in shaders.items():
shader_id = gl.glCreateShader(shader_type)
gl.glShaderSource(shader_id, shader_src)
gl.glCompileShader(shader_id)
# check if compilation was successful
result = gl.glGetShaderiv(shader_id, gl.GL_COMPILE_STATUS)
nlog = gl.glGetShaderiv(shader_id, gl.GL_INFO_LOG_LENGTH)
if nlog:
logmsg = gl.glGetShaderInfoLog(shader_id)
print("Shader Error", logmsg)
sys.exit(1)
gl.glAttachShader(program_id, shader_id)
shader_ids.append(shader_id)
gl.glLinkProgram(program_id)
result = gl.glGetProgramiv(program_id, gl.GL_LINK_STATUS)
nlog = gl.glGetProgramiv(program_id, gl.GL_INFO_LOG_LENGTH)
if nlog:
logmsg = gl.glGetProgramInfoLog(program_id)
print("Link Error", logmsg)
sys.exit(1)
gl.glUseProgram(program_id)
def draw_keys(keys_data):
key_number = int(len(keys_data)/3)
if key_number == 0:
return
vertex_array_id = gl.glGenVertexArrays(1)
gl.glBindVertexArray(vertex_array_id)
attr_id = 0
vertex_buffer = gl.glGenBuffers(1)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vertex_buffer)
array_type = (gl.GLfloat * len(keys_data))
sizeof_float = ctypes.sizeof(ctypes.c_float)
gl.glBufferData(gl.GL_ARRAY_BUFFER,
len(keys_data) * sizeof_float,
array_type(*keys_data),
gl.GL_STATIC_DRAW)
gl.glVertexAttribPointer(
attr_id, # attribute 0.
3, # components per vertex attribute
gl.GL_FLOAT, # type
False, # to be normalized?
0, # stride
None # array buffer offset
)
gl.glEnableVertexAttribArray(attr_id)
gl.glPointSize(10)
gl.glDrawArrays(gl.GL_POINTS, 0, key_number)
def draw_curve(curve_data, DRAW_MODE=gl.GL_POINTS):
if curve_data == None:
return
curve_pt_number = int(len(curve_data)/3)
print('curve pt num:', curve_pt_number)
if curve_pt_number == 0:
return
vertex_array_id = gl.glGenVertexArrays(1)
gl.glBindVertexArray(vertex_array_id)
attr_id = 0
vertex_buffer = gl.glGenBuffers(1)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vertex_buffer)
array_type = (gl.GLfloat * len(curve_data))
sizeof_float = ctypes.sizeof(ctypes.c_float)
gl.glBufferData(gl.GL_ARRAY_BUFFER,
len(curve_data) * sizeof_float,
array_type(*curve_data),
gl.GL_STATIC_DRAW)
gl.glVertexAttribPointer(
attr_id, # attribute 0.
3, # components per vertex attribute
gl.GL_FLOAT, # type
False, # to be normalized?
0, # stride
None # array buffer offset
)
gl.glEnableVertexAttribArray(attr_id)
gl.glPointSize(5)
gl.glDrawArrays(DRAW_MODE, 0, curve_pt_number)
def draw_control_polygon(key_points):
key_number = len(key_points)
segment_number = int((key_number) / 12)
for i in range(0, segment_number):
keys_on_one_polygon = key_points[4*i*3:4*(i+1)*3]
draw_curve(keys_on_one_polygon, gl.GL_LINE_STRIP)
#last strip
if key_number > segment_number * 12:
keys_on_one_polygon = key_points[segment_number * 12:]
draw_curve(keys_on_one_polygon, gl.GL_LINE_STRIP)
# end of Rendering-related Code ------------------------------------------------------------
class Point3:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __add__(self, other): return Point3(self.x+other.x, self.y+other.y, self.z+other.z)
def __sub__(self, other): return self.__add__(-other)
def __mul__(self, other): return Point3(self.x*other, self.y*other, self.z*other)
@classmethod
def point_list_to_num_list(cls, point_list):
if point_list == None or len(point_list) <= 0:
return
num_list = []
for i in range(0, len(point_list)):
pt = point_list[i]
num_list += [pt.x, pt.y, pt.z]
return num_list
@classmethod
def num_list_to_point_list(cls, num_list):
if num_list == None or len(num_list) <= 0:
return
pt_count = int(len(num_list) / 3)
point_list = []
for i in range(0, pt_count):
pt = Point3(num_list[3*i], num_list[3*i+1], num_list[3*i+2])
point_list.append(pt)
return point_list
key_points = []
def mouse_button_callback(window, button, action, mods):
global key_points
x_pos, y_pos = glfw.get_cursor_pos(window)
if button == glfw.MOUSE_BUTTON_RIGHT and action == glfw.PRESS:
print('right button at', x_pos, y_pos)
key_points.clear()
if button == glfw.MOUSE_BUTTON_LEFT and action == glfw.PRESS:
x_click = (x_pos-x_mid)/x_size
y_click = -(y_pos-y_mid)/y_size
key_points += [x_click, y_click, 0]
print('left button at', x_click, y_click)
def cursor_pos_callback(window, x_pos, y_pos):
x_hover = (x_pos - x_mid) / x_size
y_hover = -(y_pos - y_mid) / y_size
print(x_hover, y_hover)
# bezier curve interpolation ---------------------------------
def gen_polyline_points(key_points):
curve_points = key_points
return curve_points
def interpolate_bezier_position_at(t, p0, p1, p2, p3):
b0 = pow(1 - t, 3)
b1 = 3*t * pow(1 - t, 2)
b2 = 3*pow(t,2)*(1-t)
b3 = pow(t,3)
result = p0*b0 + p1*b1 + p2*b2 + p3*b3
return result
def gen_bezier_curve_points(key_points):
key_number = len(key_points)
if key_number < 4:
return
step_size = 0.01
step_count = int(1/step_size)
segment_number = int((key_number ) / 4)
print ('seg num', segment_number)
positions_on_curve = []
for i in range(0, segment_number):
#segment keys
p0=key_points[4 * i]
p1=key_points[4 * i + 1]
p2=key_points[4 * i + 2]
p3=key_points[4 * i + 3]
for s in range(0, step_count+1):
t = s * step_size
one_position_on_curve = interpolate_bezier_position_at(t, p0, p1, p2, p3)
positions_on_curve.append(one_position_on_curve)
return positions_on_curve
# run-time UI loop ---------------------------------------------------
glfw.set_mouse_button_callback(window, mouse_button_callback)
glfw.set_cursor_pos_callback(window, cursor_pos_callback)
positions_on_curve = []
while (
glfw.get_key(window, glfw.KEY_ESCAPE) != glfw.PRESS and
not glfw.window_should_close(window)
):
#press key p will capture screen shot
if glfw.get_key(window, glfw.KEY_P) == glfw.PRESS:
print ("Capture Window ", ss_id)
buffer_width, buffer_height = glfw.get_framebuffer_size(window)
ppm_name = "CurveCanvas-ss" + str(ss_id) + ".ppm"
dump_framebuffer_to_ppm(ppm_name, buffer_width, buffer_height)
ss_id += 1
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
if len(key_points) > 0:
draw_keys(key_points)
positions_on_curve = gen_polyline_points(Point3.num_list_to_point_list(key_points))
draw_control_polygon(Point3.point_list_to_num_list(positions_on_curve))
positions_on_curve = gen_bezier_curve_points(Point3.num_list_to_point_list(key_points))
draw_curve(Point3.point_list_to_num_list(positions_on_curve))
glfw.swap_buffers(window)
glfw.poll_events()
#release resource
for shader_id in shader_ids:
gl.glDetachShader(program_id, shader_id)
gl.glDeleteShader(shader_id)
gl.glUseProgram(0)
gl.glDeleteProgram(program_id)
'''
def interpolate_bezier_position_at2(t, p0, p1, p2, p3):
b0 = pow(1-t, 3)
b1 = 3*t*pow(1-t, 2)
b2 = 3*pow(t,2)*(1-t)
b3 = pow(t, 3)
result = p0*b0 + p1*b1 + p2*b2 + p3*b3
return result
'''