예제 #1
0
	def __init__(self, *args, **kwargs):

		self._show = {"curve":True, "controls":True, "bounds":True, "fps":False}

		self._throttle = 0.01
		self._color = (0,0,0, 255)
		self._controlColor = (0,0,0, 255)
		self._boundingLineColor = (0,0,0, 255)
		self._animatedLineColor = (0,255,0, 255)

		self._curve_set = BezierCollection(self._throttle)

		self.resetEverything()
예제 #2
0
class BezierCurve(object):
	SELECT_RADIUS = 10
	def __init__(self, *args, **kwargs):

		self._show = {"curve":True, "controls":True, "bounds":True, "fps":False}

		self._throttle = 0.01
		self._color = (0,0,0, 255)
		self._controlColor = (0,0,0, 255)
		self._boundingLineColor = (0,0,0, 255)
		self._animatedLineColor = (0,255,0, 255)

		self._curve_set = BezierCollection(self._throttle)

		self.resetEverything()
		
	def resetEverything(self):
		self._curve_set.reset() #have to do a reset instead of just making a new one because some buttons are bound to the object already
		
		self._zoom_factor = 1.3
		self._zoom = 1.0
		
		self._invalidated = False
		self._invalidated_all = False
		self._animating = False
		self._apply_all_curves = False
		self._animating_paused = False
		self._stepping = 0
		self._animation_length = 2.0
		self._animation_time = 0.0
		
	def set_throttle(self, throttle):
		self._throttle = float(throttle)
		self._curve_set.throttle = throttle
	def change_detail(self, amount):
		self._throttle += amount
		if self._throttle < MAX_ZOOM_DETAIL:
			self._throttle = MAX_ZOOM_DETAIL
		self._curve_set.throttle = self._throttle
		self.invalidate_all()
	def zoom(self, amount):
		self._zoom *= amount
		self._curve_set.scale(self._zoom, self.width/2, self.height/2)
		self.invalidate_all()
	def zoom_in(self, event=None):
		self.zoom(self._zoom_factor)
	def zoom_out(self, event=None):
		self.zoom(1/self._zoom_factor)
	def invalidate(self):
		self._invalidated = True
	def invalidate_all(self):
		self._invalidated = True
		self._invalidated_all = True
	def validate(self):
		self._invalidated = False
		self._invalidated_all = False
	def debug(self, val=None):
		if val is None:
			self._show["fps"] = not self._show["fps"]
		else:
			self._show["fps"] = bool(val)
	def update(self, dt):
		#self._fps_label.text = "fps = {0:.02f}".format(pyglet.clock.get_fps())
		
		if self._invalidated:
			self._curve_set.regenerate(self._invalidated_all)
			self._should_redraw = True
			self.validate()
		if self._animating and not self._animating_paused:
			self._animation_time += dt / self._animation_length
			n_canvas_time = math.floor(self._animation_time / self._curve_set.primary._throttle) * self._curve_set.primary._throttle
			if n_canvas_time >= 1.0:
				self.stop_animating()
			else:
				self._curve_set.calc_frame(n_canvas_time, self._apply_all_curves)
			self._should_redraw = True
		elif self._stepping != 0:
			self._animation_time += (self._stepping * dt) / self._animation_length
			n_canvas_time = math.floor(self._animation_time / self._curve_set.primary._throttle) * self._curve_set.primary._throttle
			self._curve_set.calc_frame(n_canvas_time, self._apply_all_curves)
			if n_canvas_time < 0:
				self._curve_set.reset_canvas_time(self._apply_all_curves)
			elif n_canvas_time > 1.0:
				self.stop_animating()
			self._should_redraw = True
	def clear(self):
		self.gc.foreground = self.white
		self.canvas.draw_rectangle(self.gc, True, 0, 0, self.width, self.height)
	def on_draw(self):
		self.clear()
		
		if self._show["curve"]:
			self.draw_curve()
		if self._show["controls"]:
			self.draw_controls()
		if self._show["bounds"]:
			self.draw_bounding_lines()
		
		if self._animating:
			self.draw_calc_lines()
		"""if self._show["fps"]:
			self._fps_label.draw()
			self._location_label.draw()"""
		#[button.draw() for button in self.buttons]
	def draw_curve(self):
		self.gc.foreground = self.canvas.get_colormap().alloc(self._color[0], self._color[1], self._color[2])
		[self.canvas.draw_lines(self.gc, points) for points in self._curve_set.get_curve_points() if len(points) > 0]
	def draw_controls(self):
		self.gc.foreground = self.canvas.get_colormap().alloc(self._controlColor[0], self._controlColor[1], self._controlColor[2])
		[self.canvas.draw_rectangle(self.gc, True, v[0], v[1], 10, 10) for v in self._curve_set.get_deselected_controls()]

		self.gc.foreground = self.canvas.get_colormap().alloc(abs(0xffff - self._controlColor[0]), abs(0xffff - self._controlColor[1]), abs(0xffff - self._controlColor[2]))
		[self.canvas.draw_rectangle(self.gc, True, v[0], v[1], 10, 10) for v in self._curve_set.get_selected_controls()]

	def draw_bounding_lines(self):
		self.gc.foreground = self.canvas.get_colormap().alloc(self._boundingLineColor[0], self._boundingLineColor[1], self._boundingLineColor[2])
		[self.canvas.draw_lines(self.gc, verts) for verts in self._curve_set.get_bounding_points() if len(verts) > 0]
	def draw_calc_lines(self):
		self.gc.foreground = self.canvas.get_colormap().alloc(self._animatedLineColor[0], self._animatedLineColor[1], self._animatedLineColor[2])
		for p, line_points in self._curve_set.get_calc_lines(self._apply_all_curves):
			if line_points != 0:
				self.canvas.draw_segments(self.gc, line_points)
				self.canvas.draw_rectangle(self.gc, True, int(p[0])-5, int(p[1])-5, 10, 10)
		
	def quit_app(self, event):
		self.quit = True
	def run(self):
		self.quit = False

		self.window = gtk.Window()
		self.window.set_title("Bezier Maker")
		self.window.connect("destroy", self.quit_app)
		
		self.width, self.height = 800, 600
		self.screen = gtk.DrawingArea()
		self.screen.set_size_request(self.width, self.height - 200)
		
		self.screen.connect("expose-event", self.canvas_expose)
		
		self.window.add_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.KEY_PRESS_MASK | gtk.gdk.KEY_RELEASE_MASK)
		self.window.connect("button-press-event", self.on_mouse_press)
		self.window.connect("button-release-event", self.on_mouse_release)
		self.window.connect("key-press-event", self.on_key_press)
		self.window.connect("key-release-event", self.on_key_release)
		self.window.connect("motion-notify-event", self.on_mouse_drag)
		self.window.set_resizable(False)
		
		self.screen.show()

		self.vbox = gtk.VBox()
		self._button_container = gtk.HButtonBox()
		
		buttons = [
			("Exit", lambda x: sys.exit(0), None),
			("Animate", BezierCurve.start_animating, self),
			("Clear", BezierCurve.run_clear, self),
			("Zoom In", BezierCurve.zoom_in, self),
			("Zoom Out", BezierCurve.zoom_out, self),
			("Add Curve", BezierCollection.add_curve, self._curve_set)
		]

		for label, func, obj in buttons:
			button = gtk.Button(label)
			if obj:
				button.connect_object("clicked", func, obj)
			else:
				button.connect("clicked", func)
			self._button_container.add(button)

		self.vbox.pack_start(self.screen)
		
		self.vbox.pack_end(self._button_container)

		self.window.add(self.vbox)
		self.window.show_all()

		self.canvas = gtk.gdk.Pixmap(self.screen.window, self.width, self.height)
		self.white = self.canvas.get_colormap().alloc(0xffff, 0xffff, 0xffff)

		self._should_redraw = True

		self._shift_down = False
		self._ctrl_down = False
		self._dragging_origin = None
		
		last_update_time = time.time()
		while not self.quit:
			while gtk.events_pending():
				gtk.main_iteration(False)
			now = time.time()
			elapsed = now - last_update_time
			if elapsed >= 1.0 / TICKS_PER_SEC:
				self.update(elapsed)
				last_update_time = now
				self.full_redraw()


	def canvas_expose(self, canvas, event):
		self.gc = event.window.new_gc()
		self._should_redraw = True
		self.full_redraw()
	def full_redraw(self):
		if self._should_redraw:
			self._should_redraw = False
			self.on_draw()
			self.screen.window.draw_drawable(self.screen.get_style().fg_gc[gtk.STATE_NORMAL], self.canvas, 0, 0, 0, 0, self.width, self.height)

	def start_animating(self, event=None):
		self._curve_set.reset_canvas_time(True)
		self._animating = True
		self._curve_set.calc_frame(0, self._apply_all_curves)
		self._animating_paused = False
	def stop_animating(self):
		self._animating = False
		
		self._animating_paused = False
		self._animation_time = 0.0
		self._curve_set.calc_frame(1, True)

		self.invalidate_all() if self._apply_all_curves else self.invalidate()

		self._apply_all_curves = False
		self._stepping = 0
	def pause_animating(self):
		self._animating_paused = not self._animating_paused
	def run_clear(self, event=None):
		self.resetEverything()
		self.clear()
		self.invalidate_all()
		
	#event bindings
	def on_mouse_press(self, canvas, event):
		x, y, button = event.x, event.y, event.button
		
		self._dragging_origin = (x, y)
		self.stop_animating()
		grabbed_index, curve_index, point = self._curve_set.find_point(self.SELECT_RADIUS, x, y)
		
		if button == 1:
			if point == self._curve_set.POINT_NOT_FOUND:
				self._curve_set.reset_selections()
				self._curve_set.primary.add_point(x, y)
			else:
				if self._ctrl_down:
					self._curve_set.deselect_from_curve(curve_index, grabbed_index)
				else:
					if not self._shift_down: #shift means add to the collection, so if theres no shifting then old selections are invalid
						self._curve_set.reset_selections()
					self._curve_set.select_from_curve(curve_index, grabbed_index)
		elif button == 3:
			self._curve_set.pop_index_from_curve(curve_index, grabbed_index)
		
		self.invalidate()
	def on_mouse_release(self, canvas, event):
		self._dragging_origin = None
	def on_mouse_motion(self, x, y, dx, dy):
		self._location_label.text = "pos = {0}, {1}".format(x, y)
	def on_mouse_drag(self, canvas, event):
		#self._location_label.text = "pos = {0}, {1}".format(x, y)
		
		if self._dragging_origin is not None:
			x, y = self._dragging_origin
			dx, dy = event.x - x, event.y - y
			self._dragging_origin = (event.x, event.y) #the d is per iteration of this function, so next time it should be d from here
			for curve, selections in self._curve_set.selections():
				for i in selections:
					existing_x, existing_y = curve.get_point(i)
					curve.set_point(i, existing_x + dx, existing_y + dy)
			self.invalidate()

	#TODO: refactor the keypress method into a key/method table system
	def on_key_press(self, canvas, event):
		symbol, modifiers = event.keyval, None

		if symbol == 65505 or symbol == 65506: #shift down
			self._shift_down = True
		elif symbol == 65507 or symbol == 65508:
			self._ctrl_down = True
		elif symbol == ord('a'): #animate it!
			self._apply_all_curves = self._ctrl_down
			self.start_animating()
		elif symbol == ord('c'):
			self.run_clear()
		elif symbol == ord('p'):
			self.pause_animating()
		elif symbol == ord('s'):
			self._apply_all_curves = self._ctrl_down
			if not (self._animating and not self._animating_paused):
				if not self._animating:
					self.start_animating()
					self._animating_paused = True
				self._stepping = 1
		elif symbol == ord('S'):
			self._apply_all_curves = self._ctrl_down
			if not (self._animating and not self._animating_paused):
				if not self._animating:
					self.start_animating()
					self._animating_paused = True

				self._stepping = -1
				if self._animation_time == 0:
					self._animation_time = 1.0
		elif symbol == ord('r'):
			for curve, selections in self._curve_set.selections():
				for i in reversed(list(selections)):
					curve.pop_point_at_index(i)
			self._curve_set.reset_selections()
			self.invalidate()
		elif symbol == ord('R'):
			self._curve_set.primary.pop_point_at_index(-1)
			self.invalidate()
		elif symbol == ord('d'):
			self.debug()
		elif symbol == ord(']'):
			self.zoom(self._zoom_factor)
		elif symbol == ord('['):
			self.zoom(1/self._zoom_factor)
	def on_key_release(self, canvas, event):
		symbol = event.keyval
		if symbol == 65505 or symbol == 65506: #shift up
			self._shift_down = False
		elif symbol == 65507 or symbol == 65508:
			self._ctrl_down = False
		if symbol == ord('s') or symbol == ord('S'):
			self._stepping = 0

	#visual toggles
	def toggle_curve(self, val=None):
		if val is None:
			self._show["curve"] = not self._show["curve"]
		else:
			self._show["curve"] = bool(val)
	def toggle_controls(self, val=None):
		if val is None:
			self._show["controls"] = not self._show["controls"]
		else:
			self._show["controls"] = bool(val)
	def toggle_bounds(self, val=None):
		if val is None:
			self._show["bounds"] = not self._show["bounds"]
		else:
			self._show["bounds"] = bool(val)

	def set_curve_color(self, color):
		color = [int(c, 0) if type(c) != int else c for c in color]
		assert len(color) == 3 or len(color) == 4
		if len(color) == 3:
			color.append(0xffff)
		self._color = color
	def set_control_color(self, color):
		color = [int(c, 0) if type(c) != int else c for c in color]
		assert len(color) == 3 or len(color) == 4
		if len(color) == 3:
			color.append(0xffff)
		self._controlColor = color
	def set_bounds_color(self, color):
		color = [int(c, 0) if type(c) != int else c for c in color]
		assert len(color) == 3 or len(color) == 4
		if len(color) == 3:
			color.append(0xffff)
		self._boundingLineColor = color
	def set_animation_color(self, color):
		color = [int(c, 0) if type(c) != int else c for c in color]
		assert len(color) == 3 or len(color) == 4
		if len(color) == 3:
			color.append(0xffff)
		self._animatedLineColor = color