Commit f34aeca7 authored by Leander Schulten's avatar Leander Schulten
Browse files

Added Polygon Class that draw a Polygon from points connected to Triangles

parent 986d4ff2
#include "polygon.h"
#include <assert.h>
#include <QSGGeometryNode>
#include <QSGFlatColorMaterial>
#include <algorithm>
#include <QJsonArray>
Polygon::Polygon()
{
setFlag(ItemHasContents);
}
Polygon::Polygon(const QJsonObject &o):Polygon(){
{
auto array = o["points"].toArray();
for(const auto p_ : array){
auto p = p_.toObject();
addPoint(Point(p));
}
}
{
auto array = o["triangles"].toArray();
for(const auto p_ : array){
auto p = p_.toObject();
triangles.emplace_back(p);
}
}
{
auto array = o["arcs"].toArray();
for(const auto p_ : array){
auto p = p_.toObject();
arcs.emplace_back(p);
}
}
}
void Polygon::writeJsonObject(QJsonObject &o) const{
{
QJsonArray a;
for(const auto p : points){
QJsonObject o_;
p.writeJsonObject(o_);
a.append(o_);
}
o.insert("points",a);
} {
QJsonArray a;
for(const auto p : triangles){
QJsonObject o_;
p.writeJsonObject(o_);
a.append(o_);
}
o.insert("triangles",a);
} {
QJsonArray a;
for(const auto p : arcs){
QJsonObject o_;
p.writeJsonObject(o_);
a.append(o_);
}
o.insert("arcs",a);
}
}
void Polygon::removePoint(Triangle::index_type index){
assert(index<points.size()&&"Index out of Bounds.");
points.erase(points.begin()+index);
for(auto i = triangles.begin();i!=triangles.cend();++i){
if(i->haveIndex(index)){
i = triangles.erase(i);
}else{
i->decrementAbove(index);
}
}
}
void Polygon::removePoint(PointContainer::const_iterator iter){
assert(iter!=points.cend()&&"Index out of Bounds.");
points.erase(iter);
auto index = iter-points.cbegin();
for(auto i = triangles.begin();i!=triangles.cend();++i){
if(i->haveIndex(index)){
i = triangles.erase(i);
}else{
i->decrementAbove(index);
}
}
}
void Polygon::addTriangle(Triangle::index_type first, Triangle::index_type second, Triangle::index_type third){
triangles.emplace_back(first,second,third);
update();
}
void Polygon::addArc(Arc::index_type first, Arc::index_type second, Arc::index_type third){
arcs.emplace_back(first,second,third);
update();
}
QSGNode* Polygon::updatePaintNode(QSGNode *oldNode , UpdatePaintNodeData *){
QSGGeometryNode *node = nullptr;
QSGGeometry *geometry = nullptr;
constexpr auto fiveDegrees = 10/180.*M_PI;
int extraVertexCountForArcs = 0;
for(const auto a : arcs){
extraVertexCountForArcs+=a.angle(points)/fiveDegrees;
}
const int extraIndexCountForArcs = 3*arcs.size()+3*extraVertexCountForArcs;
if (!oldNode) {
node = new QSGGeometryNode;
geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), points.size() + extraVertexCountForArcs,triangles.size()*3 +extraIndexCountForArcs);
geometry->setDrawingMode(QSGGeometry::DrawTriangles);
node->setGeometry(geometry);
node->setFlag(QSGNode::OwnsGeometry);
QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
material->setColor(polygonColor);
node->setMaterial(material);
node->setFlag(QSGNode::OwnsMaterial);
} else {
node = static_cast<QSGGeometryNode *>(oldNode);
geometry = node->geometry();
geometry->allocate(points.size()+extraVertexCountForArcs,triangles.size()*3+extraIndexCountForArcs);
}
std::memcpy(geometry->vertexDataAsPoint2D(),points.data(),points.size()*sizeof(PointContainer::value_type));
std::memcpy(geometry->indexDataAsUShort(),triangles.data(),triangles.size()*sizeof(TriangleContainer::value_type));
// add arcs
int lastVertexIndex = points.size();
QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D() + points.size();
unsigned short *indices= geometry->indexDataAsUShort() + triangles.size()*3;
class Matrix{
public:
// rotiert den Vector um 5 Grad nach rechts
static void rotate5Degrees(Point &p){
static const auto cos = std::cos(fiveDegrees);
static const auto sin = std::sin(fiveDegrees);
const auto newX = cos * p.x - sin * p.y;
p.y = sin * p.x + cos * p.y;
p.x = newX;
}
};
for(const auto arc : arcs){
const auto angle = arc.angle(points);
if(angle < fiveDegrees){
*indices = arc.left;
*(++indices) = arc.mid;
*(++indices) = arc.right;
++indices;
}else{
Point startVector = arc.startVector(points);
const Point endVector = arc.endVector(points);
const auto startLength = std::sqrt(startVector.x*startVector.x+startVector.y*startVector.y);
const auto endLength = std::sqrt(endVector.x*endVector.x+endVector.y*endVector.y);
const auto lengthDiff = (endLength-startLength)/startLength;
#warning Mögliche Division durch null
const auto lengthXDiff = lengthDiff ;
const auto lengthYDiff = lengthDiff ;
const int needExtraVertices = angle / fiveDegrees;
// add first Vertex and triangle
Matrix::rotate5Degrees(startVector);
vertices->x = points[arc.mid].x + startVector.x + startVector.x * lengthXDiff * (fiveDegrees/angle);
vertices->y = points[arc.mid].y + startVector.y + startVector.y * lengthYDiff * (fiveDegrees/angle);
*indices = arc.left;
*(++indices) = arc.mid;
*(++indices) = lastVertexIndex;
for(int i = 1;i<needExtraVertices;++i){
Matrix::rotate5Degrees(startVector);
const auto lengthMult = lengthDiff * ((i+1)*fiveDegrees/angle);
const auto lengthXMult = lengthXDiff * ((i+1)*fiveDegrees/angle);
const auto lengthYMult = lengthYDiff * ((i+1)*fiveDegrees/angle);
++vertices;
vertices->x = points[arc.mid].x + startVector.x + startVector.x*lengthXMult;
vertices->y = points[arc.mid].y + startVector.y + startVector.y*lengthYMult;
*++indices = lastVertexIndex;
*++indices = arc.mid;
++lastVertexIndex;
*++indices = lastVertexIndex;
}
// add last closing triangle
*++indices = lastVertexIndex;
*++indices = arc.mid;
*++indices = arc.right;
++lastVertexIndex;
++indices;
++vertices;
/*
Point startVector = arc.startVector(points);
auto startAngle = arc.startAngle(points) + M_PI_2;
const Point endVector = arc.endVector(points);
const auto startLength = std::sqrt(startVector.x*startVector.x+startVector.y*startVector.y);
const auto endLength = std::sqrt(endVector.x*endVector.x+endVector.y*endVector.y);
const auto lengthDiff = (endLength-startLength)/startLength;
const auto lengthXDiff = lengthDiff;
const auto lengthYDiff = 0;
const int needExtraVertices = angle / fiveDegrees;
// add first Vertex and triangle
vertices->x = points[arc.mid].x + std::sin(startAngle+fiveDegrees) * startLength * (1+lengthDiff * (fiveDegrees/angle));
vertices->y = points[arc.mid].y - std::cos(startAngle+fiveDegrees) * startLength * (1+lengthDiff * (fiveDegrees/angle));
*indices = arc.left;
*(++indices) = arc.mid;
*(++indices) = lastVertexIndex;
for(int i = 2;i<=needExtraVertices;++i){
const auto lengthMult = 1 + lengthDiff * (i*fiveDegrees/angle);
const auto lengthXMult = 1 + lengthXDiff * (i*fiveDegrees/angle);
const auto lengthYMult = 1 + lengthYDiff * (i*fiveDegrees/angle);
++vertices;
vertices->x = points[arc.mid].x + std::sin(startAngle+i*fiveDegrees) * startLength * lengthXMult;
vertices->y = points[arc.mid].y - std::cos(startAngle+i*fiveDegrees) * startLength * lengthYMult;
*++indices = lastVertexIndex;
*++indices = arc.mid;
++lastVertexIndex;
*++indices = lastVertexIndex;
}
// add last closing triangle
*++indices = lastVertexIndex;
*++indices = arc.mid;
*++indices = arc.right;
++lastVertexIndex;
++indices;
++vertices;*/
}
}
// count
//qDebug() << "vertices alloc : "<<geometry->vertexCount()<<" or "<<points.size() + extraVertexCountForArcs<< " used : "<<(vertices-geometry->vertexDataAsPoint2D())<<'\n';
//qDebug() << "indices alloc : "<<geometry->indexCount()<<"or"<<triangles.size()*3 +extraIndexCountForArcs<< " used : "<<(indices-geometry->indexDataAsUShort())<<'\n';
node->markDirty(QSGNode::DirtyGeometry);
return node;
}
Polygon::Triangle::index_type Polygon::createPoint(Point p){
for(auto i = points.cbegin();i!=points.cend();++i){
if(std::is_floating_point<Point::number_type>::value){
if(i->isNear(p.x,p.y,std::numeric_limits<Point::number_type>::epsilon()*2)){
return i-points.cbegin();
}
}else{
if(i->x==p.x&&i->y==p.y){
return i-points.cbegin();
}
}
}
points.push_back(p);
return points.size()-1;
}
void Polygon::addRectangle(Point p1, Point p2, Point p3, Point p4){
Triangle::index_type i1 = createPoint(p1),i2 = createPoint(p2),i3 = createPoint(p3),i4 = createPoint(p4);
triangles.emplace_back(i1,i2,i3);
triangles.emplace_back(i2,i3,i4);
}
#ifndef POLYGON_H
#define POLYGON_H
#include <limits>
#include <vector>
#include <QQuickItem>
#include <cmath>
#include <QJsonObject>
template<typename type>
class Point{
static_assert(std::is_integral<type>::value||std::is_floating_point<type>::value,"Type must be an number");
public:
Point(type x, type y):x(x),y(y){}
Point(const QJsonObject &o):x(o["x"].toDouble()),y(o["y"].toDouble()){}
typedef type number_type;
type x,y;
bool isNear(type x, type y, type maxDisstance)const{
return (this->x-x)*(this->x-x) + (this->y-y)*(this->y-y) < maxDisstance*maxDisstance;
}
void writeJsonObject(QJsonObject &o)const{
o.insert("x",x);
o.insert("y",y);
}
};
template<typename Indextype>
class Triangle{
Indextype first, second, third;
public:
typedef Indextype index_type;
Triangle(Indextype first,Indextype second,Indextype third):first(first),second(second),third(third){}
Triangle(const QJsonObject &o):first(o["first"].toInt()),second(o["second"].toInt()),third(o["third"].toInt()){}
inline bool haveIndex(Indextype i)const{
return first==i||second==i||third==i;
}
void decrementAbove(Indextype i){
if(first>i)--first;
if(second>i)--second;
if(third>i)--third;
}
void writeJsonObject(QJsonObject &o)const{
o.insert("first",first);
o.insert("second",second);
o.insert("third",third);
}
};
template<typename Indextype>
class Arc{
Indextype left, mid, right;
public:
friend class Polygon;
typedef Indextype index_type;
Arc(Indextype left,Indextype mid,Indextype right):left(left),mid(mid),right(right){}
Arc(const QJsonObject &o):left(o["left"].toInt()),mid(o["mid"].toInt()),right(o["right"].toInt()){}
inline bool haveIndex(Indextype i)const{
return left==i||mid==i||right==i;
}
void decrementAbove(Indextype i){
if(left>i)--left;
if(mid>i)--mid;
if(right>i)--right;
}
template<typename Container>
double angle(const Container &c)const{
const auto end = endAngle(c);
const auto start = startAngle(c);
if(start>end){
return M_PI*2+end-start;
}else{
return end-start;
}
}
template<typename Container>
double startAngle(const Container &c)const{
return std::atan2(c[left].y-c[mid].y, c[left].x-c[mid].x);
}
template<typename Container>
double endAngle(const Container &c)const{
return std::atan2(c[right].y-c[mid].y, c[right].x-c[mid].x);
}
template<typename Container>
typename Container::value_type startVector(const Container &c)const{
return {c[left].x-c[mid].x, c[left].y-c[mid].y};
}
template<typename Container>
typename Container::value_type endVector(const Container &c)const{
return {c[right].x-c[mid].x, c[right].y-c[mid].y};
}
void writeJsonObject(QJsonObject &o)const{
o.insert("left",left);
o.insert("mid",mid);
o.insert("right",right);
}
};
class Polygon : public QQuickItem
{
public:
typedef Point<float> Point;
typedef Triangle<unsigned short> Triangle;
typedef Arc<unsigned short> Arc;
typedef std::vector<Point> PointContainer;
typedef std::vector<Triangle> TriangleContainer;
typedef std::vector<Arc> ArcContainer;
friend class MapEditor;
friend class MapView;
protected:
PointContainer points;
TriangleContainer triangles;
ArcContainer arcs;
QColor polygonColor;
public:
Polygon();
Polygon(const QJsonObject &o);
void writeJsonObject(QJsonObject &o)const;
void setColor(QColor c){polygonColor=c;update();}
QColor getColor()const{return polygonColor;}
const PointContainer & getPoints()const{return points;}
TriangleContainer & getTriangles(){return triangles;}
const TriangleContainer & getTriangles()const{return triangles;}
const ArcContainer & getArcs()const{return arcs;}
void addPoint(Point &&p){
points.push_back(p);
}
void addPoint(const Point &p){
points.push_back(p);
}
void removePoint(Triangle::index_type index);
void removePoint(PointContainer::const_iterator index);
void addTriangle(Triangle::index_type first, Triangle::index_type second, Triangle::index_type third);
void addArc(Arc::index_type left, Arc::index_type mid, Arc::index_type right);
protected:
Triangle::index_type createPoint(Point );
void addRectangle(Point,Point,Point,Point);
void addRectangle(Point::number_type x1,Point::number_type y1,Point::number_type x2,Point::number_type y2,Point::number_type x3,Point::number_type y3,Point::number_type x4,Point::number_type y4){
addRectangle(Point(x1,y1),Point(x2,y2),Point(x3,y3),Point(x4,y4));
}
void addRectangle(Point::number_type leftX,Point::number_type rightX,Point::number_type topY,Point::number_type bottomY){
addRectangle(Point(leftX,topY),Point(rightX,topY),Point(leftX,bottomY),Point(rightX,bottomY));
}
void addArc(Point::number_type lx,Point::number_type ly,Point::number_type mx,Point::number_type my,Point::number_type rx,Point::number_type ry){
addArc(createPoint(Point(lx,ly)),createPoint(Point(mx,my)),createPoint(Point(rx,ry)));
}
public:
QSGNode * updatePaintNode(QSGNode *, UpdatePaintNodeData *)override;
};
#endif // POLYGON_H
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment