Skip to content
Jonathan De Wachter edited this page May 9, 2016 · 3 revisions

Overview

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.

Code

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()
Clone this wiki locally