// Unnamed CAD Software // // License: GPLv3, see LICENSE.txt // Language: C++17 // Notes: // - use a right-handed, Z-up coordinate system to match OpenCASCADE #include "Snapping.h" #include "ViewportWidget.h" #include "Camera.h" #include "Document.h" #include "SketchFeature.h" #include "SketchLine.h" #include "SketchRectangle.h" #include "SketchObject.h" #include "ApplicationController.h" #include #include #include Snapping::Snapping(ViewportWidget* viewport) : m_viewport(viewport) { } bool Snapping::update(const QPoint& mousePos) { bool oldIsSnappingOrigin = m_isSnappingOrigin; bool oldIsSnappingVertex = m_isSnappingVertex; bool shouldSnap = false; if (m_viewport->currentPlane() != ViewportWidget::SketchPlane::NONE && m_viewport->activeTool() != static_cast(ApplicationController::ToolType::None)) { QVector3D worldPos = m_viewport->unproject(mousePos, m_viewport->currentPlane()); const float snapRectHalfSize = 0.0075f * -m_viewport->camera()->zoom(); switch (m_viewport->currentPlane()) { case ViewportWidget::SketchPlane::XY: shouldSnap = qAbs(worldPos.x()) < snapRectHalfSize && qAbs(worldPos.y()) < snapRectHalfSize; break; case ViewportWidget::SketchPlane::XZ: shouldSnap = qAbs(worldPos.x()) < snapRectHalfSize && qAbs(worldPos.z()) < snapRectHalfSize; break; case ViewportWidget::SketchPlane::YZ: shouldSnap = qAbs(worldPos.y()) < snapRectHalfSize && qAbs(worldPos.z()) < snapRectHalfSize; break; case ViewportWidget::SketchPlane::NONE: break; } } m_isSnappingOrigin = shouldSnap; if (m_isSnappingOrigin) { m_isSnappingVertex = false; } m_isSnappingVertex = false; if (!m_isSnappingOrigin && m_viewport->document() && m_viewport->currentPlane() != ViewportWidget::SketchPlane::NONE && m_viewport->activeTool() != static_cast(ApplicationController::ToolType::None)) { QVector3D worldPos = m_viewport->unproject(mousePos, m_viewport->currentPlane()); const float snapRectHalfSize = 0.0075f * -m_viewport->camera()->zoom(); for (Feature* feature : m_viewport->document()->features()) { if (auto sketch = dynamic_cast(feature)) { for (const auto& obj : sketch->objects()) { if (obj->type() == SketchObject::ObjectType::Line) { auto line = static_cast(obj); const gp_Pnt vertices[] = {line->startPoint(), line->endPoint()}; for (const auto& vertex : vertices) { bool isClose = false; switch (m_viewport->currentPlane()) { case ViewportWidget::SketchPlane::XY: isClose = qAbs(worldPos.x() - vertex.X()) < snapRectHalfSize && qAbs(worldPos.y() - vertex.Y()) < snapRectHalfSize; break; case ViewportWidget::SketchPlane::XZ: isClose = qAbs(worldPos.x() - vertex.X()) < snapRectHalfSize && qAbs(worldPos.z() - vertex.Z()) < snapRectHalfSize; break; case ViewportWidget::SketchPlane::YZ: isClose = qAbs(worldPos.y() - vertex.Y()) < snapRectHalfSize && qAbs(worldPos.z() - vertex.Z()) < snapRectHalfSize; break; case ViewportWidget::SketchPlane::NONE: break; } if (isClose) { m_isSnappingVertex = true; m_snapVertex = vertex; goto end_snap_check; } } } else if (obj->type() == SketchObject::ObjectType::Rectangle) { auto rect = static_cast(obj); const auto& p1 = rect->corner1(); const auto& p3 = rect->corner2(); gp_Pnt p2, p4; if (sketch->plane() == SketchFeature::SketchPlane::XY) { p2.SetCoord(p3.X(), p1.Y(), p1.Z()); p4.SetCoord(p1.X(), p3.Y(), p1.Z()); } else if (sketch->plane() == SketchFeature::SketchPlane::XZ) { p2.SetCoord(p3.X(), p1.Y(), p1.Z()); p4.SetCoord(p1.X(), p1.Y(), p3.Z()); } else if (sketch->plane() == SketchFeature::SketchPlane::YZ) { p2.SetCoord(p1.X(), p3.Y(), p1.Z()); p4.SetCoord(p1.X(), p1.Y(), p3.Z()); } const gp_Pnt vertices[] = {p1, p2, p3, p4}; for (const auto& vertex : vertices) { bool isClose = false; switch (m_viewport->currentPlane()) { case ViewportWidget::SketchPlane::XY: isClose = qAbs(worldPos.x() - vertex.X()) < snapRectHalfSize && qAbs(worldPos.y() - vertex.Y()) < snapRectHalfSize; break; case ViewportWidget::SketchPlane::XZ: isClose = qAbs(worldPos.x() - vertex.X()) < snapRectHalfSize && qAbs(worldPos.z() - vertex.Z()) < snapRectHalfSize; break; case ViewportWidget::SketchPlane::YZ: isClose = qAbs(worldPos.y() - vertex.Y()) < snapRectHalfSize && qAbs(worldPos.z() - vertex.Z()) < snapRectHalfSize; break; case ViewportWidget::SketchPlane::NONE: break; } if (isClose) { m_isSnappingVertex = true; m_snapVertex = vertex; goto end_snap_check; } } } } } } end_snap_check:; } return (oldIsSnappingOrigin != m_isSnappingOrigin) || (oldIsSnappingVertex != m_isSnappingVertex); } void Snapping::paintGL() const { if (!m_isSnappingOrigin && !m_isSnappingVertex) { return; } QVector vertices; if (m_isSnappingOrigin) { const float rectSize = 0.0075f * -m_viewport->camera()->zoom(); if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { vertices << -rectSize << -rectSize << 0 << rectSize << -rectSize << 0; vertices << rectSize << -rectSize << 0 << rectSize << rectSize << 0; vertices << rectSize << rectSize << 0 << -rectSize << rectSize << 0; vertices << -rectSize << rectSize << 0 << -rectSize << -rectSize << 0; } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { vertices << -rectSize << 0 << -rectSize << rectSize << 0 << -rectSize; vertices << rectSize << 0 << -rectSize << rectSize << 0 << rectSize; vertices << rectSize << 0 << rectSize << -rectSize << 0 << rectSize; vertices << -rectSize << 0 << rectSize << -rectSize << 0 << -rectSize; } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) { vertices << 0 << -rectSize << -rectSize << 0 << rectSize << -rectSize; vertices << 0 << rectSize << -rectSize << 0 << rectSize << rectSize; vertices << 0 << rectSize << rectSize << 0 << -rectSize << rectSize; vertices << 0 << -rectSize << rectSize << 0 << -rectSize << -rectSize; } } else if (m_isSnappingVertex) { const float rectSize = 0.0075f * -m_viewport->camera()->zoom(); const auto& v = m_snapVertex; if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) { vertices << v.X() - rectSize << v.Y() - rectSize << v.Z() << v.X() + rectSize << v.Y() - rectSize << v.Z(); vertices << v.X() + rectSize << v.Y() - rectSize << v.Z() << v.X() + rectSize << v.Y() + rectSize << v.Z(); vertices << v.X() + rectSize << v.Y() + rectSize << v.Z() << v.X() - rectSize << v.Y() + rectSize << v.Z(); vertices << v.X() - rectSize << v.Y() + rectSize << v.Z() << v.X() - rectSize << v.Y() - rectSize << v.Z(); } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) { vertices << v.X() - rectSize << v.Y() << v.Z() - rectSize << v.X() + rectSize << v.Y() << v.Z() - rectSize; vertices << v.X() + rectSize << v.Y() << v.Z() - rectSize << v.X() + rectSize << v.Y() << v.Z() + rectSize; vertices << v.X() + rectSize << v.Y() << v.Z() + rectSize << v.X() - rectSize << v.Y() << v.Z() + rectSize; vertices << v.X() - rectSize << v.Y() << v.Z() + rectSize << v.X() - rectSize << v.Y() << v.Z() - rectSize; } else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) { vertices << v.X() << v.Y() - rectSize << v.Z() - rectSize << v.X() << v.Y() + rectSize << v.Z() - rectSize; vertices << v.X() << v.Y() + rectSize << v.Z() - rectSize << v.X() << v.Y() + rectSize << v.Z() + rectSize; vertices << v.X() << v.Y() + rectSize << v.Z() + rectSize << v.X() << v.Y() - rectSize << v.Z() + rectSize; vertices << v.X() << v.Y() - rectSize << v.Z() + rectSize << v.X() << v.Y() - rectSize << v.Z() - rectSize; } } m_viewport->shaderProgram()->setUniformValue(m_viewport->colorLoc(), QVector4D(1.0f, 1.0f, 0.0f, 0.5f)); m_viewport->vbo().bind(); m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDrawArrays(GL_LINES, 0, vertices.size() / 3); glDisable(GL_BLEND); }