#ifndef PATH_H #define PATH_H #include #include #include "../gl/GLHelper.h" #include "../gl/GLTriangles.h" #include "../Renderable.h" class Path : public Renderable { private: GLTriangles lines; /** path's width (in meter) */ float width; /** the path's color */ QVector4D color; public: /** ctor */ Path() { setColor(QVector4D(0.0, 0.4, 1.0, 0.6)); setWidth(0.8); } void initGL() override { loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentLine.glsl"); //loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTexSimple.glsl"); lines.setDiffuse(":/res/gl/tex/arrows.png"); program.setUniformValue("texNormalMap", 0); } /** set the path's color */ void setColor(const QVector4D& color) { this->color = color; } /** set the path's color */ void setColor(const QColor& color) { setColor(QVector4D(color.redF(), color.greenF(), color.blueF(), color.alphaF())); } /** set the path's width (in meter) */ void setWidth(const float width) { this->width = width; } /** render the floor */ void _render(const RenderParams& params) override { (void) params; program.setUniformValue("color", color); //glEnable(GL_BLEND); glDisable(GL_CULL_FACE); //glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); lines.render(&program); //glDisable(GL_BLEND); glEnable(GL_CULL_FACE); } template void set(const DijkstraPath& path) { std::vector out; for (const DijkstraNode* node : path.getVector()) { if (!node) {break;} const Node* elem = node->element; out.push_back(Point3(elem->x_cm/100.0f, elem->y_cm/100.0f, elem->z_cm/100.0f)); } set(out); } /** combine nodes while the direction stays the same (many small quads -> one large quad) */ std::vector simplify(const std::vector& path) { // copy std::vector out = path; // remove unneccesary nodes for (int i = 1; i < (int) out.size() - 1; ++i) { const Point3 pa = out[i-1]; const Point3 pb = out[i-0]; const Point3 pc = out[i+1]; // same direction as last segment? combine segments! const float dir1 = std::atan2(pb.y-pa.y, pb.x-pa.x); // last edge const float dir2 = std::atan2(pc.y-pb.y, pc.x-pb.x); // next edge const bool isSameDir = std::abs(dir1-dir2) < 0.03; // last-edge and next-edge have (approx) the same direction? if (isSameDir) {out.erase(out.begin()+i); --i; continue;} // no additional information! remove the center node // too many changes in a small space? -> remove some! const float d1 = pb.getDistance(pa); // distance to last node const float d2 = pb.getDistance(pc); // distance to next node const float min = 0.8; const bool isPackedChange = d1 < min && d2 < min; // both distances below a threshold? if (isPackedChange) {out.erase(out.begin()+i); --i; continue;} // -> many changes in a small area -> remove current node! } return out; } /** MUST BE CALLED FROM THE MAIN THREAD */ void set(const std::vector& _path) { std::vector path = simplify(_path); // reset lines.clear(); // half the width of the path const float s = this->width * 0.5; std::vector quads; for (int i = 1; i < (int) path.size(); ++i) { Point3 pa = path[i-1]; Point3 pb = path[i-0]; const Point3 pc(0, 0, 1); Point3 dir = (pb - pa).normalized(); // produce a small gap between path-segments // those segments will be smoothly connected using another quad pa += dir * 0.35; pb -= dir * 0.35; const Point3 perb = cross(pa-pb, pc).normalized(); // quad's edges const Point3 p1 = pa - perb*s; const Point3 p2 = pa + perb*s; const Point3 p3 = pb + perb*s; const Point3 p4 = pb - perb*s; // add quads.push_back(Floorplan::Quad3(p1,p2,p3,p4)); } float l1 = 0; float l2 = 0; for (int i = 0; i < (int) quads.size(); ++i) { // add the line-segment const Floorplan::Quad3 q1 = quads[i]; l2 += ((q1.p1 + q1.p2) / 2).getDistance( (q1.p3 + q1.p4) / 2 ); addQuad(q1, l1, l2); l1 = l2; // done? if (i == (int) quads.size() - 1) {break;} // construct the quad between adjacent segments const Floorplan::Quad3 q2 = quads[i+1]; const Floorplan::Quad3 q3(q1.p4, q1.p3, q2.p2, q2.p1); l2 += ((q3.p1 + q3.p2) / 2).getDistance( (q3.p3 + q3.p4) / 2 ); addQuad(q3, l1, l2); l1 = l2; } lines.rebuild(); } private: void addQuad(const Floorplan::Quad3& q, const float l1, const float l2) { // move the path upwards (slightly above the ground) const float h = 0.40; const QVector3D v1(q.p1.x, q.p1.z+h, q.p1.y); const QVector3D v2(q.p2.x, q.p2.z+h, q.p2.y); const QVector3D v3(q.p3.x, q.p3.z+h, q.p3.y); const QVector3D v4(q.p4.x, q.p4.z+h, q.p4.y); const QVector3D n(0,1,0); const QVector2D tex1(0, l1); const QVector2D tex2(1, l1); const QVector2D tex3(1, l2); const QVector2D tex4(0, l2); // const QVector2D tex1(q.p1.x, q.p1.y); // const QVector2D tex2(q.p2.x, q.p2.y); // const QVector2D tex3(q.p3.x, q.p3.y); // const QVector2D tex4(q.p4.x, q.p4.y); const VertNormTex vnt1(v1, n, tex1); const VertNormTex vnt2(v2, n, tex2); const VertNormTex vnt3(v3, n, tex3); const VertNormTex vnt4(v4, n, tex4); lines.addQuadCCW(vnt1, vnt2, vnt3, vnt4); } }; #endif // PATH_H