Skip to content

Source: Drawable Line Shape

blue-prawn edited this page Nov 9, 2012 · 2 revisions

Drawable Line Shape

Class summary

This is an SFML-based class that produces a fast-rendering shape drawn with display lists and GL_LNES. Inherits from sf::Drawable, but some of those functions don't work, specifically SetColor since it's overrided per line segment. To change the color, use the SetLineSegmentColor method. To create a shape, use AddPoint to add all the vertices, catching the returned ID. Then connect them with AddLineSegment. The shape can then be modified by adding/removing/moving points and line segments

This class meant to work with SFML (www.sfml-dev.org) and is released under the same liscence as SFML, zlib/png.

Class written by "Quasius"

SFML 2

In SFML 2 this class is lapsed, since the library has it's own ability to render lines fast:

Single lines

sf::VertexArray lines(sf::Lines, 4);
lines[0].position = sf::Vector2f(10, 0);
lines[1].position = sf::Vector2f(20, 0);
lines[2].position = sf::Vector2f(30, 5);
lines[3].position = sf::Vector2f(40, 2);

window.draw(lines);

Connected lines

sf::VertexArray lines(sf::LinesStrip, 4);
lines[0].position = sf::Vector2f(10, 0);
lines[1].position = sf::Vector2f(20, 0);
lines[2].position = sf::Vector2f(30, 5);
lines[3].position = sf::Vector2f(40, 2);

window.draw(lines);

From this point it's easy to write a wrapper class if needed.

Further functions like setting colors etc. see the documentation about the sf::VertexArray and sf::Vertex.

DrawableLineShape.h

/*
A class that produces a fast-rendering shape drawn with display lists and GL_LNES.
Inherits from sf::Drawable, but some of those functions don't work,
specifically SetColor since that's overridden per line-segment (default white).
To change the color, use the SetLineSegmentColor method.
To create a shape, use AddPoint to add all the vertices, catching the returned ID.
Then connect them with AddLineSegment.
	 
This class meant to work with SFML (www.sfml-dev.org) and is released under the same
license as SFML, zlib/png.
Class written by "Quasius"
*/

#ifndef DRAWABLE_LINE_SHAPE
#define DRAWABLE_LINE_SHAPE

#include <vector>

#include <SFML/Graphics.hpp>

using namespace std;
using namespace sf;

class DrawableLineShape : public sf::Drawable
{
	//DEFINITIONS, STRUCTS, ETC
protected:

	struct LineSegmentInfo
	{
		unsigned short nPoint1, nPoint2;
		sf::Color color;
		//false if this line segment is in a recycled state
		bool bActive;

		LineSegmentInfo(const unsigned short nPoint1ID, const unsigned short nPoint2ID, const Color& lineColor = Color(255, 255, 255)) :
				bActive(true)
		{
			nPoint1 = nPoint1ID;
			nPoint2 = nPoint2ID;
			color = lineColor;
		}
	};

	struct PointInfo
	{
		Vector2f v2fPoint;
		//false if this point is in a recycled state
		bool bActive;

		PointInfo(const Vector2f& v2fLinePoint = Vector2f(0.0f, 0.0f)) :
			bActive(true)
		{
			v2fPoint = v2fLinePoint;
		}
	};

	//METHODS
public:

	//Constructur
	DrawableLineShape();

	//Assignment operator
	DrawableLineShape& operator = (const DrawableLineShape& that);

	//Destructor
	~DrawableLineShape();

	//adds a point to the line shape
	//returns an ID for that point
	unsigned short AddPoint(const Vector2f& v2fPointPosition);

	//adds a point to the line shape
	//returns an ID for that point
	unsigned short AddPoint(const float fXPosition, const float fYPosition);

	//removes the passed point.
	//Also removes any line segments that depended on that point
	void RemovePoint(const unsigned short nPointID);

	//Moves the position of the point (Attached line segments automatically move appropriately)
	void MovePoint(const unsigned short nPointID, const Vector2f& v2fNewPosition);

	//Moves the position of the point (Attached line segments automatically move appropriately)
	void MovePoint(const unsigned short nPointID, const float fNewPositionX, const float fNewPositionY);

	//Adds a line segment that references 2 point IDs
	//Does nothing if both point IDs don't exist and returns -1
	//Object won't render if there isn't at least 1 line segment
	//Returns an ID to the line segment
	unsigned short AddLineSegment(const unsigned short nPoint1ID, const unsigned short nPoint2ID, const Color& color = sf::Color(255, 255, 255));

	//removes the passed line segment.
	//Also removes any no-longer used points if bRemoveOrphanPoints is true
	void RemoveLineSegment(const unsigned short nLineSegmentID, bool bRemoveOrphanedPoints = true);

	//sets the thickness of the passed line segment
	void SetLineThickness(const float fLineThickness);

	//sets the color of the passed line segment
	void SetLineSegmentColor(const unsigned short nLineSegmentID, const Color& color);

	//returns the color of the passed line segment ID
	const Color& GetLineSegmentColor(const unsigned short nLineSegmentID) const;

