Sliders are very nifty UI control tool, they provide some sort of natural feedback to the user visually when they interact with it. Especially in a touch user interface sliders are very useful. In PyMT we already have Horizontal and Vertical rectangular sliders.
But I needed something different, a circular slider for the next widget that I wanted to develop a Quarter Circle color picker. So I sat down with the sketchbook again and noted down the following pointers
- The Slider must provide a way to limit the angle of the slider i.e not full 360 always, meaning it should provide me a way to select 60 Degree Circular sliders too.
- Must be rotatable so that it can be set in any direction.
- Must provide a way for customizations like color, size etc.
- Must provide all features of a regular rectangular slider.
The three main things that i had to take care when designing the algorithm was that
- Drawing the Ring in Opengl
- Angle Calculation to fill in the slider as you touch it
- Collision detection, as the existing collision detection code was for rectangular widgets
Let start with each part one at a time
1. OPENGL Ring
I was wondering how I can make a ring like structure in opengl, I thought maybe make two circles one of the outer radius another of the inner radius, with the difference between the two being the thickness of the ring. But a bit of browsing on the internet revealed that there is a OPENGL call to draw the exact same thing, and its very customizable too.
void gluPartialDisk( GLUquadric* quad, GLdouble inner, GLdouble outer, GLint slices, GLint loops, GLdouble start, GLdouble sweep )
you can read more about gluPartialDisk here. This really simplified the drawing part of the slider.
2. Angle Calculation
The angle calculation was easy once i found out how to do it,
- First Make a vector of the one edge of the slider w.r.t the center.
- Now as the touch is moved on the slider, take one more vector at the current touch location w.r.t to the center
- Find the angle between the two vector, this is the fill angle for the slider
3. Collision Detection
Next problem was collision detection algorithm, since the sliders where in a arc form, the collision detection has two boundary conditions and which itself formed the algorithm for collision detection.
- The distance of touch from the center of the slider must be lesser than the outer radius and lesser than the outer radius-thickness of the slider
- The Angle created by the current touch location and the vertical axis must be greater than the angle of the start edge of the slider with the vertical axis and lesser than the angle of the end edge of the slider with the vertical axis
Using the above two conditions I wrote the following code which works very nicely
def collide_point(self, x, y): #A algorithm to find the whether a touch is within a semi ring point_dist = Vector(self.pos).distance((x, y)) point_angle = Vector(self.radius_line).angle((x - self.pos, y - self.pos)) if point_angle < 0: point_angle=360+point_angle if point_angle <= self.sweep_angle and point_angle >=0: return point_dist<= self.radius and point_dist > self.radius-self.thickness
Here is a screenshot of the circular sliders, this was taken by my other mentor Thomas, see how they can be stacked over one another and still the collision detection algorithm can work flawlessly
You can read more about the widget here. In the next blog i’ll be posting about Color Wheel, and that involves alot of trigometric equations 🙂 .