### Author Topic: Round Ended Lines  (Read 3745 times)

0 Members and 1 Guest are viewing this topic.

#### Foaly

• Sr. Member
• Posts: 453
##### Round Ended Lines
« on: September 10, 2012, 12:03:05 pm »
Hi there,
I'm trying to implement a class that draws round ended lines. For that I made a class that inherits from sf::Shape. First I thought I implement a class that draws lines. I did that and i works. But now I'm not sure how to put the rounded ends on it. I'm thinking that I have to compute half a circle and just put it on the two end points, but I'm not quiet sure how to compute the "rotation" of the circle. Can somebody point me in the right direction?

Here is my code so far.

#ifndef ROUNDENDEDLINE_H
#define ROUNDENDEDLINE_H

#include <SFML/Graphics/Shape.hpp>
#include <cmath>
#include <iostream>

class CRoundendedLine : public sf::Shape
{
public:

CRoundendedLine(const sf::Vector2f& endPoint = sf::Vector2f(0, 0), const float width = 1.0);

void setEndPoint(const sf::Vector2f& endPoint);

void setWidth(const float width);

virtual unsigned int getPointCount() const;

virtual sf::Vector2f getPoint(unsigned int index) const;

private :

sf::Vector2f m_endPoint;
float m_Width;
};

#endif //ROUNDENDEDLINE_H

Implementation:
#include "RoundendedLine.h"

CRoundendedLine::CRoundendedLine(const sf::Vector2f& endPoint, const float width) : m_endPoint (endPoint), m_Width (width)
{
update();
}

void CRoundendedLine::setEndPoint(const sf::Vector2f& endPoint)
{
m_endPoint = endPoint;
update();
}

void CRoundendedLine::setWidth(const float width)
{
m_Width = width;
update();
}

unsigned int CRoundendedLine::getPointCount() const
{
return 4;
}

// Compute the normal of a segment
sf::Vector2f computeNormal(const sf::Vector2f& p1, const sf::Vector2f& p2)
{
sf::Vector2f normal(p1.y - p2.y, p2.x - p1.x);
float length = std::sqrt(normal.x * normal.x + normal.y * normal.y);
if (length != 0.f)
normal /= length;
return normal;
}

sf::Vector2f CRoundendedLine::getPoint(unsigned int index) const
{
sf::Vector2f P1(1.0, 0.0);
sf::Vector2f P2(m_endPoint + sf::Vector2f(1.0, 0.0) - getPosition());

// Compute the extrusion direction
sf::Vector2f Normal = computeNormal(P1, P2);
Normal *= m_Width / 2;

switch (index)
{
default:
case 0: return sf::Vector2f(P1 - Normal);
case 1: return sf::Vector2f(P2 - Normal);
case 2: return sf::Vector2f(P2 + Normal);
case 3: return sf::Vector2f(P1 + Normal);
}
}

Usage:
#include <SFML/Graphics.hpp>
#include "RoundendedLine.h"
#include <iostream>

int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML works!");

CRoundendedLine RoundendedLine;
RoundendedLine.setPosition(200,200);
RoundendedLine.setEndPoint(sf::Vector2f(270, 300));
RoundendedLine.setWidth(11);

sf::Vertex* startVertex =  new sf::Vertex(sf::Vector2f(200, 200), sf::Color::Red);
sf::Vertex* endVertex =  new sf::Vertex(sf::Vector2f(270, 300), sf::Color::Blue);

while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
// Close Event
if (event.type == sf::Event::Closed)
window.close();

// Escape key pressed
if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape))
window.close();
}

window.clear();
window.draw(RoundendedLine);
window.draw(startVertex, 1, sf::Points);
window.draw(endVertex, 1, sf::Points);
window.display();
}

return 0;
}

#### Laurent

• Hero Member
• Posts: 32504
##### Re: Round Ended Lines
« Reply #1 on: September 10, 2012, 12:58:19 pm »
You must first compute the angle of your line (std::atan2(dy, dx)). Then you compute circle points between -Pi/2 + angle and Pi/2 + angle, with a radius of width/2. And that's it
Laurent Gomila - SFML developer

#### Foaly