	//gets the number of current line segments
	int GetNumLineSegments();

	//removes all points and line segments
	void Clear();

protected:

	//See Drawable::Render
	virtual void Render(const RenderWindow& Window);

	//MEMBERS
protected:

	//All the points
	vector<PointInfo> m_vPoints;
	//All the line segments
	vector<LineSegmentInfo> m_vLineSegments;
	//the line thickness
	float m_fLineThickness;
	//Stores if the display list needs to be recompiled or not
	bool m_bIsDisplayListCurrent;
	//the ID for the OpenGL display list
	int m_nDisplayListID;
};

#endif	

DrawableLineShape.cpp

#include "DrawableLineShape.h"
     
//Constructur
DrawableLineShape::DrawableLineShape() :
  m_fLineThickness(1.0f),
  m_bIsDisplayListCurrent(false)
{
	m_nDisplayListID = glGenLists(1);
}
     
//Destructor
DrawableLineShape::~DrawableLineShape()
{
	glDeleteLists(m_nDisplayListID, 1);
}

//Assignment operator
DrawableLineShape& DrawableLineShape::operator = (const DrawableLineShape& that)
{
	if (this != &that)
	{
		m_vPoints = that.m_vPoints;
		m_vLineSegments = that.m_vLineSegments;
		m_bIsDisplayListCurrent = that.m_bIsDisplayListCurrent;
	}

	return *this;
}

//adds a point to the line shape
//returns an ID for that point
unsigned short DrawableLineShape::AddPoint(const sf::Vector2f& v2fPointPosition)
{
	//search for a recycled point to use
	for (unsigned short i = 0; i < m_vPoints.size(); ++i)
	{
		if (!m_vPoints[i].bActive)
		{
			m_vPoints[i].v2fPoint = v2fPointPosition;
			m_vPoints[i].bActive = true;
			return i;
		}
	}

	//if no recycled point, add a new point
	m_vPoints.push_back(PointInfo(v2fPointPosition));
	return (unsigned short)(m_vPoints.size() - 1);
     
	//display commands are out-of-date now
	m_bIsDisplayListCurrent = false;
}
     
//adds a point to the line shape
//returns an ID for that point
unsigned short DrawableLineShape::AddPoint(const float fXPosition, const float fYPosition)
{
	return AddPoint(sf::Vector2f(fXPosition, fYPosition));
}
     
//removes the passed point.
//Also removes any line segments that depended on that point
void DrawableLineShape::RemovePoint(const unsigned short nPointID)
{
	//remove the point
	m_vPoints[nPointID].bActive = false;
 
	//remove all line segments referencing that point
	for (unsigned int i = 0; i < m_vLineSegments.size(); ++i)
	{
		if (m_vLineSegments[i].bActive &&
		    (m_vLineSegments[i].nPoint1 == nPointID ||
		     m_vLineSegments[i].nPoint2 == nPointID))
			RemoveLineSegment(i);
	}
     
	//display commands are out-of-date now
	m_bIsDisplayListCurrent = false;
}
     
//Moves the position of the point (Attached line segments automatically move appropriately)
void DrawableLineShape::MovePoint(const unsigned short nPointID, const Vector2f& v2fNewPosition)
{
	m_vPoints[nPointID].v2fPoint = v2fNewPosition;
     
	//display commands are out-of-date now
	m_bIsDisplayListCurrent = false;
}
     
//Moves the position of the point (Attached line segments automatically move appropriately)
void DrawableLineShape::MovePoint(const unsigned short nPointID,
		const float fNewPositionX, const float fNewPositionY)
{
	m_vPoints[nPointID].v2fPoint.x = fNewPositionX;
	m_vPoints[nPointID].v2fPoint.y = fNewPositionY;
     
	//display commands are out-of-date now
	m_bIsDisplayListCurrent = false;
}
     
//Adds a line segment that references 2 point IDs
//Does nothing if both point IDs don't exist and returns -1
//Object won't render if there isn't at least 1 line segment
//Returns an ID to the line segment
unsigned short DrawableLineShape::AddLineSegment(
		const unsigned short nPoint1ID, const unsigned short nPoint2ID, const sf::Color& color)
{
	//search for a recycled line segment to use
	for (unsigned short = 0; i < m_vLineSegments.size(); ++i)
	{
		if (!m_vLineSegments[i].bActive)
		{
			m_vLineSegments[i].nPoint1 = nPoint1ID;
			m_vLineSegments[i].nPoint2 = nPoint2ID;
			m_vLineSegments[i].color = color;
			m_vLineSegments[i].bActive = true;
			return (unsigned short)i;
		}
	}
     
	//if no recycled line segment, add a new one
	m_vLineSegments.push_back(LineSegmentInfo(nPoint1ID, nPoint2ID, color));
	return (unsigned short)(m_vLineSegments.size() - 1);
     
	//display commands are out-of-date now
	m_bIsDisplayListCurrent = false;
}
     
