Files
mapmap/Shape.h

392 lines
9.3 KiB
C++

/*
* Shape.h
*
* (c) 2013 Sofian Audry -- info(@)sofianaudry(.)com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SHAPE_H_
#define SHAPE_H_
#include <QtGlobal>
#include <QPointF>
#include <vector>
#include <map>
#include <tr1/memory>
#include <iostream>
/**
* Point (or vertex) on the 2-D canvas.
*/
struct Point
{
double x;
double y;
Point(double x_, double y_) : x(x_), y(y_) {}
Point(const QPointF& p) : x(p.x()), y(p.y()) {}
QPointF toQPointF() const
{
return QPointF(x, y);
}
};
/**
* Series of vertices. (points)
*/
class Shape
{
public:
typedef std::tr1::shared_ptr<Shape> ptr;
Shape() {}
Shape(std::vector<Point> vertices_) :
vertices(vertices_)
{}
virtual ~Shape() {}
virtual void build() {}
int nVertices() const { return vertices.size(); }
Point getVertex(int i) const
{
return vertices[i];
}
void setVertex(int i, Point v)
{
vertices[i] = v;
}
void setVertex(int i, double x, double y)
{
vertices[i].x = x;
vertices[i].y = y;
}
/** Return true if Shape includes point (x,y), false otherwise
* Algorithm should work for all polygons, including non-convex
* Found at http://www.cs.tufts.edu/comp/163/notes05/point_inclusion_handout.pdf
*/
bool includesPoint(int x, int y)
{
Point *prev = NULL, *cur;
int left = 0, right = 0, maxy, miny;
for (std::vector<Point>::iterator it = vertices.begin() ; it !=
vertices.end(); it++)
{
if (!prev) {
prev = &vertices.back();
}
cur = &(*it);
miny = std::min(cur->y, prev->y);
maxy = std::max(cur->y, prev->y);
if (y > miny && y < maxy) {
if (prev->x == cur->x)
{
if (x < cur->x)
right++;
else left++;
}
else
{
double slope = (cur->y - prev->y) / (cur->x - prev->x);
double offset = cur->y - slope * cur->x;
int xintersect = int((y - offset ) / slope);
if (x < xintersect)
right++;
else left++;
}
}
prev = &(*it);
}
if (right % 2 && left % 2)
return true;
return false;
}
/* Translate all vertices of shape by the vector (x,y) */
void translate(int x, int y)
{
for (std::vector<Point>::iterator it = vertices.begin() ; it !=
vertices.end(); ++it)
{
it->x += x;
it->y += y;
}
}
int nVertices()
{
return vertices.size();
}
protected:
std::vector<Point> vertices;
};
/**
* Four-vertex shape.
*/
class Quad : public Shape
{
public:
Quad() {}
Quad(Point p1, Point p2, Point p3, Point p4)
{
vertices.push_back(p1);
vertices.push_back(p2);
vertices.push_back(p3);
vertices.push_back(p4);
}
virtual ~Quad() {}
};
/**
* Triangle shape.
*/
class Triangle : public Shape
{
public:
Triangle() {}
Triangle(Point p1, Point p2, Point p3)
{
vertices.push_back(p1);
vertices.push_back(p2);
vertices.push_back(p3);
}
virtual ~Triangle() {}
};
class Mesh : public Quad {
public:
Mesh() : _nColumns(0), _nRows(0) {
init(1, 1);
}
Mesh(Point p1, Point p2, Point p3, Point p4, int nColumns=2, int nRows=2)
: Quad(p1, p2, p3, p4), _nColumns(0), _nRows(0)
{
Q_ASSERT(nColumns >= 2 && nRows >= 2);
init(nColumns, nRows);
}
virtual ~Mesh() {}
void resizeVertices2d(std::vector< std::vector<int> >& vertices2d, int nColumns, int nRows)
{
vertices2d.resize(nColumns);
for (int i=0; i<nColumns; i++)
vertices2d[i].resize(nRows);
}
void init(int nColumns, int nRows)
{
// Create vertices correspondence of bouding quad.
resizeVertices2d(_vertices2d, 2, 2);
_vertices2d[0][0] = 0;
_vertices2d[1][0] = 1;
_vertices2d[1][1] = 2;
_vertices2d[0][1] = 3;
// Init number of columns and rows.
_nColumns = _nRows = 2;
// Add extra columns and rows.
for (int i=0; i<nColumns-2; i++)
addColumn();
for (int i=0; i<nRows-2; i++)
addRow();
}
/**
* This is what _vertices2d looks like.
*
* 0----4----6----1
* | | | |
* 8----9---10---11
* | | | |
* 3----5----7----2
*/
// vertices 0..3 = 4 corners
//
void addColumn()
{
// Create new vertices 2d (temporary).
std::vector< std::vector<int> > newVertices2d;
resizeVertices2d(newVertices2d, nColumns()+1, nRows());
// Left displacement of points already there.
float leftMoveProp = 1.0f/(nColumns()-1) - 1.0f/nColumns();
// Add a point at each row.
int k = nVertices();
for (int y=0; y<nRows(); y++)
{
// Get left and right vertices.
QPointF left = getVertex( _vertices2d[0] [y] ).toQPointF();
QPointF right = getVertex( _vertices2d[nColumns()-1][y] ).toQPointF();
QPointF diff = right - left;
// First pass: move middle points.
for (int x=1; x<nColumns()-1; x++)
{
QPointF p = getVertex( _vertices2d[x][y] ).toQPointF();
p -= diff * x * leftMoveProp;
setVertex( _vertices2d[x][y], p );
}
// Create and add new point.
QPointF newPoint = right - diff * 1.0f/nColumns();
vertices.push_back(newPoint);
// Assign new vertices 2d.
for (int x=0; x<nColumns()-1; x++)
newVertices2d[x][y] = _vertices2d[x][y];
// The new point.
newVertices2d[nColumns()-1][y] = k;
// The rightmost point.
newVertices2d[nColumns()][y] = _vertices2d[nColumns()-1][y];
k++;
}
// Copy new mapping.
_vertices2d = newVertices2d;
// Increment number of columns.
_nColumns++;
}
void addRow()
{
// Create new vertices 2d (temporary).
std::vector< std::vector<int> > newVertices2d;
resizeVertices2d(newVertices2d, nColumns(), nRows()+1);
// Top displacement of points already there.
float topMoveProp = 1.0f/(nRows()-1) - 1.0f/nRows();
// Add a point at each row.
int k = nVertices();
for (int x=0; x<nColumns(); x++)
{
// Get left and right vertices.
QPointF top = getVertex( _vertices2d[x][0] ).toQPointF();
QPointF bottom = getVertex( _vertices2d[x][nRows()-1] ).toQPointF();
QPointF diff = bottom - top;
// First pass: move middle points.
for (int y=1; y<nRows()-1; y++)
{
QPointF p = getVertex( _vertices2d[x][y] ).toQPointF();
p -= diff * y * topMoveProp;
setVertex( _vertices2d[x][y], p );
}
// Create and add new point.
QPointF newPoint = bottom - diff * 1.0f/nRows();
vertices.push_back(newPoint);
// Assign new vertices 2d.
for (int y=0; y<nRows()-1; y++)
newVertices2d[x][y] = _vertices2d[x][y];
// The new point.
newVertices2d[x][nRows()-1] = k;
// The rightmost point.
newVertices2d[x][nRows()] = _vertices2d[x][nRows()-1];
k++;
}
// Copy new mapping.
_vertices2d = newVertices2d;
// Increment number of columns.
_nRows++;
}
// void removeColumn(int columnId)
// {
// Q_ASSERT(columnId >= 1 && columnId < nColumns());
//
// std::vector< std::vector<int> > newVertices2d;
// resizeVertices2d(newVertices2d, nHorizontalVertices()-1, nVerticalVertices());
// for (int y=0; y<nVerticalVertices(); y++)
// {
//
// }
//
// }
std::vector<Quad> getQuads() const
{
std::vector<Quad> quads;
for (int i=0; i<nHorizontalQuads(); i++)
{
for (int j=0; j<nVerticalQuads(); j++)
{
Quad quad(
vertices[ _vertices2d[i] [j] ],
vertices[ _vertices2d[i+1][j] ],
vertices[ _vertices2d[i+1][j+1] ],
vertices[ _vertices2d[i] [j+1] ]
);
quads.push_back(quad);
}
}
return quads;
}
std::vector< std::vector<Quad> > getQuads2d() const
{
std::vector< std::vector<Quad> > quads2d;
for (int i=0; i<nHorizontalQuads(); i++)
{
std::vector<Quad> column;
for (int j=0; j<nVerticalQuads(); j++)
{
Quad quad(
vertices[ _vertices2d[i] [j] ],
vertices[ _vertices2d[i+1][j] ],
vertices[ _vertices2d[i+1][j+1] ],
vertices[ _vertices2d[i] [j+1] ]
);
column.push_back(quad);
}
quads2d.push_back(column);
}
return quads2d;
}
int nColumns() const { return _nColumns; }
int nRows() const { return _nRows; }
int nHorizontalQuads() const { return _nColumns-1; }
int nVerticalQuads() const { return _nRows-1; }
protected:
int _nColumns;
int _nRows;
// _vertices[i][j] contains vertex id of vertex at position (i,j) where i = 0..nColumns and j = 0..nRows
std::vector< std::vector<int> > _vertices2d;
// Maps a vertex id to the pair of vertex ids it "splits".
std::map<int, std::pair<int, int> > _splitVertices;
};
#endif /* SHAPE_H_ */