• Sr. Member
• Posts: 453
##### Re: Round Ended Lines
« Reply #2 on: September 10, 2012, 02:56:08 pm »
Aaah thank you very much, for the quick reply! This is what I thought it would be like. I just didn't know how exactly.
I tried to implement it using a vertex array, just to see if the algorithm works. But somehow i can't get it to work... Here is my code:
What am I doing wrong?
float angle = atan2(0, 100);
sf::VertexArray lines(sf::LinesStrip, 16);
for (int i = 0; i < 15; i++)
{
angle = i * ((M_PI / 2 + angle) / 16) - (M_PI / 2 - angle);
float x = std::cos(angle) * radius;
float y = std::sin(angle) * radius;

lines[i].position = sf::Vector2f(radius + x + 100, radius + y + 100);
}
lines[15] = lines[0];

#### Laurent

• Hero Member
• Posts: 32504
##### Re: Round Ended Lines
« Reply #3 on: September 10, 2012, 03:11:26 pm »
Without testing, I'd say:
angle = i * M_PI / 15 - M_PI / 2 + angle;
« Last Edit: September 10, 2012, 03:13:17 pm by Laurent »
Laurent Gomila - SFML developer

#### Foaly

• Sr. Member
• Posts: 453
##### Re: Round Ended Lines
« Reply #4 on: September 10, 2012, 03:17:00 pm »
Hmm no that doesn't change anything at all. It still looks completely random...

edit: Well now it looks a little more like a semi circle, but it's still choppy and not round
« Last Edit: September 10, 2012, 03:23:32 pm by Foaly »

#### Laurent

• Hero Member
• Posts: 32504
##### Re: Round Ended Lines
« Reply #5 on: September 10, 2012, 03:25:14 pm »
Can you show a screenshot?
Laurent Gomila - SFML developer

#### Foaly

• Sr. Member
• Posts: 453
##### Re: Round Ended Lines
« Reply #6 on: September 10, 2012, 03:39:43 pm »
This is what it looks like:

#### Laurent

• Hero Member
• Posts: 32504
##### Re: Round Ended Lines
« Reply #7 on: September 10, 2012, 03:50:55 pm »
Arf, you have two variables named "angle", that's why the result is messed up

The following code works:
float start = atan2(0, 100);
sf::VertexArray lines(sf::LinesStrip, 16);
for (int i = 0; i < 15; i++)
{
float angle = i * M_PI / 14 - M_PI / 2 + start;
float x = std::cos(angle) * radius;
float y = std::sin(angle) * radius;

lines[i].position = sf::Vector2f(radius + x + 100, radius + y + 100);
}
lines[15] = lines[0];
« Last Edit: September 10, 2012, 03:52:49 pm by Laurent »
Laurent Gomila - SFML developer

#### Foaly

• Sr. Member
• Posts: 453
##### Re: Round Ended Lines
« Reply #8 on: September 10, 2012, 05:53:54 pm »
Ah that makes sense... Alright it took me some time, but I got it work with my class. Everything is working like a charm now. Thanks again for the help.
Here is my working code:
sf::Vector2f CRoundendedLine::getPoint(unsigned int index) const
{
sf::Vector2f P1(1.0, 0.0);
sf::Vector2f P2(m_endPoint + sf::Vector2f(1.0, 0.0) - getPosition());

sf::Vector2f offset;
int iFlipDirection;

if(index < 15)
{
offset = P2;
iFlipDirection = 1;
}
else
{
offset = P1;
iFlipDirection = -1;
index -= 15;
}

float start = -atan2(P1.y - P2.y, P2.x - P1.x);

float angle = index * M_PI / 14 - M_PI / 2 + start;
float x = std::cos(angle) * m_Width / 2;
float y = std::sin(angle) * m_Width / 2;

return sf::Vector2f(offset.x + x * iFlipDirection, offset.y + y * iFlipDirection);
}

#### Laurent

• Hero Member
• Posts: 32504
##### Re: Round Ended Lines
« Reply #9 on: September 10, 2012, 08:17:20 pm »
Quote
I think so
Laurent Gomila - SFML developer

#### Foaly

• Sr. Member
• Posts: 453
##### Re: Round Ended Lines
« Reply #10 on: September 10, 2012, 08:47:14 pm »
Great!
It's done! https://github.com/SFML/SFML/wiki/Round-Ended-Lines
I'm glad i was able to contribute something to the wiki

Also I made a new Drawable section on the Source Code Page, because a couple source codes seemed to be misplaced in the Game Engine category.