//removes the passed line segment.
//Also removes any no-longer used points if bRemoveOrphanPoints is true
void DrawableLineShape::RemoveLineSegment(const unsigned short nLineSegmentID, bool bRemoveOrphanedPoints)
{
	//remove the line segment
	m_vLineSegments[nLineSegmentID].bActive = false;
     
	//remove all newly-orphaned points, if requested
	if (bRemoveOrphanedPoints)
	{
		//get the point IDs (we deactivated the segment above, but not change/delete values)
		unsigned short nPointID1 = m_vLineSegments[nLineSegmentID].nPoint1;
		unsigned short nPointID2 = m_vLineSegments[nLineSegmentID].nPoint2;
 
		//assume no reference until found
		bool bPoint1Referenced = false;
		bool bPoint2Referenced = false;

		//iterate through lines searching for references to both points
		for (unsigned int nLineIndex = 0; nLineIndex < m_vLineSegments.size(); ++nLineIndex)
		{
			if (!m_vLineSegments[nLineIndex].bActive)
				continue;
     
			if (m_vLineSegments[nLineIndex].nPoint1 == nPointID1 ||
			    m_vLineSegments[nLineIndex].nPoint2 == nPointID1)
				bPoint1Referenced = true;
			if (m_vLineSegments[nLineIndex].nPoint1 == nPointID2 ||
			    m_vLineSegments[nLineIndex].nPoint2 == nPointID2)
				bPoint2Referenced = true;
     
			//early out
			if (bPoint1Referenced && bPoint2Referenced)
				break;
		}
     
		//remove unreferenced points
		if (!bPoint1Referenced)
			m_vPoints[nPointID1].bActive = false;
		if (!bPoint2Referenced)
			m_vPoints[nPointID2].bActive = false;
	}
     
	//display commands are out-of-date now
	m_bIsDisplayListCurrent = false;
}
     
//sets the thickness of the passed line segment
void DrawableLineShape::SetLineThickness(const float fLineThickness)
{
	m_fLineThickness = fLineThickness;
     
	//display commands are out-of-date now
	m_bIsDisplayListCurrent = false;
}
     
//sets the color of the passed line segment
void DrawableLineShape::SetLineSegmentColor(
		const unsigned short nLineSegmentID, const sf::Color& color)
{
	m_vLineSegments[nLineSegmentID].color = color;
     
	//display commands are out-of-date now
	m_bIsDisplayListCurrent = false;
}
     
//returns the color of the passed line segment ID
const Color& DrawableLineShape::GetLineSegmentColor(const unsigned short nLineSegmentID) const
{
	return m_vLineSegments[nLineSegmentID].color;
}
     
     
//gets the number of current line segments
int DrawableLineShape::GetNumLineSegments()
{
	int nNumLineSegments = 0;
     
	//count each active line segment
	for (unsigned int i = 0; i < m_vLineSegments.size(); ++i)
	{
		if (m_vLineSegments[i].bActive)
			++nNumLineSegments;
	}
     
	return nNumLineSegments;
}
     
//removes all points and line segments
void DrawableLineShape::Clear()
{
	unsigned int i;
	//deactivate points
	for (i = 0; i < m_vPoints.size(); ++i)
		m_vPoints[i].bActive = false;
	//deactivate line segments
	for (i = 0; i < m_vLineSegments.size(); ++i)
		m_vLineSegments[i].bActive = false;
     
	//display commands are out-of-date now
	m_bIsDisplayListCurrent = false;
}
     
     
//See Drawable::Render
void DrawableLineShape::Render(const RenderWindow& Window)
{
	//if the display list is current, just run it
	if (m_bIsDisplayListCurrent)
	{
		glCallList(m_nDisplayListID);
		return;
	}
     
	// Make sure the shape has at least 1 line segment
	if (m_vLineSegments.size() < 1)
		return;
     
	//store all these generated commands in the display list
	glNewList(m_nDisplayListID, GL_COMPILE_AND_EXECUTE);
     
	// Shapes only use color, no texture
	glDisable(GL_TEXTURE_2D);
     
	// Draw the shape
	Color lineColor;
	Vector2f v2fLinePoint1, v2fLinePoint2;
     
	//set line thickness
	glLineWidth(m_fLineThickness);
	glBegin(GL_LINES);
	for (unsigned int i = 0; i < m_vLineSegments.size(); ++i)
	{
		if (!m_vLineSegments[i].bActive)
			continue;
     
		//set color
		lineColor = m_vLineSegments[i].color;
		glColor4ub(lineColor.r, lineColor.g, lineColor.b, lineColor.a);
		//pass line segment end-points
		v2fLinePoint1 = m_vPoints[m_vLineSegments[i].nPoint1].v2fPoint;
		v2fLinePoint2 = m_vPoints[m_vLineSegments[i].nPoint2].v2fPoint;
		glVertex2f(v2fLinePoint1.x, v2fLinePoint1.y);
		glVertex2f(v2fLinePoint2.x, v2fLinePoint2.y);
	}
	glEnd();
     
	glEndList();
     
	m_bIsDisplayListCurrent = true;
}
Clone this wiki locally