mirror of
https://github.com/mapmapteam/mapmap.git
synced 2026-06-16 12:33:19 +02:00
Split the shapes in different files and made them all QObjects with QProperties.
This commit is contained in:
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Ellipse.cpp
|
||||
*
|
||||
* (c) 2016 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/>.
|
||||
*/
|
||||
|
||||
#include "Ellipse.h"
|
||||
|
||||
void Ellipse::sanitize()
|
||||
{
|
||||
// Get horizontal axis rotated 90 degrees CW
|
||||
QVector2D hAxis = getHorizontalAxis();
|
||||
const QVector2D center(getCenter());
|
||||
QVector2D hAxisRotated(hAxis.y(), -hAxis.x());
|
||||
|
||||
// Project vertex 1 onto it.
|
||||
QVector2D vAxisNormalized = hAxisRotated.normalized();
|
||||
|
||||
QVector2D vFromCenter = QVector2D(getVertex(1)) - center;
|
||||
const QVector2D& projection = QVector2D::dotProduct( vFromCenter, vAxisNormalized ) * vAxisNormalized;
|
||||
MShape::setVertex(1, (center + projection).toPointF());
|
||||
MShape::setVertex(3, (center - projection).toPointF());
|
||||
|
||||
if (hasCenterControl())
|
||||
{
|
||||
// Clip control point.
|
||||
MShape::setVertex(4, clipInside(getVertex(4)));
|
||||
}
|
||||
}
|
||||
|
||||
QPointF Ellipse::clipInside(const QPointF& v) const
|
||||
{
|
||||
// Map point as vector on a unit circle.
|
||||
QVector2D vector(toUnitCircle().map(v));
|
||||
|
||||
// Clip control point.
|
||||
return (vector.length() <= 1 ?
|
||||
v :
|
||||
fromUnitCircle().map(vector.normalized().toPointF()));
|
||||
|
||||
}
|
||||
|
||||
QTransform Ellipse::toUnitCircle() const
|
||||
{
|
||||
const QPointF& center = getCenter();
|
||||
return QTransform().scale(1.0/getHorizontalRadius(), 1.0/getVerticalRadius())
|
||||
.rotateRadians(-getRotationRadians())
|
||||
.translate(-center.x(), -center.y());
|
||||
}
|
||||
|
||||
QTransform Ellipse::fromUnitCircle() const
|
||||
{
|
||||
return toUnitCircle().inverted();
|
||||
}
|
||||
|
||||
bool Ellipse::includesPoint(qreal x, qreal y)
|
||||
{
|
||||
return (QVector2D(toUnitCircle().map(QPointF(x, y))).length() <= 1);
|
||||
}
|
||||
|
||||
void Ellipse::setVertex(int i, const QPointF& v)
|
||||
{
|
||||
// Save vertical axis vector.
|
||||
const QVector2D& vAxis = getVerticalAxis();
|
||||
|
||||
// If changed one of the two rotation-controlling points, adjust the other two points.
|
||||
if (i == 0 || i == 2)
|
||||
{
|
||||
// Transformation ellipse_t --> circle.
|
||||
QTransform transform = toUnitCircle();
|
||||
|
||||
// Change the vertex.
|
||||
_rawSetVertex(i, v);
|
||||
|
||||
// Combine with transformation circle -> ellipse_{t+1}.
|
||||
transform *= fromUnitCircle();
|
||||
|
||||
// Set vertices.
|
||||
MShape::setVertex(1, transform.map( getVertex(1) ));
|
||||
MShape::setVertex(3, transform.map( getVertex(3) ));
|
||||
if (hasCenterControl())
|
||||
MShape::setVertex(4, transform.map( getVertex(4) ));
|
||||
}
|
||||
|
||||
// If changed one of the two other points, just change the vertical axis.
|
||||
else if (i == 1 || i == 3)
|
||||
{
|
||||
// Retrieve the new horizontal axis vector and center.
|
||||
const QVector2D center(getCenter());
|
||||
|
||||
QVector2D vFromCenter = QVector2D(v) - center;
|
||||
|
||||
// Find projection of v onto vAxis / 2.
|
||||
QVector2D vAxisNormalized = vAxis.normalized();
|
||||
const QVector2D& projection = QVector2D::dotProduct( vFromCenter, vAxisNormalized ) * vAxisNormalized;
|
||||
|
||||
// Assign vertical control points.
|
||||
QPointF v1;
|
||||
QPointF v3;
|
||||
if (i == 1)
|
||||
{
|
||||
v1 = (center + projection).toPointF();
|
||||
v3 = (center - projection).toPointF();
|
||||
}
|
||||
else
|
||||
{
|
||||
v1 = (center - projection).toPointF();
|
||||
v3 = (center + projection).toPointF();
|
||||
}
|
||||
|
||||
// Transformation ellipse_t --> circle.
|
||||
QTransform transform = toUnitCircle();
|
||||
|
||||
// Change vertical points.
|
||||
_rawSetVertex(1, v1);
|
||||
_rawSetVertex(3, v3);
|
||||
|
||||
// Combine with transformation circle -> ellipse_{t+1}.
|
||||
transform *= fromUnitCircle();
|
||||
|
||||
// Set vertices.
|
||||
if (hasCenterControl())
|
||||
_rawSetVertex(4, transform.map( getVertex(4) ));
|
||||
}
|
||||
|
||||
// Center control point (make sure it stays inside!).
|
||||
else if (hasCenterControl())
|
||||
{
|
||||
// Clip control point.
|
||||
_rawSetVertex(4, clipInside(v));
|
||||
}
|
||||
|
||||
// Just to be sure.
|
||||
sanitize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Ellipse.h
|
||||
*
|
||||
* (c) 2016 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 ELLIPSE_H_
|
||||
#define ELLIPSE_H_
|
||||
|
||||
#include "Shape.h"
|
||||
|
||||
|
||||
class Ellipse : public MShape
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Ellipse() {}
|
||||
Ellipse(QPointF p1, QPointF p2, QPointF p3, QPointF p4, QPointF p5)
|
||||
{
|
||||
_addVertex(p1);
|
||||
_addVertex(p2);
|
||||
_addVertex(p3);
|
||||
_addVertex(p4);
|
||||
_addVertex(p5);
|
||||
sanitize();
|
||||
}
|
||||
|
||||
Ellipse(QPointF p1, QPointF p2, QPointF p3, QPointF p4, bool hasCenterControl=true)
|
||||
{
|
||||
_addVertex(p1);
|
||||
_addVertex(p2);
|
||||
_addVertex(p3);
|
||||
_addVertex(p4);
|
||||
if (hasCenterControl)
|
||||
_addVertex(getCenter());
|
||||
sanitize();
|
||||
}
|
||||
|
||||
virtual ~Ellipse() {}
|
||||
|
||||
/// Remaps points so as to make sure this is a correct ellipse, keeping vertices 0 and 2 as
|
||||
/// reference for the horizzontal axis.
|
||||
void sanitize();
|
||||
|
||||
virtual QString getType() const { return "ellipse"; }
|
||||
|
||||
qreal getRotationRadians() const
|
||||
{
|
||||
QVector2D hAxis = getHorizontalAxis();
|
||||
return atan2( hAxis.y(), hAxis.x() );
|
||||
}
|
||||
|
||||
qreal getRotation() const
|
||||
{
|
||||
return radiansToDegrees( getRotationRadians() );
|
||||
}
|
||||
|
||||
bool hasCenterControl() const
|
||||
{
|
||||
return (nVertices() == 5);
|
||||
}
|
||||
|
||||
/// If v is outside boundaries, remap it to the border.
|
||||
QPointF clipInside(const QPointF& v) const;
|
||||
|
||||
// QRect getBoundingRect() const {
|
||||
// return QRect(0, getVerticalAxis().manhattanLength(),
|
||||
// getHorizontalAxis().manhattanLength(), getVerticalAxis().manhattanLength());
|
||||
// }
|
||||
//
|
||||
QPointF getCenter() const
|
||||
{
|
||||
return (QVector2D(getVertex(0)) - (getHorizontalAxis() / 2)).toPointF();
|
||||
}
|
||||
|
||||
QVector2D getHorizontalAxis() const
|
||||
{
|
||||
return QVector2D(getVertex(0)) - QVector2D(getVertex(2));
|
||||
}
|
||||
|
||||
QVector2D getVerticalAxis() const
|
||||
{
|
||||
return QVector2D(getVertex(1)) - QVector2D(getVertex(3));
|
||||
}
|
||||
|
||||
qreal getHorizontalRadius() const
|
||||
{
|
||||
return getHorizontalAxis().length() / 2;
|
||||
}
|
||||
|
||||
qreal getVerticalRadius() const
|
||||
{
|
||||
return getVerticalAxis().length() / 2;
|
||||
}
|
||||
|
||||
/// Remaps point from ellipse to a circle with radius 1 set at origin (0,0).
|
||||
QTransform toUnitCircle() const;
|
||||
|
||||
/// Remaps point from circle with radius 1 set at origin (0,0) to ellipse coordinates.
|
||||
QTransform fromUnitCircle() const;
|
||||
|
||||
/** 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
|
||||
*/
|
||||
virtual bool includesPoint(qreal x, qreal y);
|
||||
|
||||
virtual bool includesPoint(const QPointF& p)
|
||||
{
|
||||
return includesPoint(p.x(), p.y());
|
||||
}
|
||||
|
||||
// Override the parent, checking to make sure the vertices are displaced correctly.
|
||||
virtual void setVertex(int i, const QPointF& v);
|
||||
|
||||
protected:
|
||||
/// Returns a new MShape (using default constructor).
|
||||
virtual MShape* _create() const { return new Ellipse(); }
|
||||
|
||||
//protected:
|
||||
// virtual void _vertexChanged(int i, Point* p=NULL) {
|
||||
// // Get horizontal and vertical axis length.
|
||||
// qreal hAxisLength = Point::dist(getVertex(0)->toPoint(), getVertex(2)->toPoint());
|
||||
// qreal vAxisLength = Point::dist(getVertex(1)->toPoint(), getVertex(3)->toPoint());
|
||||
// }
|
||||
};
|
||||
|
||||
#endif /* ELLIPSE_H_ */
|
||||
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
* Mesh.cpp
|
||||
*
|
||||
* (c) 2016 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/>.
|
||||
*/
|
||||
|
||||
#include "Mesh.h"
|
||||
|
||||
Mesh::Mesh() : Quad(), _nColumns(0), _nRows(0) {}
|
||||
|
||||
Mesh::Mesh(QPointF p1, QPointF p2, QPointF p3, QPointF p4) : Quad()
|
||||
{
|
||||
// Add points in standard order.
|
||||
QVector<QPointF> points;
|
||||
points.push_back(p1);
|
||||
points.push_back(p2);
|
||||
points.push_back(p4);
|
||||
points.push_back(p3);
|
||||
|
||||
// Init.
|
||||
init(points, 2, 2);
|
||||
}
|
||||
|
||||
Mesh::Mesh(const QVector<QPointF>& points, int nColumns, int nRows) : Quad()
|
||||
{
|
||||
init(points, nColumns, nRows);
|
||||
}
|
||||
|
||||
void Mesh::init(const QVector<QPointF>& points, int nColumns, int nRows)
|
||||
{
|
||||
Q_ASSERT(nColumns >= 2 && nRows >= 2);
|
||||
Q_ASSERT(points.size() == nColumns * nRows);
|
||||
|
||||
_nColumns = nColumns;
|
||||
_nRows = nRows;
|
||||
|
||||
// Resize the vertices2d vector to appropriate dimensions.
|
||||
resizeVertices2d(_vertices2d, _nColumns, _nRows);
|
||||
|
||||
// Just build vertices2d in the standard order.
|
||||
int k = 0;
|
||||
for (int y=0; y<_nRows; y++)
|
||||
for (int x=0; x<_nColumns; x++)
|
||||
{
|
||||
vertices.push_back( points[k] );
|
||||
_vertices2d[x][y] = k;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
QPolygonF Mesh::toPolygon() const
|
||||
{
|
||||
QPolygonF polygon;
|
||||
for (int i=0; i<nColumns(); i++)
|
||||
polygon.append(getVertex2d(i, 0));
|
||||
for (int i=0; i<nRows(); i++)
|
||||
polygon.append(getVertex2d(nColumns()-1, i));
|
||||
for (int i=nColumns()-1; i>=0; i--)
|
||||
polygon.append(getVertex2d(i, nRows()-1));
|
||||
for (int i=nRows()-1; i>=1; i--)
|
||||
polygon.append(getVertex2d(0, i));
|
||||
return polygon;
|
||||
}
|
||||
|
||||
void Mesh::setVertex(int i, const QPointF& v)
|
||||
{
|
||||
// Extract column and row of vertex.
|
||||
int col = i % nColumns();
|
||||
int row = i / nColumns();
|
||||
|
||||
// Make a copy.
|
||||
QPointF realV = v;
|
||||
|
||||
// Constrain vertex to stay within the internal quads it is part of.
|
||||
if (col < nColumns()-1)
|
||||
{
|
||||
if (row < nRows() - 1)
|
||||
{
|
||||
Quad quad(getVertex2d(col, row), getVertex2d(col+1, row), getVertex2d(col+1, row+1), getVertex2d(col, row+1));
|
||||
_constrainVertex(quad.toPolygon(), 0, realV);
|
||||
}
|
||||
if (row > 0)
|
||||
{
|
||||
Quad quad(getVertex2d(col, row), getVertex2d(col+1, row), getVertex2d(col+1, row-1), getVertex2d(col, row-1));
|
||||
_constrainVertex(quad.toPolygon(), 0, realV);
|
||||
}
|
||||
}
|
||||
if (col > 0)
|
||||
{
|
||||
if (row < nRows() - 1)
|
||||
{
|
||||
Quad quad(getVertex2d(col, row), getVertex2d(col-1, row), getVertex2d(col-1, row+1), getVertex2d(col, row+1));
|
||||
_constrainVertex(quad.toPolygon(), 0, realV);
|
||||
}
|
||||
if (row > 0)
|
||||
{
|
||||
Quad quad(getVertex2d(col, row), getVertex2d(col-1, row), getVertex2d(col-1, row-1), getVertex2d(col, row-1));
|
||||
_constrainVertex(quad.toPolygon(), 0, realV);
|
||||
}
|
||||
}
|
||||
|
||||
// Do set vertex.
|
||||
_rawSetVertex(i, realV);
|
||||
}
|
||||
|
||||
void Mesh::resizeVertices2d(IndexVector2d& vertices2d, int nColumns, int nRows)
|
||||
{
|
||||
vertices2d.resize(nColumns);
|
||||
for (int i=0; i<nColumns; i++)
|
||||
vertices2d[i].resize(nRows);
|
||||
}
|
||||
|
||||
//void Mesh::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();
|
||||
//}
|
||||
|
||||
// vertices 0..3 = 4 corners
|
||||
//
|
||||
void Mesh::addColumn()
|
||||
{
|
||||
// Create new vertices 2d (temporary).
|
||||
IndexVector2d newVertices2d;
|
||||
resizeVertices2d(newVertices2d, nColumns()+1, nRows());
|
||||
|
||||
// Left displacement of points already there.
|
||||
qreal 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 = getVertex2d( 0, y );
|
||||
QPointF right = getVertex2d( nColumns()-1, y );
|
||||
QPointF diff = right - left;
|
||||
|
||||
// First pass: move middle points.
|
||||
for (int x=1; x<nColumns()-1; x++)
|
||||
{
|
||||
QPointF p = getVertex2d(x, y);
|
||||
p -= diff * x * leftMoveProp;
|
||||
_rawSetVertex( _vertices2d[x][y], p );
|
||||
}
|
||||
|
||||
// Create and add new point.
|
||||
QPointF newPoint = right - diff * 1.0f/nColumns();
|
||||
_addVertex(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++;
|
||||
|
||||
// Reorder.
|
||||
_reorderVertices();
|
||||
}
|
||||
|
||||
void Mesh::addRow()
|
||||
{
|
||||
// Create new vertices 2d (temporary).
|
||||
IndexVector2d newVertices2d;
|
||||
resizeVertices2d(newVertices2d, nColumns(), nRows()+1);
|
||||
|
||||
// Top displacement of points already there.
|
||||
qreal 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 = getVertex2d(x, 0);
|
||||
QPointF bottom = getVertex2d(x, nRows()-1);
|
||||
QPointF diff = bottom - top;
|
||||
|
||||
// First pass: move middle points.
|
||||
for (int y=1; y<nRows()-1; y++)
|
||||
{
|
||||
QPointF p = getVertex2d(x, y);
|
||||
p -= diff * y * topMoveProp;
|
||||
_rawSetVertex( _vertices2d[x][y], p );
|
||||
}
|
||||
|
||||
// Create and add new point.
|
||||
QPointF newPoint = bottom - diff * 1.0f/nRows();
|
||||
_addVertex(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++;
|
||||
|
||||
// Reorder.
|
||||
_reorderVertices();
|
||||
}
|
||||
|
||||
void Mesh::removeColumn(int columnId)
|
||||
{
|
||||
// Cannot remove first and last columns
|
||||
Q_ASSERT(columnId >= 1 && columnId < nColumns()-1);
|
||||
|
||||
// Temporary containers that will be used to rebuild new vertex space.
|
||||
IndexVector2d newVertices2d;
|
||||
resizeVertices2d(newVertices2d, nColumns()-1, nRows());
|
||||
|
||||
QVector<QPointF> newVertices(vertices.size()-nRows());
|
||||
|
||||
// Right displacement of points already there.
|
||||
qreal rightMoveProp = 1.0f/(nColumns()-2) - 1.0f/(nColumns()-1);
|
||||
|
||||
// Process all rows.
|
||||
int k = 0;
|
||||
for (int y=0; y<nRows(); y++)
|
||||
{
|
||||
// Get left and right vertices.
|
||||
QPointF left = getVertex2d( 0, y );
|
||||
QPointF right = getVertex2d( nColumns()-1, y );
|
||||
QPointF diff = right - left;
|
||||
|
||||
// Move all columns.
|
||||
for (int x=0; x<nColumns(); x++)
|
||||
{
|
||||
// Ignore points from target column.
|
||||
if (x == columnId)
|
||||
continue;
|
||||
|
||||
// Get current vertex.
|
||||
QPointF p = getVertex2d( x, y );
|
||||
|
||||
// The x value of this point in the new space.
|
||||
int newX = x < columnId ? x : x-1;
|
||||
|
||||
// Move middle points.
|
||||
if (x > 0 && x < nColumns()-1)
|
||||
{
|
||||
p += (x < columnId ? +1 : -1) * diff * newX * rightMoveProp;
|
||||
}
|
||||
|
||||
// Assign new containers.
|
||||
newVertices[k] = p;
|
||||
newVertices2d[newX][y] = k;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy new mapping.
|
||||
vertices = newVertices;
|
||||
_vertices2d = newVertices2d;
|
||||
|
||||
// Decrement number of columns.
|
||||
_nColumns--;
|
||||
|
||||
// Reorder.
|
||||
_reorderVertices();
|
||||
}
|
||||
|
||||
void Mesh::removeRow(int rowId)
|
||||
{
|
||||
// Cannot remove first and last columns
|
||||
Q_ASSERT(rowId >= 1 && rowId < nRows()-1);
|
||||
|
||||
// Temporary containers that will be used to rebuild new vertex space.
|
||||
IndexVector2d newVertices2d;
|
||||
resizeVertices2d(newVertices2d, nColumns(), nRows()-1);
|
||||
|
||||
QVector<QPointF> newVertices(vertices.size()-nColumns());
|
||||
|
||||
// Bottom displacement of points already there.
|
||||
qreal bottomMoveProp = 1.0f/(nRows()-2) - 1.0f/(nRows()-1);
|
||||
|
||||
// Process all columns.
|
||||
int k = 0;
|
||||
for (int x=0; x<nColumns(); x++)
|
||||
{
|
||||
// Get top and bottom vertices.
|
||||
QPointF top = getVertex2d(x, 0);
|
||||
QPointF bottom = getVertex2d(x, nRows()-1);
|
||||
QPointF diff = bottom - top;
|
||||
|
||||
// Move all rows.
|
||||
for (int y=0; y<nRows(); y++)
|
||||
{
|
||||
// Ignore points from target row.
|
||||
if (y == rowId)
|
||||
continue;
|
||||
|
||||
// Get current vertex.
|
||||
QPointF p = getVertex2d( x, y );
|
||||
|
||||
// The y value of this point in the new space.
|
||||
int newY = y < rowId ? y : y-1;
|
||||
|
||||
// Move middle points.
|
||||
if (y > 0 && y < nRows()-1)
|
||||
{
|
||||
p += (y < rowId ? +1 : -1) * diff * newY * bottomMoveProp;
|
||||
}
|
||||
|
||||
// Assign new containers.
|
||||
newVertices[k] = p;
|
||||
newVertices2d[x][newY] = k;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy new mapping.
|
||||
vertices = newVertices;
|
||||
_vertices2d = newVertices2d;
|
||||
|
||||
// Decrement number of rows.
|
||||
_nRows--;
|
||||
|
||||
// Reorder.
|
||||
_reorderVertices();
|
||||
}
|
||||
|
||||
|
||||
void Mesh::resize(int nColumns_, int nRows_)
|
||||
{
|
||||
// Brutal: if asked to reduce columns or rows, just delete and redo.
|
||||
if (nColumns_ < nColumns())
|
||||
{
|
||||
while (nColumns_ != nColumns())
|
||||
removeColumn(nColumns()-2);
|
||||
}
|
||||
if (nRows_ < nRows())
|
||||
{
|
||||
while (nRows_ != nRows())
|
||||
removeRow(nRows()-2);
|
||||
}
|
||||
if (nColumns_ > nColumns())
|
||||
{
|
||||
while (nColumns_ != nColumns())
|
||||
addColumn();
|
||||
}
|
||||
if (nRows_ > nRows())
|
||||
{
|
||||
while (nRows_ != nRows())
|
||||
addRow();
|
||||
}
|
||||
}
|
||||
|
||||
QVector<Quad::ptr> Mesh::getQuads() const
|
||||
{
|
||||
QVector<Quad::ptr> quads;
|
||||
for (int i=0; i<nHorizontalQuads(); i++)
|
||||
{
|
||||
for (int j=0; j<nVerticalQuads(); j++)
|
||||
{
|
||||
quads.push_back(Quad::ptr(
|
||||
new Quad(
|
||||
getVertex2d(i, j ),
|
||||
getVertex2d(i+1, j ),
|
||||
getVertex2d(i+1, j+1),
|
||||
getVertex2d(i, j+1)
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
QVector< QVector<Quad::ptr> > Mesh::getQuads2d() const
|
||||
{
|
||||
QVector< QVector<Quad::ptr> > quads2d;
|
||||
for (int i=0; i<nHorizontalQuads(); i++)
|
||||
{
|
||||
QVector<Quad::ptr> column;
|
||||
for (int j=0; j<nVerticalQuads(); j++)
|
||||
{
|
||||
column.push_back(Quad::ptr(
|
||||
new Quad(
|
||||
getVertex2d(i, j ),
|
||||
getVertex2d(i+1, j ),
|
||||
getVertex2d(i+1, j+1),
|
||||
getVertex2d(i, j+1)
|
||||
)
|
||||
));
|
||||
}
|
||||
quads2d.push_back(column);
|
||||
}
|
||||
return quads2d;
|
||||
}
|
||||
|
||||
void Mesh::_reorderVertices()
|
||||
{
|
||||
// Populate new vertices vector.
|
||||
QVector<QPointF> newVertices(vertices.size());
|
||||
int k = 0;
|
||||
for (int y=0; y<nRows(); y++)
|
||||
for (int x=0; x<nColumns(); x++)
|
||||
newVertices[k++] = getVertex2d( x, y );
|
||||
|
||||
// Populate _vertices2d.
|
||||
k = 0;
|
||||
for (int y=0; y<nRows(); y++)
|
||||
for (int x=0; x<nColumns(); x++)
|
||||
_vertices2d[x][y] = k++;
|
||||
|
||||
// Copy.
|
||||
vertices = newVertices;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Mesh.h
|
||||
*
|
||||
* (c) 2016 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 MESH_H_
|
||||
#define MESH_H_
|
||||
|
||||
#include "Quad.h"
|
||||
|
||||
class Mesh : public Quad
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int nColumns READ nColumns)
|
||||
Q_PROPERTY(int nRows READ nRows)
|
||||
|
||||
typedef QVector<QVector<int> > IndexVector2d;
|
||||
|
||||
public:
|
||||
Mesh();
|
||||
|
||||
// This constructor creates a quad mesh (four corners) using the same order as for the quad
|
||||
// constructor (ie. clockwise).
|
||||
Mesh(QPointF p1, QPointF p2, QPointF p3, QPointF p4);
|
||||
|
||||
// Standard mesh constructor.
|
||||
Mesh(const QVector<QPointF>& points, int nColumns, int nRows);
|
||||
|
||||
virtual ~Mesh() {}
|
||||
|
||||
// Performs the actual adding of points (used for loading).
|
||||
void init(const QVector<QPointF>& points, int nColumns, int nRows);
|
||||
|
||||
virtual QString getType() const { return "mesh"; }
|
||||
|
||||
/// Returns a polygon that is formed by all the contour points of the mesh.
|
||||
virtual QPolygonF toPolygon() const;
|
||||
|
||||
// Override the parent, checking to make sure the vertices are displaced correctly.
|
||||
virtual void setVertex(int i, const QPointF& v);
|
||||
|
||||
QPointF getVertex2d(int i, int j) const
|
||||
{
|
||||
return vertices[_vertices2d[i][j]];
|
||||
}
|
||||
|
||||
void setVertex2d(int i, int j, const QPointF& v)
|
||||
{
|
||||
vertices[_vertices2d[i][j]] = v; // copy
|
||||
}
|
||||
|
||||
void setVertex2d(int i, int j, double x, double y)
|
||||
{
|
||||
vertices[_vertices2d[i][j]] = QPointF(x, y);
|
||||
}
|
||||
|
||||
void resizeVertices2d(IndexVector2d& vertices2d, int nColumns, int nRows);
|
||||
|
||||
//
|
||||
void addColumn();
|
||||
void addRow();
|
||||
|
||||
void removeColumn(int columnId);
|
||||
void removeRow(int rowId);
|
||||
|
||||
void resize(int nColumns_, int nRows_);
|
||||
|
||||
QVector<Quad::ptr> getQuads() const;
|
||||
QVector<QVector<Quad::ptr> > getQuads2d() const;
|
||||
|
||||
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
|
||||
IndexVector2d _vertices2d;
|
||||
|
||||
/**
|
||||
* Reorder vertices in a standard order:
|
||||
*
|
||||
* 0----1----2----3
|
||||
* | | | |
|
||||
* 4----5----6----7
|
||||
* | | | |
|
||||
* 8----9---10----11
|
||||
*/
|
||||
void _reorderVertices();
|
||||
|
||||
/// Returns a new MShape (using default constructor).
|
||||
virtual MShape* _create() const { return new Mesh(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* MESH_H_ */
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Polygon.cpp
|
||||
*
|
||||
* (c) 2016 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/>.
|
||||
*/
|
||||
|
||||
#include "Polygon.h"
|
||||
|
||||
void Polygon::setVertex(int i, const QPointF& v)
|
||||
{
|
||||
// Constrain vertex.
|
||||
QPointF realV = v;
|
||||
_constrainVertex(toPolygon(), i, realV);
|
||||
// Really set the vertex.
|
||||
_rawSetVertex(i, realV);
|
||||
}
|
||||
|
||||
void Polygon::_constrainVertex(const QPolygonF& polygon, int i, QPointF& v)
|
||||
{
|
||||
// Weird, but nothing to do.
|
||||
if (polygon.size() <= 3)
|
||||
return;
|
||||
|
||||
// Save previous position of vertex.
|
||||
QPointF prevV = polygon.at(i);
|
||||
|
||||
// Look at the two adjunct segments to vertex i and see if they
|
||||
// intersect with any non-adjacent segments.
|
||||
|
||||
// Construct the list of segments (with the new candidate vertex).
|
||||
QVector<QLineF> segments = _getSegments(polygon);
|
||||
int prev = wrapAround(i - 1, segments.size());
|
||||
int next = wrapAround(i + 1, segments.size());
|
||||
segments[prev] = QLineF(polygon.at(prev), v);
|
||||
segments[i] = QLineF(v, polygon.at(next));
|
||||
|
||||
// We now stretch segments a little bit to cope with approximation errors.
|
||||
for (QVector<QLineF>::Iterator it = segments.begin(); it != segments.end(); ++it)
|
||||
{
|
||||
QLineF& seg = *it;
|
||||
QPointF p1 = seg.p1();
|
||||
QPointF p2 = seg.p2();
|
||||
seg.setP1( p1 + (p1 - p2) * 0.35f);
|
||||
seg.setP2( p2 + (p2 - p1) * 0.35f);
|
||||
}
|
||||
|
||||
// For each adjunct segment.
|
||||
for (int adj=0; adj<2; adj++)
|
||||
{
|
||||
int idx = wrapAround(i + adj - 1, segments.size());
|
||||
for (int j=0; j<segments.size(); j++)
|
||||
{
|
||||
// If the segment to compare to is valid (ie. if it is not
|
||||
// the segment itself nor an adjacent one) then check for
|
||||
// intersection.
|
||||
if (j != idx &&
|
||||
j != wrapAround(idx-1, segments.size()) &&
|
||||
j != wrapAround(idx+1, segments.size()))
|
||||
{
|
||||
QPointF intersection;
|
||||
if (segments[idx].intersect(segments[j], &intersection) == QLineF::BoundedIntersection)
|
||||
{
|
||||
// Rearrange segments with new position at intersection point.
|
||||
v = intersection;
|
||||
segments[prev] = QLineF(polygon.at(prev), v);
|
||||
segments[i] = QLineF(v, polygon.at(next));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVector<QLineF> Polygon::_getSegments() const
|
||||
{
|
||||
return _getSegments(toPolygon());
|
||||
}
|
||||
|
||||
QVector<QLineF> Polygon::_getSegments(const QPolygonF& polygon)
|
||||
{
|
||||
QVector<QLineF> segments;
|
||||
for (int i=0; i<polygon.size(); i++)
|
||||
segments.push_back(QLineF(polygon.at(i), polygon.at( (i+1) % polygon.size() )));
|
||||
return segments;
|
||||
}
|
||||
|
||||
QPolygonF Polygon::toPolygon() const
|
||||
{
|
||||
QPolygonF polygon;
|
||||
for (QVector<QPointF>::const_iterator it = vertices.begin() ;
|
||||
it != vertices.end(); ++it)
|
||||
{
|
||||
polygon.append(*it);
|
||||
}
|
||||
return polygon;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Polygon.h
|
||||
*
|
||||
* (c) 2016 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 POLYGON_H_
|
||||
#define POLYGON_H_
|
||||
|
||||
#include "Shape.h"
|
||||
|
||||
/**
|
||||
* This class represents a simple polygon (ie. the control points are vertices).
|
||||
*/
|
||||
class Polygon : public MShape
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Polygon() {}
|
||||
Polygon(QVector<QPointF> vertices_) : MShape(vertices_) {}
|
||||
virtual ~Polygon() {}
|
||||
|
||||
virtual QPolygonF toPolygon() const;
|
||||
|
||||
virtual bool includesPoint(const QPointF& p) {
|
||||
return toPolygon().containsPoint(p, Qt::OddEvenFill);
|
||||
}
|
||||
|
||||
// Override the parent, checking to make sure the vertices are displaced correctly.
|
||||
virtual void setVertex(int i, const QPointF& v);
|
||||
|
||||
protected:
|
||||
/// Returns all line segments of the polygon.
|
||||
QVector<QLineF> _getSegments() const;
|
||||
|
||||
/// Returns all line segments of a polygon.
|
||||
static QVector<QLineF> _getSegments(const QPolygonF& polygon);
|
||||
|
||||
/// Makes sure vertex v as the i-th point of polygon stays inside the polygon.
|
||||
static void _constrainVertex(const QPolygonF& polygon, int i, QPointF& v);
|
||||
};
|
||||
|
||||
|
||||
#endif /* POLYGON_H_ */
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Quad.h
|
||||
*
|
||||
* (c) 2016 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 QUAD_H_
|
||||
#define QUAD_H_
|
||||
|
||||
#include "Polygon.h"
|
||||
|
||||
/**
|
||||
* Four-vertex shape.
|
||||
*/
|
||||
class Quad : public Polygon
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef QSharedPointer<Quad> ptr;
|
||||
|
||||
Quad() {}
|
||||
Quad(QPointF p1, QPointF p2, QPointF p3, QPointF p4)
|
||||
{
|
||||
_addVertex(p1);
|
||||
_addVertex(p2);
|
||||
_addVertex(p3);
|
||||
_addVertex(p4);
|
||||
}
|
||||
virtual ~Quad() {}
|
||||
|
||||
virtual QString getType() const { return "quad"; }
|
||||
|
||||
protected:
|
||||
/// Returns a new MShape (using default constructor).
|
||||
virtual MShape* _create() const { return new Quad(); }
|
||||
};
|
||||
|
||||
|
||||
#endif /* QUAD_H_ */
|
||||
@@ -38,655 +38,3 @@ void MShape::translate(const QPointF& offset)
|
||||
*it += offset;
|
||||
}
|
||||
|
||||
void Polygon::setVertex(int i, const QPointF& v)
|
||||
{
|
||||
// Constrain vertex.
|
||||
QPointF realV = v;
|
||||
_constrainVertex(toPolygon(), i, realV);
|
||||
// Really set the vertex.
|
||||
_rawSetVertex(i, realV);
|
||||
}
|
||||
|
||||
void Polygon::_constrainVertex(const QPolygonF& polygon, int i, QPointF& v)
|
||||
{
|
||||
// Weird, but nothing to do.
|
||||
if (polygon.size() <= 3)
|
||||
return;
|
||||
|
||||
// Save previous position of vertex.
|
||||
QPointF prevV = polygon.at(i);
|
||||
|
||||
// Look at the two adjunct segments to vertex i and see if they
|
||||
// intersect with any non-adjacent segments.
|
||||
|
||||
// Construct the list of segments (with the new candidate vertex).
|
||||
QVector<QLineF> segments = _getSegments(polygon);
|
||||
int prev = wrapAround(i - 1, segments.size());
|
||||
int next = wrapAround(i + 1, segments.size());
|
||||
segments[prev] = QLineF(polygon.at(prev), v);
|
||||
segments[i] = QLineF(v, polygon.at(next));
|
||||
|
||||
// We now stretch segments a little bit to cope with approximation errors.
|
||||
for (QVector<QLineF>::Iterator it = segments.begin(); it != segments.end(); ++it)
|
||||
{
|
||||
QLineF& seg = *it;
|
||||
QPointF p1 = seg.p1();
|
||||
QPointF p2 = seg.p2();
|
||||
seg.setP1( p1 + (p1 - p2) * 0.35f);
|
||||
seg.setP2( p2 + (p2 - p1) * 0.35f);
|
||||
}
|
||||
|
||||
// For each adjunct segment.
|
||||
for (int adj=0; adj<2; adj++)
|
||||
{
|
||||
int idx = wrapAround(i + adj - 1, segments.size());
|
||||
for (int j=0; j<segments.size(); j++)
|
||||
{
|
||||
// If the segment to compare to is valid (ie. if it is not
|
||||
// the segment itself nor an adjacent one) then check for
|
||||
// intersection.
|
||||
if (j != idx &&
|
||||
j != wrapAround(idx-1, segments.size()) &&
|
||||
j != wrapAround(idx+1, segments.size()))
|
||||
{
|
||||
QPointF intersection;
|
||||
if (segments[idx].intersect(segments[j], &intersection) == QLineF::BoundedIntersection)
|
||||
{
|
||||
// Rearrange segments with new position at intersection point.
|
||||
v = intersection;
|
||||
segments[prev] = QLineF(polygon.at(prev), v);
|
||||
segments[i] = QLineF(v, polygon.at(next));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QVector<QLineF> Polygon::_getSegments() const
|
||||
{
|
||||
return _getSegments(toPolygon());
|
||||
}
|
||||
|
||||
QVector<QLineF> Polygon::_getSegments(const QPolygonF& polygon)
|
||||
{
|
||||
QVector<QLineF> segments;
|
||||
for (int i=0; i<polygon.size(); i++)
|
||||
segments.push_back(QLineF(polygon.at(i), polygon.at( (i+1) % polygon.size() )));
|
||||
return segments;
|
||||
}
|
||||
|
||||
QPolygonF Polygon::toPolygon() const
|
||||
{
|
||||
QPolygonF polygon;
|
||||
for (QVector<QPointF>::const_iterator it = vertices.begin() ;
|
||||
it != vertices.end(); ++it)
|
||||
{
|
||||
polygon.append(*it);
|
||||
}
|
||||
return polygon;
|
||||
}
|
||||
|
||||
Mesh::Mesh() : Quad(), _nColumns(0), _nRows(0) {}
|
||||
|
||||
Mesh::Mesh(QPointF p1, QPointF p2, QPointF p3, QPointF p4) : Quad()
|
||||
{
|
||||
// Add points in standard order.
|
||||
QVector<QPointF> points;
|
||||
points.push_back(p1);
|
||||
points.push_back(p2);
|
||||
points.push_back(p4);
|
||||
points.push_back(p3);
|
||||
|
||||
// Init.
|
||||
init(points, 2, 2);
|
||||
}
|
||||
|
||||
Mesh::Mesh(const QVector<QPointF>& points, int nColumns, int nRows) : Quad()
|
||||
{
|
||||
init(points, nColumns, nRows);
|
||||
}
|
||||
|
||||
void Mesh::init(const QVector<QPointF>& points, int nColumns, int nRows)
|
||||
{
|
||||
Q_ASSERT(nColumns >= 2 && nRows >= 2);
|
||||
Q_ASSERT(points.size() == nColumns * nRows);
|
||||
|
||||
_nColumns = nColumns;
|
||||
_nRows = nRows;
|
||||
|
||||
// Resize the vertices2d vector to appropriate dimensions.
|
||||
resizeVertices2d(_vertices2d, _nColumns, _nRows);
|
||||
|
||||
// Just build vertices2d in the standard order.
|
||||
int k = 0;
|
||||
for (int y=0; y<_nRows; y++)
|
||||
for (int x=0; x<_nColumns; x++)
|
||||
{
|
||||
vertices.push_back( points[k] );
|
||||
_vertices2d[x][y] = k;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
QPolygonF Mesh::toPolygon() const
|
||||
{
|
||||
QPolygonF polygon;
|
||||
for (int i=0; i<nColumns(); i++)
|
||||
polygon.append(getVertex2d(i, 0));
|
||||
for (int i=0; i<nRows(); i++)
|
||||
polygon.append(getVertex2d(nColumns()-1, i));
|
||||
for (int i=nColumns()-1; i>=0; i--)
|
||||
polygon.append(getVertex2d(i, nRows()-1));
|
||||
for (int i=nRows()-1; i>=1; i--)
|
||||
polygon.append(getVertex2d(0, i));
|
||||
return polygon;
|
||||
}
|
||||
|
||||
void Mesh::setVertex(int i, const QPointF& v)
|
||||
{
|
||||
// Extract column and row of vertex.
|
||||
int col = i % nColumns();
|
||||
int row = i / nColumns();
|
||||
|
||||
// Make a copy.
|
||||
QPointF realV = v;
|
||||
|
||||
// Constrain vertex to stay within the internal quads it is part of.
|
||||
if (col < nColumns()-1)
|
||||
{
|
||||
if (row < nRows() - 1)
|
||||
{
|
||||
Quad quad(getVertex2d(col, row), getVertex2d(col+1, row), getVertex2d(col+1, row+1), getVertex2d(col, row+1));
|
||||
_constrainVertex(quad.toPolygon(), 0, realV);
|
||||
}
|
||||
if (row > 0)
|
||||
{
|
||||
Quad quad(getVertex2d(col, row), getVertex2d(col+1, row), getVertex2d(col+1, row-1), getVertex2d(col, row-1));
|
||||
_constrainVertex(quad.toPolygon(), 0, realV);
|
||||
}
|
||||
}
|
||||
if (col > 0)
|
||||
{
|
||||
if (row < nRows() - 1)
|
||||
{
|
||||
Quad quad(getVertex2d(col, row), getVertex2d(col-1, row), getVertex2d(col-1, row+1), getVertex2d(col, row+1));
|
||||
_constrainVertex(quad.toPolygon(), 0, realV);
|
||||
}
|
||||
if (row > 0)
|
||||
{
|
||||
Quad quad(getVertex2d(col, row), getVertex2d(col-1, row), getVertex2d(col-1, row-1), getVertex2d(col, row-1));
|
||||
_constrainVertex(quad.toPolygon(), 0, realV);
|
||||
}
|
||||
}
|
||||
|
||||
// Do set vertex.
|
||||
_rawSetVertex(i, realV);
|
||||
}
|
||||
|
||||
void Mesh::resizeVertices2d(IndexVector2d& vertices2d, int nColumns, int nRows)
|
||||
{
|
||||
vertices2d.resize(nColumns);
|
||||
for (int i=0; i<nColumns; i++)
|
||||
vertices2d[i].resize(nRows);
|
||||
}
|
||||
|
||||
//void Mesh::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();
|
||||
//}
|
||||
|
||||
// vertices 0..3 = 4 corners
|
||||
//
|
||||
void Mesh::addColumn()
|
||||
{
|
||||
// Create new vertices 2d (temporary).
|
||||
IndexVector2d newVertices2d;
|
||||
resizeVertices2d(newVertices2d, nColumns()+1, nRows());
|
||||
|
||||
// Left displacement of points already there.
|
||||
qreal 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 = getVertex2d( 0, y );
|
||||
QPointF right = getVertex2d( nColumns()-1, y );
|
||||
QPointF diff = right - left;
|
||||
|
||||
// First pass: move middle points.
|
||||
for (int x=1; x<nColumns()-1; x++)
|
||||
{
|
||||
QPointF p = getVertex2d(x, y);
|
||||
p -= diff * x * leftMoveProp;
|
||||
_rawSetVertex( _vertices2d[x][y], p );
|
||||
}
|
||||
|
||||
// Create and add new point.
|
||||
QPointF newPoint = right - diff * 1.0f/nColumns();
|
||||
_addVertex(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++;
|
||||
|
||||
// Reorder.
|
||||
_reorderVertices();
|
||||
}
|
||||
|
||||
void Mesh::addRow()
|
||||
{
|
||||
// Create new vertices 2d (temporary).
|
||||
IndexVector2d newVertices2d;
|
||||
resizeVertices2d(newVertices2d, nColumns(), nRows()+1);
|
||||
|
||||
// Top displacement of points already there.
|
||||
qreal 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 = getVertex2d(x, 0);
|
||||
QPointF bottom = getVertex2d(x, nRows()-1);
|
||||
QPointF diff = bottom - top;
|
||||
|
||||
// First pass: move middle points.
|
||||
for (int y=1; y<nRows()-1; y++)
|
||||
{
|
||||
QPointF p = getVertex2d(x, y);
|
||||
p -= diff * y * topMoveProp;
|
||||
_rawSetVertex( _vertices2d[x][y], p );
|
||||
}
|
||||
|
||||
// Create and add new point.
|
||||
QPointF newPoint = bottom - diff * 1.0f/nRows();
|
||||
_addVertex(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++;
|
||||
|
||||
// Reorder.
|
||||
_reorderVertices();
|
||||
}
|
||||
|
||||
void Mesh::removeColumn(int columnId)
|
||||
{
|
||||
// Cannot remove first and last columns
|
||||
Q_ASSERT(columnId >= 1 && columnId < nColumns()-1);
|
||||
|
||||
// Temporary containers that will be used to rebuild new vertex space.
|
||||
IndexVector2d newVertices2d;
|
||||
resizeVertices2d(newVertices2d, nColumns()-1, nRows());
|
||||
|
||||
QVector<QPointF> newVertices(vertices.size()-nRows());
|
||||
|
||||
// Right displacement of points already there.
|
||||
qreal rightMoveProp = 1.0f/(nColumns()-2) - 1.0f/(nColumns()-1);
|
||||
|
||||
// Process all rows.
|
||||
int k = 0;
|
||||
for (int y=0; y<nRows(); y++)
|
||||
{
|
||||
// Get left and right vertices.
|
||||
QPointF left = getVertex2d( 0, y );
|
||||
QPointF right = getVertex2d( nColumns()-1, y );
|
||||
QPointF diff = right - left;
|
||||
|
||||
// Move all columns.
|
||||
for (int x=0; x<nColumns(); x++)
|
||||
{
|
||||
// Ignore points from target column.
|
||||
if (x == columnId)
|
||||
continue;
|
||||
|
||||
// Get current vertex.
|
||||
QPointF p = getVertex2d( x, y );
|
||||
|
||||
// The x value of this point in the new space.
|
||||
int newX = x < columnId ? x : x-1;
|
||||
|
||||
// Move middle points.
|
||||
if (x > 0 && x < nColumns()-1)
|
||||
{
|
||||
p += (x < columnId ? +1 : -1) * diff * newX * rightMoveProp;
|
||||
}
|
||||
|
||||
// Assign new containers.
|
||||
newVertices[k] = p;
|
||||
newVertices2d[newX][y] = k;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy new mapping.
|
||||
vertices = newVertices;
|
||||
_vertices2d = newVertices2d;
|
||||
|
||||
// Decrement number of columns.
|
||||
_nColumns--;
|
||||
|
||||
// Reorder.
|
||||
_reorderVertices();
|
||||
}
|
||||
|
||||
void Mesh::removeRow(int rowId)
|
||||
{
|
||||
// Cannot remove first and last columns
|
||||
Q_ASSERT(rowId >= 1 && rowId < nRows()-1);
|
||||
|
||||
// Temporary containers that will be used to rebuild new vertex space.
|
||||
IndexVector2d newVertices2d;
|
||||
resizeVertices2d(newVertices2d, nColumns(), nRows()-1);
|
||||
|
||||
QVector<QPointF> newVertices(vertices.size()-nColumns());
|
||||
|
||||
// Bottom displacement of points already there.
|
||||
qreal bottomMoveProp = 1.0f/(nRows()-2) - 1.0f/(nRows()-1);
|
||||
|
||||
// Process all columns.
|
||||
int k = 0;
|
||||
for (int x=0; x<nColumns(); x++)
|
||||
{
|
||||
// Get top and bottom vertices.
|
||||
QPointF top = getVertex2d(x, 0);
|
||||
QPointF bottom = getVertex2d(x, nRows()-1);
|
||||
QPointF diff = bottom - top;
|
||||
|
||||
// Move all rows.
|
||||
for (int y=0; y<nRows(); y++)
|
||||
{
|
||||
// Ignore points from target row.
|
||||
if (y == rowId)
|
||||
continue;
|
||||
|
||||
// Get current vertex.
|
||||
QPointF p = getVertex2d( x, y );
|
||||
|
||||
// The y value of this point in the new space.
|
||||
int newY = y < rowId ? y : y-1;
|
||||
|
||||
// Move middle points.
|
||||
if (y > 0 && y < nRows()-1)
|
||||
{
|
||||
p += (y < rowId ? +1 : -1) * diff * newY * bottomMoveProp;
|
||||
}
|
||||
|
||||
// Assign new containers.
|
||||
newVertices[k] = p;
|
||||
newVertices2d[x][newY] = k;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy new mapping.
|
||||
vertices = newVertices;
|
||||
_vertices2d = newVertices2d;
|
||||
|
||||
// Decrement number of rows.
|
||||
_nRows--;
|
||||
|
||||
// Reorder.
|
||||
_reorderVertices();
|
||||
}
|
||||
|
||||
|
||||
void Mesh::resize(int nColumns_, int nRows_)
|
||||
{
|
||||
// Brutal: if asked to reduce columns or rows, just delete and redo.
|
||||
if (nColumns_ < nColumns())
|
||||
{
|
||||
while (nColumns_ != nColumns())
|
||||
removeColumn(nColumns()-2);
|
||||
}
|
||||
if (nRows_ < nRows())
|
||||
{
|
||||
while (nRows_ != nRows())
|
||||
removeRow(nRows()-2);
|
||||
}
|
||||
if (nColumns_ > nColumns())
|
||||
{
|
||||
while (nColumns_ != nColumns())
|
||||
addColumn();
|
||||
}
|
||||
if (nRows_ > nRows())
|
||||
{
|
||||
while (nRows_ != nRows())
|
||||
addRow();
|
||||
}
|
||||
}
|
||||
QVector<Quad> Mesh::getQuads() const
|
||||
{
|
||||
QVector<Quad> quads;
|
||||
for (int i=0; i<nHorizontalQuads(); i++)
|
||||
{
|
||||
for (int j=0; j<nVerticalQuads(); j++)
|
||||
{
|
||||
Quad quad(
|
||||
getVertex2d(i, j ),
|
||||
getVertex2d(i+1, j ),
|
||||
getVertex2d(i+1, j+1),
|
||||
getVertex2d(i, j+1)
|
||||
);
|
||||
quads.push_back(quad);
|
||||
}
|
||||
}
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
QVector< QVector<Quad> > Mesh::getQuads2d() const
|
||||
{
|
||||
QVector< QVector<Quad> > quads2d;
|
||||
for (int i=0; i<nHorizontalQuads(); i++)
|
||||
{
|
||||
QVector<Quad> column;
|
||||
for (int j=0; j<nVerticalQuads(); j++)
|
||||
{
|
||||
Quad quad(
|
||||
getVertex2d(i, j ),
|
||||
getVertex2d(i+1, j ),
|
||||
getVertex2d(i+1, j+1),
|
||||
getVertex2d(i, j+1)
|
||||
);
|
||||
column.push_back(quad);
|
||||
}
|
||||
quads2d.push_back(column);
|
||||
}
|
||||
return quads2d;
|
||||
}
|
||||
|
||||
void Mesh::_reorderVertices()
|
||||
{
|
||||
// Populate new vertices vector.
|
||||
QVector<QPointF> newVertices(vertices.size());
|
||||
int k = 0;
|
||||
for (int y=0; y<nRows(); y++)
|
||||
for (int x=0; x<nColumns(); x++)
|
||||
newVertices[k++] = getVertex2d( x, y );
|
||||
|
||||
// Populate _vertices2d.
|
||||
k = 0;
|
||||
for (int y=0; y<nRows(); y++)
|
||||
for (int x=0; x<nColumns(); x++)
|
||||
_vertices2d[x][y] = k++;
|
||||
|
||||
// Copy.
|
||||
vertices = newVertices;
|
||||
}
|
||||
|
||||
void Ellipse::sanitize()
|
||||
{
|
||||
// Get horizontal axis rotated 90 degrees CW
|
||||
QVector2D hAxis = getHorizontalAxis();
|
||||
const QVector2D center(getCenter());
|
||||
QVector2D hAxisRotated(hAxis.y(), -hAxis.x());
|
||||
|
||||
// Project vertex 1 onto it.
|
||||
QVector2D vAxisNormalized = hAxisRotated.normalized();
|
||||
|
||||
QVector2D vFromCenter = QVector2D(getVertex(1)) - center;
|
||||
const QVector2D& projection = QVector2D::dotProduct( vFromCenter, vAxisNormalized ) * vAxisNormalized;
|
||||
MShape::setVertex(1, (center + projection).toPointF());
|
||||
MShape::setVertex(3, (center - projection).toPointF());
|
||||
|
||||
if (hasCenterControl())
|
||||
{
|
||||
// Clip control point.
|
||||
MShape::setVertex(4, clipInside(getVertex(4)));
|
||||
}
|
||||
}
|
||||
|
||||
QPointF Ellipse::clipInside(const QPointF& v) const
|
||||
{
|
||||
// Map point as vector on a unit circle.
|
||||
QVector2D vector(toUnitCircle().map(v));
|
||||
|
||||
// Clip control point.
|
||||
return (vector.length() <= 1 ?
|
||||
v :
|
||||
fromUnitCircle().map(vector.normalized().toPointF()));
|
||||
|
||||
}
|
||||
|
||||
QTransform Ellipse::toUnitCircle() const
|
||||
{
|
||||
const QPointF& center = getCenter();
|
||||
return QTransform().scale(1.0/getHorizontalRadius(), 1.0/getVerticalRadius())
|
||||
.rotateRadians(-getRotationRadians())
|
||||
.translate(-center.x(), -center.y());
|
||||
}
|
||||
|
||||
QTransform Ellipse::fromUnitCircle() const
|
||||
{
|
||||
return toUnitCircle().inverted();
|
||||
}
|
||||
|
||||
bool Ellipse::includesPoint(qreal x, qreal y)
|
||||
{
|
||||
return (QVector2D(toUnitCircle().map(QPointF(x, y))).length() <= 1);
|
||||
}
|
||||
|
||||
void Ellipse::setVertex(int i, const QPointF& v)
|
||||
{
|
||||
// Save vertical axis vector.
|
||||
const QVector2D& vAxis = getVerticalAxis();
|
||||
|
||||
// If changed one of the two rotation-controlling points, adjust the other two points.
|
||||
if (i == 0 || i == 2)
|
||||
{
|
||||
// Transformation ellipse_t --> circle.
|
||||
QTransform transform = toUnitCircle();
|
||||
|
||||
// Change the vertex.
|
||||
_rawSetVertex(i, v);
|
||||
|
||||
// Combine with transformation circle -> ellipse_{t+1}.
|
||||
transform *= fromUnitCircle();
|
||||
|
||||
// Set vertices.
|
||||
MShape::setVertex(1, transform.map( getVertex(1) ));
|
||||
MShape::setVertex(3, transform.map( getVertex(3) ));
|
||||
if (hasCenterControl())
|
||||
MShape::setVertex(4, transform.map( getVertex(4) ));
|
||||
}
|
||||
|
||||
// If changed one of the two other points, just change the vertical axis.
|
||||
else if (i == 1 || i == 3)
|
||||
{
|
||||
// Retrieve the new horizontal axis vector and center.
|
||||
const QVector2D center(getCenter());
|
||||
|
||||
QVector2D vFromCenter = QVector2D(v) - center;
|
||||
|
||||
// Find projection of v onto vAxis / 2.
|
||||
QVector2D vAxisNormalized = vAxis.normalized();
|
||||
const QVector2D& projection = QVector2D::dotProduct( vFromCenter, vAxisNormalized ) * vAxisNormalized;
|
||||
|
||||
// Assign vertical control points.
|
||||
QPointF v1;
|
||||
QPointF v3;
|
||||
if (i == 1)
|
||||
{
|
||||
v1 = (center + projection).toPointF();
|
||||
v3 = (center - projection).toPointF();
|
||||
}
|
||||
else
|
||||
{
|
||||
v1 = (center - projection).toPointF();
|
||||
v3 = (center + projection).toPointF();
|
||||
}
|
||||
|
||||
// Transformation ellipse_t --> circle.
|
||||
QTransform transform = toUnitCircle();
|
||||
|
||||
// Change vertical points.
|
||||
_rawSetVertex(1, v1);
|
||||
_rawSetVertex(3, v3);
|
||||
|
||||
// Combine with transformation circle -> ellipse_{t+1}.
|
||||
transform *= fromUnitCircle();
|
||||
|
||||
// Set vertices.
|
||||
if (hasCenterControl())
|
||||
_rawSetVertex(4, transform.map( getVertex(4) ));
|
||||
}
|
||||
|
||||
// Center control point (make sure it stays inside!).
|
||||
else if (hasCenterControl())
|
||||
{
|
||||
// Clip control point.
|
||||
_rawSetVertex(4, clipInside(v));
|
||||
}
|
||||
|
||||
// Just to be sure.
|
||||
sanitize();
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SHAPE_H_
|
||||
#define SHAPE_H_
|
||||
#ifndef M_SHAPE_H_
|
||||
#define M_SHAPE_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
@@ -36,13 +36,18 @@
|
||||
#include <QString>
|
||||
#include <QMetaType>
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "Maths.h"
|
||||
|
||||
/**
|
||||
* Shape represented by a series of control points.
|
||||
*/
|
||||
class MShape
|
||||
class MShape : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QVector<QPointF> vertices READ getVertices WRITE setVertices)
|
||||
public:
|
||||
typedef QSharedPointer<MShape> ptr;
|
||||
|
||||
@@ -90,6 +95,12 @@ public:
|
||||
|
||||
virtual MShape* clone() const;
|
||||
|
||||
QVector<QPointF> getVertices() const { return vertices; }
|
||||
virtual void setVertices(QVector<QPointF> vertices_)
|
||||
{
|
||||
vertices = vertices_;
|
||||
}
|
||||
|
||||
protected:
|
||||
QVector<QPointF> vertices;
|
||||
|
||||
@@ -107,274 +118,5 @@ protected:
|
||||
virtual MShape* _create() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class represents a simple polygon (ie. the control points are vertices).
|
||||
*/
|
||||
class Polygon : public MShape {
|
||||
public:
|
||||
Polygon() {}
|
||||
Polygon(QVector<QPointF> vertices_) : MShape(vertices_) {}
|
||||
virtual ~Polygon() {}
|
||||
|
||||
virtual QPolygonF toPolygon() const;
|
||||
|
||||
virtual bool includesPoint(const QPointF& p) {
|
||||
return toPolygon().containsPoint(p, Qt::OddEvenFill);
|
||||
}
|
||||
|
||||
// Override the parent, checking to make sure the vertices are displaced correctly.
|
||||
virtual void setVertex(int i, const QPointF& v);
|
||||
|
||||
protected:
|
||||
/// Returns all line segments of the polygon.
|
||||
QVector<QLineF> _getSegments() const;
|
||||
|
||||
/// Returns all line segments of a polygon.
|
||||
static QVector<QLineF> _getSegments(const QPolygonF& polygon);
|
||||
|
||||
/// Makes sure vertex v as the i-th point of polygon stays inside the polygon.
|
||||
static void _constrainVertex(const QPolygonF& polygon, int i, QPointF& v);
|
||||
};
|
||||
|
||||
/**
|
||||
* Four-vertex shape.
|
||||
*/
|
||||
class Quad : public Polygon
|
||||
{
|
||||
public:
|
||||
Quad() {}
|
||||
Quad(QPointF p1, QPointF p2, QPointF p3, QPointF p4)
|
||||
{
|
||||
_addVertex(p1);
|
||||
_addVertex(p2);
|
||||
_addVertex(p3);
|
||||
_addVertex(p4);
|
||||
}
|
||||
virtual ~Quad() {}
|
||||
|
||||
virtual QString getType() const { return "quad"; }
|
||||
|
||||
protected:
|
||||
/// Returns a new MShape (using default constructor).
|
||||
virtual MShape* _create() const { return new Quad(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Triangle shape.
|
||||
*/
|
||||
class Triangle : public Polygon
|
||||
{
|
||||
public:
|
||||
Triangle() {}
|
||||
Triangle(QPointF p1, QPointF p2, QPointF p3)
|
||||
{
|
||||
_addVertex(p1);
|
||||
_addVertex(p2);
|
||||
_addVertex(p3);
|
||||
}
|
||||
virtual ~Triangle() {}
|
||||
virtual QString getType() const { return "triangle"; }
|
||||
|
||||
protected:
|
||||
/// Returns a new MShape (using default constructor).
|
||||
virtual MShape* _create() const { return new Triangle(); }
|
||||
};
|
||||
|
||||
class Mesh : public Quad
|
||||
{
|
||||
typedef QVector<QVector<int> > IndexVector2d;
|
||||
|
||||
public:
|
||||
Mesh();
|
||||
|
||||
// This constructor creates a quad mesh (four corners) using the same order as for the quad
|
||||
// constructor (ie. clockwise).
|
||||
Mesh(QPointF p1, QPointF p2, QPointF p3, QPointF p4);
|
||||
|
||||
// Standard mesh constructor.
|
||||
Mesh(const QVector<QPointF>& points, int nColumns, int nRows);
|
||||
|
||||
virtual ~Mesh() {}
|
||||
|
||||
// Performs the actual adding of points (used for loading).
|
||||
void init(const QVector<QPointF>& points, int nColumns, int nRows);
|
||||
|
||||
virtual QString getType() const { return "mesh"; }
|
||||
|
||||
/// Returns a polygon that is formed by all the contour points of the mesh.
|
||||
virtual QPolygonF toPolygon() const;
|
||||
|
||||
// Override the parent, checking to make sure the vertices are displaced correctly.
|
||||
virtual void setVertex(int i, const QPointF& v);
|
||||
|
||||
QPointF getVertex2d(int i, int j) const
|
||||
{
|
||||
return vertices[_vertices2d[i][j]];
|
||||
}
|
||||
|
||||
void setVertex2d(int i, int j, const QPointF& v)
|
||||
{
|
||||
vertices[_vertices2d[i][j]] = v; // copy
|
||||
}
|
||||
|
||||
void setVertex2d(int i, int j, double x, double y)
|
||||
{
|
||||
vertices[_vertices2d[i][j]] = QPointF(x, y);
|
||||
}
|
||||
|
||||
void resizeVertices2d(IndexVector2d& vertices2d, int nColumns, int nRows);
|
||||
|
||||
//
|
||||
void addColumn();
|
||||
void addRow();
|
||||
|
||||
void removeColumn(int columnId);
|
||||
void removeRow(int rowId);
|
||||
|
||||
void resize(int nColumns_, int nRows_);
|
||||
|
||||
QVector<Quad> getQuads() const;
|
||||
QVector<QVector<Quad> > getQuads2d() const;
|
||||
|
||||
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
|
||||
IndexVector2d _vertices2d;
|
||||
|
||||
/**
|
||||
* Reorder vertices in a standard order:
|
||||
*
|
||||
* 0----1----2----3
|
||||
* | | | |
|
||||
* 4----5----6----7
|
||||
* | | | |
|
||||
* 8----9---10----11
|
||||
*/
|
||||
void _reorderVertices();
|
||||
|
||||
/// Returns a new MShape (using default constructor).
|
||||
virtual MShape* _create() const { return new Mesh(); }
|
||||
};
|
||||
|
||||
class Ellipse : public MShape {
|
||||
public:
|
||||
Ellipse() {}
|
||||
Ellipse(QPointF p1, QPointF p2, QPointF p3, QPointF p4, QPointF p5)
|
||||
{
|
||||
_addVertex(p1);
|
||||
_addVertex(p2);
|
||||
_addVertex(p3);
|
||||
_addVertex(p4);
|
||||
_addVertex(p5);
|
||||
sanitize();
|
||||
}
|
||||
|
||||
Ellipse(QPointF p1, QPointF p2, QPointF p3, QPointF p4, bool hasCenterControl=true)
|
||||
{
|
||||
_addVertex(p1);
|
||||
_addVertex(p2);
|
||||
_addVertex(p3);
|
||||
_addVertex(p4);
|
||||
if (hasCenterControl)
|
||||
_addVertex(getCenter());
|
||||
sanitize();
|
||||
}
|
||||
|
||||
virtual ~Ellipse() {}
|
||||
|
||||
/// Remaps points so as to make sure this is a correct ellipse, keeping vertices 0 and 2 as
|
||||
/// reference for the horizzontal axis.
|
||||
void sanitize();
|
||||
|
||||
virtual QString getType() const { return "ellipse"; }
|
||||
|
||||
qreal getRotationRadians() const
|
||||
{
|
||||
QVector2D hAxis = getHorizontalAxis();
|
||||
return atan2( hAxis.y(), hAxis.x() );
|
||||
}
|
||||
|
||||
qreal getRotation() const
|
||||
{
|
||||
return radiansToDegrees( getRotationRadians() );
|
||||
}
|
||||
|
||||
bool hasCenterControl() const
|
||||
{
|
||||
return (nVertices() == 5);
|
||||
}
|
||||
|
||||
/// If v is outside boundaries, remap it to the border.
|
||||
QPointF clipInside(const QPointF& v) const;
|
||||
|
||||
// QRect getBoundingRect() const {
|
||||
// return QRect(0, getVerticalAxis().manhattanLength(),
|
||||
// getHorizontalAxis().manhattanLength(), getVerticalAxis().manhattanLength());
|
||||
// }
|
||||
//
|
||||
QPointF getCenter() const
|
||||
{
|
||||
return (QVector2D(getVertex(0)) - (getHorizontalAxis() / 2)).toPointF();
|
||||
}
|
||||
|
||||
QVector2D getHorizontalAxis() const
|
||||
{
|
||||
return QVector2D(getVertex(0)) - QVector2D(getVertex(2));
|
||||
}
|
||||
|
||||
QVector2D getVerticalAxis() const
|
||||
{
|
||||
return QVector2D(getVertex(1)) - QVector2D(getVertex(3));
|
||||
}
|
||||
|
||||
qreal getHorizontalRadius() const
|
||||
{
|
||||
return getHorizontalAxis().length() / 2;
|
||||
}
|
||||
|
||||
qreal getVerticalRadius() const
|
||||
{
|
||||
return getVerticalAxis().length() / 2;
|
||||
}
|
||||
|
||||
/// Remaps point from ellipse to a circle with radius 1 set at origin (0,0).
|
||||
QTransform toUnitCircle() const;
|
||||
|
||||
/// Remaps point from circle with radius 1 set at origin (0,0) to ellipse coordinates.
|
||||
QTransform fromUnitCircle() const;
|
||||
|
||||
/** 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
|
||||
*/
|
||||
virtual bool includesPoint(qreal x, qreal y);
|
||||
|
||||
virtual bool includesPoint(const QPointF& p)
|
||||
{
|
||||
return includesPoint(p.x(), p.y());
|
||||
}
|
||||
|
||||
// Override the parent, checking to make sure the vertices are displaced correctly.
|
||||
virtual void setVertex(int i, const QPointF& v);
|
||||
|
||||
protected:
|
||||
/// Returns a new MShape (using default constructor).
|
||||
virtual MShape* _create() const { return new Ellipse(); }
|
||||
|
||||
//protected:
|
||||
// virtual void _vertexChanged(int i, Point* p=NULL) {
|
||||
// // Get horizontal and vertical axis length.
|
||||
// qreal hAxisLength = Point::dist(getVertex(0)->toPoint(), getVertex(2)->toPoint());
|
||||
// qreal vAxisLength = Point::dist(getVertex(1)->toPoint(), getVertex(3)->toPoint());
|
||||
// }
|
||||
};
|
||||
|
||||
#endif /* SHAPE_H_ */
|
||||
|
||||
@@ -88,10 +88,10 @@ void MeshControlPainter::_paintShape(QPainter *painter, MapperGLCanvas* canvas)
|
||||
painter->setPen(getRescaledShapeStroke(canvas, true));
|
||||
|
||||
// Draw inner quads.
|
||||
QVector<Quad> quads = mesh->getQuads();
|
||||
for (QVector<Quad>::const_iterator it = quads.begin(); it != quads.end(); ++it)
|
||||
QVector<Quad::ptr> quads = mesh->getQuads();
|
||||
for (QVector<Quad::ptr>::const_iterator it = quads.begin(); it != quads.end(); ++it)
|
||||
{
|
||||
painter->drawPolygon(it->toPolygon());
|
||||
painter->drawPolygon((*it)->toPolygon());
|
||||
}
|
||||
|
||||
// Draw outer quad.
|
||||
|
||||
+26
-26
@@ -317,8 +317,8 @@ void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter)
|
||||
{
|
||||
QSharedPointer<Mesh> outputMesh = qSharedPointerCast<Mesh>(_shape);
|
||||
QSharedPointer<Mesh> inputMesh = qSharedPointerCast<Mesh>(_inputShape);
|
||||
QVector<QVector<Quad> > outputQuads = outputMesh->getQuads2d();
|
||||
QVector<QVector<Quad> > inputQuads = inputMesh->getQuads2d();
|
||||
QVector<QVector<Quad::ptr> > outputQuads = outputMesh->getQuads2d();
|
||||
QVector<QVector<Quad::ptr> > inputQuads = inputMesh->getQuads2d();
|
||||
|
||||
// Check if we increased or decreased number of columns/rows in mesh.
|
||||
bool forceRebuild = false;
|
||||
@@ -350,14 +350,14 @@ void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter)
|
||||
{
|
||||
for (int y = 0; y < outputMesh->nVerticalQuads(); y++)
|
||||
{
|
||||
Quad& inputQuad = inputQuads[x][y];
|
||||
Quad& outputQuad = outputQuads[x][y];
|
||||
Quad::ptr inputQuad = inputQuads[x][y];
|
||||
Quad::ptr outputQuad = outputQuads[x][y];
|
||||
|
||||
// Verify if item needs recomputing.
|
||||
CacheQuadItem& item = _cachedQuadItems[x][y];
|
||||
if (forceRebuild ||
|
||||
item.parent.input.toPolygon() != inputQuad.toPolygon() ||
|
||||
item.parent.output.toPolygon() != outputQuad.toPolygon()) {
|
||||
item.parent.input->toPolygon() != inputQuad->toPolygon() ||
|
||||
item.parent.output->toPolygon() != outputQuad->toPolygon()) {
|
||||
|
||||
// Copy input and output quads for verification purposes.
|
||||
item.parent.input = inputQuad;
|
||||
@@ -366,7 +366,7 @@ void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter)
|
||||
// Recompute sub quads.
|
||||
item.subQuads.clear();
|
||||
|
||||
QSizeF size = mapFromScene(outputQuad.toPolygon()).boundingRect().size();
|
||||
QSizeF size = mapFromScene(outputQuad->toPolygon()).boundingRect().size();
|
||||
float area = size.width() * size.height();
|
||||
|
||||
// Rebuild cache quad item.
|
||||
@@ -377,9 +377,9 @@ void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter)
|
||||
foreach (CacheQuadMapping m, item.subQuads)
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
for (int i = 0; i < outputQuad.nVertices(); i++)
|
||||
for (int i = 0; i < outputQuad->nVertices(); i++)
|
||||
{
|
||||
Util::setGlTexPoint(*_texture.toStrongRef(), m.input.getVertex(i), mapFromScene(m.output.getVertex(i)));
|
||||
Util::setGlTexPoint(*_texture.toStrongRef(), m.input->getVertex(i), mapFromScene(m.output->getVertex(i)));
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
@@ -388,21 +388,21 @@ void MeshTextureGraphicsItem::_doDrawOutput(QPainter* painter)
|
||||
}
|
||||
}
|
||||
|
||||
void MeshTextureGraphicsItem::_buildCacheQuadItem(CacheQuadItem& item, const Quad& inputQuad, const Quad& outputQuad, float outputArea, float inputThreshod, float outputThreshold, int minArea, int maxDepth)
|
||||
void MeshTextureGraphicsItem::_buildCacheQuadItem(CacheQuadItem& item, const Quad::ptr& inputQuad, const Quad::ptr& outputQuad, float outputArea, float inputThreshod, float outputThreshold, int minArea, int maxDepth)
|
||||
{
|
||||
bool stop = false;
|
||||
if (maxDepth == 0 || outputArea < minArea)
|
||||
stop = true;
|
||||
else {
|
||||
QPointF oa = mapFromScene(outputQuad.getVertex(0));
|
||||
QPointF ob = mapFromScene(outputQuad.getVertex(1));
|
||||
QPointF oc = mapFromScene(outputQuad.getVertex(2));
|
||||
QPointF od = mapFromScene(outputQuad.getVertex(3));
|
||||
QPointF oa = mapFromScene(outputQuad->getVertex(0));
|
||||
QPointF ob = mapFromScene(outputQuad->getVertex(1));
|
||||
QPointF oc = mapFromScene(outputQuad->getVertex(2));
|
||||
QPointF od = mapFromScene(outputQuad->getVertex(3));
|
||||
|
||||
QPointF ia = inputQuad.getVertex(0);
|
||||
QPointF ib = inputQuad.getVertex(1);
|
||||
QPointF ic = inputQuad.getVertex(2);
|
||||
QPointF id = inputQuad.getVertex(3);
|
||||
QPointF ia = inputQuad->getVertex(0);
|
||||
QPointF ib = inputQuad->getVertex(1);
|
||||
QPointF ic = inputQuad->getVertex(2);
|
||||
QPointF id = inputQuad->getVertex(3);
|
||||
|
||||
QPointF outputV1 = oa-ob;
|
||||
QPointF outputV2 = oc-ob;
|
||||
@@ -440,8 +440,8 @@ void MeshTextureGraphicsItem::_buildCacheQuadItem(CacheQuadItem& item, const Qua
|
||||
}
|
||||
else // subdivide
|
||||
{
|
||||
QList<Quad> inputSubQuads = _split(inputQuad);
|
||||
QList<Quad> outputSubQuads = _split(outputQuad);
|
||||
QList<Quad::ptr> inputSubQuads = _split(*inputQuad);
|
||||
QList<Quad::ptr> outputSubQuads = _split(*outputQuad);
|
||||
for (int i = 0; i < inputSubQuads.size(); i++)
|
||||
{
|
||||
_buildCacheQuadItem(item, inputSubQuads[i], outputSubQuads[i], outputArea*0.25, inputThreshod, outputThreshold, minArea, (maxDepth == -1 ? -1 : maxDepth - 1));
|
||||
@@ -449,9 +449,9 @@ void MeshTextureGraphicsItem::_buildCacheQuadItem(CacheQuadItem& item, const Qua
|
||||
}
|
||||
}
|
||||
|
||||
QList<Quad> MeshTextureGraphicsItem::_split(const Quad& quad)
|
||||
QList<Quad::ptr> MeshTextureGraphicsItem::_split(const Quad& quad)
|
||||
{
|
||||
QList<Quad> quads;
|
||||
QList<Quad::ptr> quads;
|
||||
|
||||
QPointF a = quad.getVertex(0);
|
||||
QPointF b = quad.getVertex(1);
|
||||
@@ -465,10 +465,10 @@ QList<Quad> MeshTextureGraphicsItem::_split(const Quad& quad)
|
||||
|
||||
QPointF abcd = (ab + cd) * 0.5f;
|
||||
|
||||
quads.append(Quad(a, ab, abcd, ad));
|
||||
quads.append(Quad(ab, b, bc, abcd));
|
||||
quads.append(Quad(abcd, bc, c, cd));
|
||||
quads.append(Quad(ad, abcd, cd, d));
|
||||
quads.append(Quad::ptr(new Quad(a, ab, abcd, ad)));
|
||||
quads.append(Quad::ptr(new Quad(ab, b, bc, abcd)));
|
||||
quads.append(Quad::ptr(new Quad(abcd, bc, c, cd)));
|
||||
quads.append(Quad::ptr(new Quad(ad, abcd, cd, d)));
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
+6
-5
@@ -31,7 +31,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Shape.h"
|
||||
#include "Shapes.h"
|
||||
|
||||
#include "Paint.h"
|
||||
#include "Mapping.h"
|
||||
#include "MapperGLCanvas.h"
|
||||
@@ -204,8 +205,8 @@ class MeshTextureGraphicsItem : public PolygonTextureGraphicsItem
|
||||
{
|
||||
// Internal use (cache). A structure consisting of the input and output quads of mapping.
|
||||
struct CacheQuadMapping {
|
||||
Quad input;
|
||||
Quad output;
|
||||
Quad::ptr input;
|
||||
Quad::ptr output;
|
||||
};
|
||||
|
||||
// Internal use (cache). Contains a parent mapping and all its sub-mappings.
|
||||
@@ -224,12 +225,12 @@ private:
|
||||
* Builds cache item recursively using the technique described in
|
||||
* Oliveira, M. "Correcting Texture Mapping Errors Introduced by Graphics Hardware"
|
||||
*/
|
||||
void _buildCacheQuadItem(CacheQuadItem& item, const Quad& inputQuad, const Quad& outputQuad,
|
||||
void _buildCacheQuadItem(CacheQuadItem& item, const Quad::ptr& inputQuad, const Quad::ptr& outputQuad,
|
||||
float outputArea, float inputThreshold = 0.0001f, float outputThreshold = 0.001f,
|
||||
int minArea=MM::MESH_SUBDIVISION_MIN_AREA, int maxDepth=-1);
|
||||
|
||||
// Help function that returns four equal-size sub-quads from a quad.
|
||||
QList<Quad> _split(const Quad& quad);
|
||||
QList<Quad::ptr> _split(const Quad& quad);
|
||||
|
||||
// Contains the current cache.
|
||||
QVector<QVector<CacheQuadItem> > _cachedQuadItems;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Shapes.h
|
||||
*
|
||||
* (c) 2016 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 SHAPES_H_
|
||||
#define SHAPES_H_
|
||||
|
||||
#include "Quad.h"
|
||||
#include "Ellipse.h"
|
||||
#include "Mesh.h"
|
||||
#include "Triangle.h"
|
||||
|
||||
#endif /* SHAPES_H_ */
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Triangle.h
|
||||
*
|
||||
* (c) 2016 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 TRIANGLE_H_
|
||||
#define TRIANGLE_H_
|
||||
|
||||
#include "Polygon.h"
|
||||
|
||||
/**
|
||||
* Triangle shape.
|
||||
*/
|
||||
class Triangle : public Polygon
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Triangle() {}
|
||||
Triangle(QPointF p1, QPointF p2, QPointF p3)
|
||||
{
|
||||
_addVertex(p1);
|
||||
_addVertex(p2);
|
||||
_addVertex(p3);
|
||||
}
|
||||
virtual ~Triangle() {}
|
||||
virtual QString getType() const { return "triangle"; }
|
||||
|
||||
protected:
|
||||
/// Returns a new MShape (using default constructor).
|
||||
virtual MShape* _create() const { return new Triangle(); }
|
||||
};
|
||||
|
||||
|
||||
#endif /* TRIANGLE_H_ */
|
||||
@@ -216,10 +216,10 @@ void drawControlsMesh(QPainter* painter, const QList<int>* selectedVertices, con
|
||||
painter->setPen(MM::SHAPE_INNER_STROKE);
|
||||
|
||||
// Draw inner quads.
|
||||
QVector<Quad> quads = mesh.getQuads();
|
||||
for (QVector<Quad>::const_iterator it = quads.begin(); it != quads.end(); ++it)
|
||||
QVector<Quad::ptr> quads = mesh.getQuads();
|
||||
for (QVector<Quad::ptr>::const_iterator it = quads.begin(); it != quads.end(); ++it)
|
||||
{
|
||||
painter->drawPolygon(it->toPolygon());
|
||||
painter->drawPolygon((*it)->toPolygon());
|
||||
}
|
||||
|
||||
// Draw outer quad.
|
||||
|
||||
@@ -26,8 +26,9 @@
|
||||
#include <GL/gl.h>
|
||||
#endif
|
||||
|
||||
#include "Shapes.h"
|
||||
|
||||
#include "MM.h"
|
||||
#include "Shape.h"
|
||||
#include "Paint.h"
|
||||
#include <QString>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ HEADERS = \
|
||||
Commands.h \
|
||||
DestinationGLCanvas.h \
|
||||
Element.h \
|
||||
Ellipse.h \
|
||||
MM.h \
|
||||
MainApplication.h \
|
||||
MainWindow.h \
|
||||
@@ -19,6 +20,7 @@ HEADERS = \
|
||||
MappingManager.h \
|
||||
Maths.h \
|
||||
MediaImpl.h \
|
||||
Mesh.h \
|
||||
MetaObjectRegistry.h \
|
||||
OscInterface.h \
|
||||
OscReceiver.h \
|
||||
@@ -26,13 +28,17 @@ HEADERS = \
|
||||
OutputGLWindow.h \
|
||||
Paint.h \
|
||||
PaintGui.h \
|
||||
Polygon.h \
|
||||
PreferencesDialog.h \
|
||||
ProjectReader.h \
|
||||
ProjectWriter.h \
|
||||
Quad.h \
|
||||
Shape.h \
|
||||
Shapes.h \
|
||||
ShapeControlPainter.h \
|
||||
ShapeGraphicsItem.h \
|
||||
SourceGLCanvas.h \
|
||||
Triangle.h \
|
||||
UidAllocator.h \
|
||||
Util.h
|
||||
|
||||
@@ -40,6 +46,7 @@ SOURCES = \
|
||||
Commands.cpp \
|
||||
DestinationGLCanvas.cpp \
|
||||
Element.cpp \
|
||||
Ellipse.cpp \
|
||||
MM.cpp \
|
||||
MainApplication.cpp \
|
||||
MainWindow.cpp \
|
||||
@@ -48,6 +55,7 @@ SOURCES = \
|
||||
Mapping.cpp \
|
||||
MappingManager.cpp \
|
||||
MediaImpl.cpp \
|
||||
Mesh.cpp \
|
||||
MetaObjectRegistry.cpp \
|
||||
OscInterface.cpp \
|
||||
OscReceiver.cpp \
|
||||
@@ -55,6 +63,7 @@ SOURCES = \
|
||||
OutputGLWindow.cpp \
|
||||
Paint.cpp \
|
||||
PaintGui.cpp \
|
||||
Polygon.cpp \
|
||||
PreferencesDialog.cpp \
|
||||
ProjectReader.cpp \
|
||||
ProjectWriter.cpp \
|
||||
|
||||
Reference in New Issue
Block a user