# HG changeset patch # User Teemu Piippo # Date 1583524410 -7200 # Node ID 198d25fe4e215b09eca1b1174bb7caf4e6bd5a58 # Parent f21b800b02a4e3b30d439e20e29cd344ce419c1f show axis directions on the screen diff -r f21b800b02a4 -r 198d25fe4e21 src/geometry.cpp --- a/src/geometry.cpp Fri Mar 06 20:13:10 2020 +0200 +++ b/src/geometry.cpp Fri Mar 06 21:53:30 2020 +0200 @@ -2,23 +2,15 @@ #include "geometry.h" /** - * @brief Computes a line from two points - * @param point_1 - * @param point_2 - * @return line - */ -geom::Line geom::lineFromPoints(const glm::vec3& point_1, const glm::vec3 point_2) -{ - return {point_2 - point_1, point_1}; -} - -/** * @brief Computes line-plane intersection * @param line * @param plane * @return point of intersection. Does not return a value if the line is in parallel to the plane. */ -std::optional geom::linePlaneIntersection(const geom::Line& line, const geom::Plane& plane, const float epsilon) +std::optional geom::linePlaneIntersection( + const geom::Line<3>& line, + const geom::Plane& plane, + const float epsilon) { const float denominator = glm::dot(line.direction, plane.normal); if (std::abs(denominator) < epsilon) @@ -82,3 +74,123 @@ }; return glm::vec3{component(matrix, 0), component(matrix, 1), component(matrix, 2)}; } + +std::optional geom::lineLineIntersection(const Line<2>& line_1, const Line<2>& line_2) +{ + const float denominator = (line_1.direction.x * line_2.direction.y) - (line_1.direction.y * line_2.direction.x); + constexpr float epsilon = 1e-6f; + if (std::abs(denominator) < epsilon) + { + return {}; + } + else + { + const glm::vec2 p1 = line_1.anchor + line_1.direction; + const glm::vec2& p2 = line_1.anchor; + const glm::vec2 p3 = line_2.anchor + line_2.direction; + const glm::vec2& p4 = line_2.anchor; + const float a = glm::determinant(glm::mat2{{p1.x, p2.x}, {p1.y, p2.y}}); + const float b = glm::determinant(glm::mat2{{p3.x, p4.x}, {p3.y, p4.y}}); + const float c_x = glm::determinant(glm::mat2{{p1.x, p2.x}, {1, 1}}); + const float c_y = glm::determinant(glm::mat2{{p1.y, p2.y}, {1, 1}}); + const float d_x = glm::determinant(glm::mat2{{p3.x, p4.x}, {1, 1}}); + const float d_y = glm::determinant(glm::mat2{{p3.y, p4.y}, {1, 1}}); + const float x = glm::determinant(glm::mat2{{a, b}, {c_x, d_x}}); + const float y = glm::determinant(glm::mat2{{a, b}, {c_y, d_y}}); + return glm::vec2{x / denominator, y / denominator}; + } +} + +std::optional geom::rayLineSegmentIntersection(const Ray<2>& ray, const LineSegment2D& line) +{ + std::optional result = lineLineIntersection(rayToLine(ray), lineFromPoints(line.points[0], line.points[1])); + if (result.has_value()) + { + const float d1 = glm::dot(*result - ray.anchor, ray.direction); + if (d1 < 0) + { + result.reset(); + } + else + { + const float d2 = glm::dot(*result - line.points[0], *result - line.points[1]); + if (d2 > 0) + { + result.reset(); + } + } + } + return result; +} + +std::optional geom::rayRectangleIntersection(const Ray<2>& ray, const QRectF& rectangle) +{ + std::optional position; + std::optional result; + // Try top + position = rayLineSegmentIntersection(ray, top(rectangle)); + if (position.has_value()) + { + result = {*position, RectangleSide::Top}; + } + else + { + // Try bottom + position = rayLineSegmentIntersection(ray, bottom(rectangle)); + if (position.has_value()) + { + result = {*position, RectangleSide::Bottom}; + } + else + { + // Try left + position = rayLineSegmentIntersection(ray, left(rectangle)); + if (position.has_value()) + { + result = {*position, RectangleSide::Left}; + } + else + { + // Try right + position = rayLineSegmentIntersection(ray, right(rectangle)); + if (position.has_value()) + { + result = {*position, RectangleSide::Right}; + } + } + } + } + return result; +} + +geom::LineSegment2D geom::top(const QRectF& rectangle) +{ + return { + glm::vec2{rectangle.left(), rectangle.top()}, + glm::vec2{rectangle.right(), rectangle.top()} + }; +} + +geom::LineSegment2D geom::bottom(const QRectF& rectangle) +{ + return { + glm::vec2{rectangle.left(), rectangle.bottom()}, + glm::vec2{rectangle.right(), rectangle.bottom()} + }; +} + +geom::LineSegment2D geom::left(const QRectF& rectangle) +{ + return { + glm::vec2{rectangle.left(), rectangle.top()}, + glm::vec2{rectangle.left(), rectangle.bottom()} + }; +} + +geom::LineSegment2D geom::right(const QRectF& rectangle) +{ + return { + glm::vec2{rectangle.right(), rectangle.top()}, + glm::vec2{rectangle.right(), rectangle.bottom()} + }; +} diff -r f21b800b02a4 -r 198d25fe4e21 src/geometry.h --- a/src/geometry.h Fri Mar 06 20:13:10 2020 +0200 +++ b/src/geometry.h Fri Mar 06 21:53:30 2020 +0200 @@ -9,10 +9,18 @@ glm::vec3 anchor; }; + template struct Line { - glm::vec3 direction; - glm::vec3 anchor; + glm::vec direction; + glm::vec anchor; + }; + + template + struct Ray + { + glm::vec direction; + glm::vec anchor; }; template @@ -21,17 +29,68 @@ glm::vec3 points[N]; }; + template + struct Polygon2D + { + glm::vec2 points[N]; + }; + inline const glm::vec3 origin = {0, 0, 0}; inline const Plane XY = {{0, 0, 1}, origin}; inline const Plane XZ = {{0, 1, 0}, origin}; inline const Plane YZ = {{1, 0, 0}, origin}; using Triangle = Polygon<3>; + using LineSegment2D = Polygon2D<2>; - Line lineFromPoints(const glm::vec3& point_1, const glm::vec3 point_2); + /** + * @brief Computes a line from two points + * @param point_1 + * @param point_2 + * @return line + */ + template + Line lineFromPoints(const glm::vec& point_1, const glm::vec& point_2) + { + return {point_2 - point_1, point_1}; + } + + template + Ray rayFromPoints(const glm::vec& point_1, const glm::vec& point_2) + { + return {point_2 - point_1, point_1}; + } + + template + Line rayToLine(const Ray& ray) + { + return {ray.direction, ray.anchor}; + } + + enum class RectangleSide + { + Top, + Left, + Bottom, + Right + }; + + struct PointOnRectagle + { + glm::vec2 position; + RectangleSide side; + }; + + std::optional lineLineIntersection(const Line<2>& line_1, const Line<2>& line_2); + std::optional rayLineSegmentIntersection(const Ray<2>& ray, const LineSegment2D& line); + std::optional rayRectangleIntersection(const Ray<2>& ray, const QRectF& rectangle); Plane planeFromTriangle(const Triangle& triangle); glm::vec3 normalVector(const Triangle& triangle); - std::optional linePlaneIntersection(const Line& line, const Plane& plane, const float epsilon = 1e-6f); + std::optional linePlaneIntersection(const Line<3>& line, const Plane& plane, const float epsilon = 1e-6f); glm::vec3 scalingVector(const glm::mat4 matrix); + LineSegment2D top(const QRectF& rectangle); + LineSegment2D bottom(const QRectF& rectangle); + LineSegment2D left(const QRectF& rectangle); + LineSegment2D right(const QRectF& rectangle); struct ScalingExtract { glm::vec3 scaling; diff -r f21b800b02a4 -r 198d25fe4e21 src/gl/partrenderer.cpp --- a/src/gl/partrenderer.cpp Fri Mar 06 20:13:10 2020 +0200 +++ b/src/gl/partrenderer.cpp Fri Mar 06 21:53:30 2020 +0200 @@ -309,7 +309,7 @@ return toQPointF(glm::vec2{projected.x, this->height() - projected.y}); } -geom::Line PartRenderer::cameraLine(const QPoint& point) const +geom::Line<3> PartRenderer::cameraLine(const QPoint& point) const { const glm::vec3 p1 = this->unproject({point.x(), point.y(), 0}); const glm::vec3 p2 = this->unproject({point.x(), point.y(), 1}); diff -r f21b800b02a4 -r 198d25fe4e21 src/gl/partrenderer.h --- a/src/gl/partrenderer.h Fri Mar 06 20:13:10 2020 +0200 +++ b/src/gl/partrenderer.h Fri Mar 06 21:53:30 2020 +0200 @@ -36,7 +36,7 @@ ldraw::Id highlighted = ldraw::NULL_ID; std::optional screenToModelCoordinates(const QPoint& point, const geom::Plane& plane) const; QPointF modelToScreenCoordinates(const glm::vec3& point) const; - geom::Line cameraLine(const QPoint& point) const; + geom::Line<3> cameraLine(const QPoint& point) const; glm::vec3 unproject(const glm::vec3& win) const; glm::mat4 projectionMatrix; glm::mat4 viewMatrix; diff -r f21b800b02a4 -r 198d25fe4e21 src/ui/canvas.cpp --- a/src/ui/canvas.cpp Fri Mar 06 20:13:10 2020 +0200 +++ b/src/ui/canvas.cpp Fri Mar 06 21:53:30 2020 +0200 @@ -180,6 +180,61 @@ painter.setPen(Qt::white); painter.drawText(pos + QPointF{5, 5}, vectorToString(*this->worldPosition)); } + { + QPainter painter{this}; + QFont font; + //font.setStyle(QFont::StyleItalic); + painter.setFont(font); + QFontMetrics fontMetrics{font}; + const auto renderText = [&](const QString& text, const geom::PointOnRectagle& intersection) + { + QPointF position = toQPointF(intersection.position); + const geom::RectangleSide side = intersection.side; + switch (side) + { + case geom::RectangleSide::Top: + position += QPointF{0, static_cast(fontMetrics.ascent())}; + break; + case geom::RectangleSide::Left: + break; + case geom::RectangleSide::Bottom: + position += QPointF{0, static_cast(-fontMetrics.descent())}; + break; + case geom::RectangleSide::Right: + position += QPointF{static_cast(-fontMetrics.width(text)), 0}; + break; + } + painter.drawText(position, text); + }; + const QRectF box { + QPointF{0, 0}, + QPointF{static_cast(this->width()), static_cast(this->height())} + }; + const QPointF p1 = this->modelToScreenCoordinates(glm::vec3{0, 0, 0}); + + static const struct + { + QString text; + glm::vec3 direction; + } directions[] = + { + {"+𝑥", {1, 0, 0}}, + {"-𝑥", {-1, 0, 0}}, + {"+𝑦", {0, 1, 0}}, + {"-𝑦", {0, -1, 0}}, + {"+𝑧", {0, 0, 1}}, + {"-𝑧", {0, 0, -1}}, + }; + for (const auto& axis : directions) + { + const QPointF x_p = this->modelToScreenCoordinates(axis.direction); + const auto intersection = geom::rayRectangleIntersection(geom::rayFromPoints(toVec2(p1), toVec2(x_p)), box); + if (intersection.has_value()) + { + renderText(axis.text, *intersection); + } + } + } } void Canvas::updateGridMatrix()