/* * Shape.cpp * * (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 . */ #include "Shape.h" void MShape::copyFrom(const MShape& shape) { // Just copy vertices. vertices = shape.vertices; } MShape* MShape::clone() const { MShape* copyShape = _create(); copyShape->copyFrom(*this); return copyShape; } void MShape::translate(const QPointF& offset) { for (int i=0; i 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::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 Polygon::_getSegments() const { return _getSegments(toPolygon()); } QVector Polygon::_getSegments(const QPolygonF& polygon) { QVector segments; for (int i=0; i::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 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& points, int nColumns, int nRows) : Quad() { init(points, nColumns, nRows); } void Mesh::init(const QVector& 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=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= 1 && columnId < nColumns()-1); // Temporary containers that will be used to rebuild new vertex space. IndexVector2d newVertices2d; resizeVertices2d(newVertices2d, nColumns()-1, nRows()); QVector 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 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; // Increment number of columns. _nColumns--; } 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 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 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; // Increment number of columns. _nRows--; } 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 Mesh::getQuads() const { QVector quads; for (int i=0; i > Mesh::getQuads2d() const { QVector< QVector > quads2d; for (int i=0; i column; for (int j=0; j newVertices(vertices.size()); int k = 0; for (int y=0; y 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(); }