--- a/src/layers/edittools.cpp Fri Jul 01 23:48:27 2022 +0300 +++ b/src/layers/edittools.cpp Fri Jul 01 23:51:16 2022 +0300 @@ -84,6 +84,7 @@ void EditTools::mouseMoved(const QMouseEvent* event) { this->worldPosition = this->renderer->screenToModelCoordinates(event->pos(), this->gridPlane); + this->localPosition = event->localPos(); if (this->worldPosition.has_value()) { // Snap the position to grid. This procedure is basically the "change of basis" and almost follows the @@ -99,10 +100,7 @@ // grid matrix. this->worldPosition = this->gridMatrix * glm::vec4{*this->worldPosition, 1}; this->polygon.back() = *this->worldPosition; - } - this->numpoints = this->polygon.size(); - if (this->isCloseToExistingPoints()) { - this->numpoints -= 1; + this->numpoints = this->calcNumPoints(); } } @@ -253,6 +251,11 @@ } } +constexpr QPointF vecToQPoint(const glm::vec2& a) +{ + return {a.x, a.y}; +} + void EditTools::renderPreview(QPainter* painter, const void* pensptr) { const Pens& pens = *reinterpret_cast<const Pens*>(pensptr); @@ -278,12 +281,43 @@ for (const glm::vec3& point : this->polygon) { drawWorldPoint(painter, point, this->renderer); } + if (this->mode == CircleMode and this->polygon.size() >= 2) { + const glm::vec3 circleOrigin = this->polygon[0]; + const QPointF originScreen = this->renderer->modelToScreenCoordinates(circleOrigin); + const auto extremity = [this, &originScreen](const glm::vec3& p){ + const QPointF s2 = this->renderer->modelToScreenCoordinates(p); + const auto intersection = rayRectangleIntersection( + rayFromPoints(toVec2(originScreen), toVec2(s2)), + this->renderer->rect()); + if (intersection.has_value()) { + return intersection->position; + } + else { + return glm::vec2{s2.x(), s2.y()}; + } + }; + const glm::vec3 zvec = this->gridMatrix[2]; + const glm::vec2 p1 = extremity(this->polygon[0] + zvec); + const glm::vec2 p2 = extremity(this->polygon[0] - zvec); + const glm::vec2 lateral = glm::normalize(glm::mat2{{0, 1}, {-1, 0}} * (p2 - p1)); + painter->setPen(QPen{Qt::white, 3}); + painter->drawLine(vecToQPoint(p1), vecToQPoint(p2)); + constexpr float notchsize = 40.0f; + for (int a = -30; a <= 30; ++a) { + const glm::vec3 notch = this->polygon[0] + static_cast<float>(a) * zvec; + const QPointF s_notchcenter = this->renderer->modelToScreenCoordinates(notch); + const QPointF notch_s1 = s_notchcenter + notchsize * 0.5f * vecToQPoint(lateral); + const QPointF notch_s2 = s_notchcenter - notchsize * 0.5f * vecToQPoint(lateral); + painter->drawLine(notch_s1, notch_s2); + } + } } void EditTools::removeLastPoint() { if (this->polygon.size() > 1) { this->polygon.erase(this->polygon.end() - 2); + this->numpoints = this->calcNumPoints(); } } @@ -300,6 +334,15 @@ } } +std::size_t EditTools::calcNumPoints() const +{ + std::size_t result = this->polygon.size(); + if (this->isCloseToExistingPoints()) { + result -= 1; + } + return result; +} + EditingMode EditTools::currentEditingMode() const { return this->mode; @@ -326,7 +369,7 @@ break; case CircleMode: if (event->button() == Qt::LeftButton and this->worldPosition.has_value()) { - if (this->polygon.size() == 2) { + if (this->polygon.size() == 3) { this->closeShape(); } else { @@ -406,23 +449,39 @@ const std::vector<ModelAction> EditTools::circleModeActions() const { std::vector<ModelAction> result; - if (this->numpoints == 2) { + if (this->numpoints == 3) { const glm::vec3 x = polygon[1] - polygon[0]; - glm::mat4 transform{ - glm::vec4{x, 0}, - this->gridMatrix[2], - glm::vec4{glm::cross(glm::vec3{-this->gridMatrix[2]}, x), 0}, - glm::vec4{this->polygon[0], 1}, - }; - Colored<CircularPrimitive> circ{ - CircularPrimitive{ - .type = this->circleToolOptions.type, - .fraction = this->circleToolOptions.fraction, - .transformation = transform, - }, - MAIN_COLOR - }; - result.push_back(AppendToModel{.newElement = circ}); + const opt<float> cyliheight = [&]() -> opt<float> { + const Plane plane{ + .normal = glm::normalize(this->renderer->cameraVector(this->localPosition)), + .anchor = this->polygon[0], + }; + const opt<glm::vec3> p = this->renderer->screenToModelCoordinates(this->localPosition, plane); + if (p.has_value()) { + const glm::vec3 heightvec = glm::normalize(glm::vec3{gridMatrix[2]}); + return std::round(glm::dot(*p - polygon[0], heightvec)); + } + else { + return {}; + } + }(); + if (cyliheight.has_value()) { + glm::mat4 transform{ + glm::vec4{x, 0}, + *cyliheight * this->gridMatrix[2], + glm::vec4{glm::cross(glm::vec3{-this->gridMatrix[2]}, x), 0}, + glm::vec4{this->polygon[0], 1}, + }; + Colored<CircularPrimitive> circ{ + CircularPrimitive{ + .type = this->circleToolOptions.type, + .fraction = this->circleToolOptions.fraction, + .transformation = transform, + }, + MAIN_COLOR + }; + result.push_back(AppendToModel{.newElement = circ}); + } } return result; }