-
Notifications
You must be signed in to change notification settings - Fork 41
Curvy strokes
The utility below allows your to draw curvy strokes by adding points to an object. It uses Bezier curves to compute tiny segments which give you curves once attached together. Create a Curve
object and draw it using window.draw()
after adding several points to it (minimum 3 points before it starts generating the curve).
In the following sections, you'll find two functional scripts (targeting Python 2.X) which will allow you to experiment the code.
You're free to do whatever you want with the code below.
from math import sqrt, pow
import sfml as sf
def quadratic_curve(p0, p1, p2, count, stop=1):
step = 1. / count
start = 0
while start < stop:
t = start
x = pow(1.-t, 2) * p0.x + 2*(1.-t)*t*p1.x+pow(t, 2)*p2.x
y = pow(1.-t, 2) * p0.y + 2*(1.-t)*t*p1.y+pow(t, 2)*p2.y
yield sf.Vector2(x, y)
start += step
class Curve(sf.Drawable):
def __init__(self):
sf.Drawable.__init__(self)
# list of original points
self._points = []
# list of computed vertices that shapes the curve
self._vertices = sf.VertexArray()
self._vertices.primitive_type = sf.PrimitiveType.LINES_STRIP
# list of visual marks for debug purpose
self._marks = []
self._segments = sf.VertexArray()
self._segments.primitive_type = sf.PrimitiveType.LINES_STRIP
self._complexity = 10
self._debug_mode = False
def add_point(self, position):
self._points.append(position)
# in debug mode, create visual marks
mark = sf.CircleShape(2)
mark.origin = (1, 1)
mark.position = position
mark.fill_color = sf.Color.RED
mark.outline_color = sf.Color.BLACK
mark.outline_thickness = 1
self._marks.append(mark)
self._segments.append(sf.Vertex(position, sf.Color.RED))
if len(self._points) <= 2:
self._last_computed_vertice = sf.Vector2(self._points[0].x, self._points[0].y)
# compute half the next curve
index = len(self._points)-1
curve = list(quadratic_curve(self._last_computed_vertice, self._points[index-1], self._points[index], self._complexity, 0.5))
for point in curve:
self._vertices.append(sf.Vertex(point, sf.Color.BLACK))
self._last_computed_vertice = sf.Vector2(curve[-1].x, curve[-1].y)
def clear(self):
self._points = []
self._vertices.clear()
self._marks = []
self._segments.clear()
def draw(self, target, states):
target.draw(self._vertices, states)
# in debug mode, display original segments and circles at end points
if self.debug_mode:
target.draw(self._segments, states)
for mark in self._marks:
target.draw(mark, states)
def _recompute(self):
points = self._points
self.clear()
for point in points:
self.add_point(point)
def _get_complexity(self):
return self._complexity
def _set_complexity(self, complexity):
self._complexity = complexity
self._recompute()
def _get_debug_mode(self):
return self._debug_mode
def _set_debug_mode(self, debug_mode):
if self._debug_mode != debug_mode:
self._debug_mode = debug_mode
complexity = property(_get_complexity, _set_complexity)
debug_mode = property(_get_debug_mode, _set_debug_mode)
#How-to-use The following example will tell you better. Copy it the content to a local file, run it and draw your curve using the following commands (by default, you have to click to add points):
- R - Reset the curve and start over.
- M - Switch mode; either click to add points or move your mouse.
- D - Toggle the debug mode which displays the original path with marks.
Use your arrow key (up and down) to increase or decrease the precision of the curve. Note that it has to recompute the entire curve so if your curve is really long, you get small lags.
import sfml as sf
from curve import Curve
window = sf.RenderWindow(sf.VideoMode(640, 480), "SFML - Bezier curves")
window.key_repeat_enabled = False
curve = Curve()
curve.debug_mode = True
click_mode = True
while window.is_open:
for event in window.events:
if event == sf.CloseEvent:
window.close()
if event == sf.KeyEvent:
if not event.pressed:
break
if event.code == sf.Keyboard.R:
curve.clear()
curve.complexity = 10
elif event.code == sf.Keyboard.UP:
curve.complexity += 1
elif event.code == sf.Keyboard.DOWN:
curve.complexity -= 1
elif event.code == sf.Keyboard.D:
curve.debug_mode = not curve.debug_mode
elif event.code == sf.Keyboard.M:
click_mode = not click_mode
if click_mode:
if event == sf.MouseButtonEvent:
if event.pressed:
curve.add_point(event.position)
else:
if event == sf.MouseMoveEvent:
curve.add_point(event.position)
window.clear(sf.Color.WHITE)
window.draw(curve)
window.display()