Skip to content

Rounded Rectangle Shape

Jonathan De Wachter edited this page May 9, 2016 · 1 revision

Overview

In order to create Custom Shape Types in SFML, it is recommended to subclass the abstract class Shape and reimplement some methods. Due to porting difficulties, this does not seem possible in python-sfml.

However, the class sf.ConvexShape basically provides the same capabilities, but in a different format, without the need to subclass anything. But it is not as convenient, mainly because you can't simply make your own class out of it.

That's why I made a class CustomShape (which is actually an sf.TransformableDrawable) that contains and manages a sf.ConvexShape and lets you subclass it to implement the function points that should simply return a list of points. You also need to call update whenever the points change, and the points function will be called automatically. What's very important, you can use such a class exactly like you would use any sf.Shape, because the attributes that sf.ConvexShape exposes, e.g. outline_thickness or rotation are retranslated from the class.

Here is an example of how to use it, inspired by "Draw rounded rectangles".
You're free to do whatever you want with the code below.
It works with Python 3.x and should work with Python 2.7

Code

class RoundedRectangleShape(CustomShape):
    def __init__(self, size, radius, corner_points=5):
        CustomShape.__init__(self)
        
        self._radius = radius
        self._corner_points = corner_points
        self.size = size
        
    def points(self):
        points = []
        
        centers = [
            (self.size.x-self.radius, self.radius), (self.radius, self.radius),
            (self.radius, self.size.y-self.radius), (self.size.x-self.radius, self.size.y-self.radius)
        ]
        
        for index in range(self.corner_points*4):
            center_index = index//self.corner_points
            angle = (index-center_index)*math.pi/2/(self.corner_points-1);
            center = centers[center_index]
            points.append((center[0]+self.radius*math.cos(angle), center[1]-self.radius*math.sin(angle)))
        
        return points
    
    @property
    def size(self):
        return self._size
    @size.setter
    def size(self, value):
        self._size = sf.Vector2(*value)
        self.update()
    
    @property
    def radius(self):
        return self._radius
    @radius.setter
    def radius(self, value):
        self._radius = value
        self.update()
    
    @property
    def corner_points(self):
        return self._corner_points
    @corner_points.setter
    def corner_points(self, value):
        self._corner_points = value
        self.update()

The needed imports are: sfml as sf and math.

And here is the CustomShape "abstract" class itself:

class CustomShape(sf.TransformableDrawable):
    _retranslated_names = {name for name in dir(sf.ConvexShape) if not name.startswith('_')}
    
    def __init__(self):
        self._shape = sf.ConvexShape()
    
    def points(self):
        raise NotImplementedError("Abstract method")

    def update(self):
        pts = self.points()
        self._shape.point_count = len(pts)
        for i, p in enumerate(pts):
            self._shape.set_point(i, p)


    def draw(self, target, states):
        states.transform *= self.transform
        target.draw(self._shape, states)

    def __setattr__(self, name, value):
        if name in CustomShape._retranslated_names:
            setattr(self._shape, name, value)
        else:
            object.__setattr__(self, name, value)
    
    def __getattr__(self, name):
        return getattr(self._shape, name)

How-to-use

shape = RoundedRectangleShape((80, 50), 10, corner_points=8)
shape.position = (200, 300)
shape.fill_color = sf.Color.GREEN
target.draw(shape)
Clone this wiki locally