Compare commits

21 Commits

Author SHA1 Message Date
tanner 4f1a8b22ac fix: Update ElCLib 2D/3D point conversion API in ViewportWidget
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:36:07 -07:00
tanner 1b1d2eab11 fix: Remove redeclaration of lineDirGp and lineDir2d
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:34:40 -07:00
tanner 6d92349938 fix: Update Open CASCADE point conversion API in ApplicationController
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:33:21 -07:00
tanner aa8127c5f8 fix: Update OpenCASCADE 2D/3D point conversion API in Snapping
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:31:33 -07:00
tanner b75e355698 fix: Update OpenCASCADE API usage for 2D/3D geometry conversion
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:30:13 -07:00
tanner 4cebb8d552 fix: Adapt RectangleTool to updated Open CASCADE ElCLib API
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:25:59 -07:00
tanner d7e9d784cc fix: Correct ElCLib 2D/3D conversion function calls
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:22:31 -07:00
tanner f942a424f3 fix: Correct ElCLib point conversion function names
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:20:07 -07:00
tanner e37959ca89 refactor: Eliminate SketchPlane enum, use gp_Ax2 for all plane operations
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:18:36 -07:00
tanner 64ff7396c2 refactor: Use gp_Ax2 for camera plane orientation
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:09:38 -07:00
tanner 79c78f0601 refactor: Refactor LineTool to use gp_Ax2 for sketch plane operations
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:07:13 -07:00
tanner 96371b0aa6 refactor: Use gp_Ax2 for geometric calculations in RectangleTool
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 16:02:18 -07:00
tanner e363595eb5 refactor: Use gp_Ax2 for all sketch plane calculations in CircleTool
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 15:58:17 -07:00
tanner 228540e095 refactor: Use gp_Ax2 for plane-agnostic snapping calculations and rendering
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 15:54:29 -07:00
tanner e960c30b48 refactor: Use gp_Ax2 to define and draw SketchGrid planes
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 15:51:43 -07:00
tanner 0469853a2c refactor: Adopt gp_Ax2 for sketch plane and geometry definition
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 15:46:26 -07:00
tanner 577fb5f846 refactor: Use gp_Ax2 for sketch plane definition and drawing
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 15:44:13 -07:00
tanner 08904feb1e fix: Resolve type mismatch when adding wire to sketch
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 15:31:45 -07:00
tanner 125e97df50 feat: Create Open CASCADE shapes for sketch geometry
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-03 15:30:01 -07:00
tanner f9cac02e06 Add file headers 2026-02-20 09:28:23 -07:00
tanner 05d35bbd86 Add GPLv3 license 2026-02-20 09:28:11 -07:00
41 changed files with 997 additions and 978 deletions
+2 -2
View File
@@ -7,7 +7,7 @@
On Debian 12:
```bash
$ sudo apt install cmake qt6-base-dev qt6-svg-dev libtbb-dev
$ sudo apt install cmake qt6-base-dev qt6-svg-dev libtbb-dev libfontconfig1-dev
$ sudo apt install libocct-foundation-dev libocct-modeling-data-dev libocct-modeling-algorithms-dev libocct-visualization-dev
$ mkdir build
@@ -22,7 +22,7 @@ $ ./OpenCAD
## License
This program is free and open-source software licensed under the GNU GPLv3. Please see the `LICENSE.txt` file for details.
This program is free and open-source software licensed under the GNU GPLv3 (or later). Please see the `LICENSE.txt` file for details.
That means you have the right to study, change, and distribute the software and source code to anyone and for any purpose as long as you grant the same rights when distributing it.
+45 -17
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -13,6 +13,16 @@
#include "SketchCircle.h"
#include "MainWindow.h"
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <GC_MakeSegment.hxx>
#include <ElCLib.hxx>
#include <gp_Ax2.hxx>
#include <gp_Circ.hxx>
#include <gp_Dir.hxx>
#include <gp_Vec.hxx>
#include <gp_Pnt2d.hxx>
#include <QInputDialog>
#include <QFileDialog>
#include <QMessageBox>
@@ -117,26 +127,12 @@ void ApplicationController::beginSketchCreation()
emit planeSelectionModeStarted();
}
void ApplicationController::onPlaneSelected(ViewportWidget::SketchPlane plane)
void ApplicationController::onPlaneSelected(const gp_Ax2& plane)
{
auto feature = new SketchFeature("Sketch");
m_activeSketch = feature;
switch (plane) {
case ViewportWidget::SketchPlane::XY:
feature->setPlane(SketchFeature::SketchPlane::XY);
break;
case ViewportWidget::SketchPlane::XZ:
feature->setPlane(SketchFeature::SketchPlane::XZ);
break;
case ViewportWidget::SketchPlane::YZ:
feature->setPlane(SketchFeature::SketchPlane::YZ);
break;
case ViewportWidget::SketchPlane::NONE:
delete feature;
m_activeSketch = nullptr;
return;
}
feature->setPlane(plane);
m_document->addFeature(feature);
emit sketchModeStarted(plane);
@@ -146,6 +142,10 @@ void ApplicationController::addLine(const gp_Pnt& start, const gp_Pnt& end)
{
if (m_activeSketch) {
m_activeSketch->addObject(new SketchLine(start, end));
Handle(Geom_TrimmedCurve) segment = GC_MakeSegment(start, end);
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(segment);
m_activeSketch->addShape(edge);
}
}
@@ -153,6 +153,27 @@ void ApplicationController::addRectangle(const gp_Pnt& corner1, const gp_Pnt& co
{
if (m_activeSketch) {
m_activeSketch->addObject(new SketchRectangle(corner1, corner2));
const auto& plane = m_activeSketch->plane();
gp_Pnt2d p1_2d(gp_Vec(plane.Location(), corner1).Dot(plane.XDirection()), gp_Vec(plane.Location(), corner1).Dot(plane.YDirection()));
gp_Pnt2d p3_2d(gp_Vec(plane.Location(), corner2).Dot(plane.XDirection()), gp_Vec(plane.Location(), corner2).Dot(plane.YDirection()));
gp_Pnt2d p2_2d(p3_2d.X(), p1_2d.Y());
gp_Pnt2d p4_2d(p1_2d.X(), p3_2d.Y());
gp_Pnt p2_3d = ElCLib::To3d(plane, p2_2d);
gp_Pnt p4_3d = ElCLib::To3d(plane, p4_2d);
TopoDS_Edge e1 = BRepBuilderAPI_MakeEdge(corner1, p2_3d);
TopoDS_Edge e2 = BRepBuilderAPI_MakeEdge(p2_3d, corner2);
TopoDS_Edge e3 = BRepBuilderAPI_MakeEdge(corner2, p4_3d);
TopoDS_Edge e4 = BRepBuilderAPI_MakeEdge(p4_3d, corner1);
BRepBuilderAPI_MakeWire wireMaker(e1, e2, e3, e4);
if (wireMaker.IsDone()) {
m_activeSketch->addShape(wireMaker.Shape());
}
}
}
@@ -160,6 +181,13 @@ void ApplicationController::addCircle(const gp_Pnt& center, double radius)
{
if (m_activeSketch) {
m_activeSketch->addObject(new SketchCircle(center, radius));
const auto& sketchPlane = m_activeSketch->plane();
gp_Ax2 axis(center, sketchPlane.Direction());
gp_Circ circle(axis, radius);
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(circle);
m_activeSketch->addShape(edge);
}
}
+4 -4
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -9,8 +9,8 @@
#define APPLICATIONCONTROLLER_H
#include <QObject>
#include "ViewportWidget.h" // For SketchPlane enum
#include <gp_Pnt.hxx>
#include <gp_Ax2.hxx>
class Document;
class MainWindow;
@@ -47,12 +47,12 @@ public slots:
bool saveDocumentAs();
void beginSketchCreation();
void onPlaneSelected(ViewportWidget::SketchPlane plane);
void onPlaneSelected(const gp_Ax2& plane);
void endSketch();
signals:
void planeSelectionModeStarted();
void sketchModeStarted(ViewportWidget::SketchPlane plane);
void sketchModeStarted(const gp_Ax2& plane);
void sketchModeEnded();
void currentFileChanged(const QString& path);
void activeToolChanged(ToolType tool);
+24 -20
View File
@@ -1,12 +1,13 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
#include "Camera.h"
#include "ViewportWidget.h"
#include <gp_Ax2.hxx>
#include <gp_Dir.hxx>
#include <QApplication>
#include <QWheelEvent>
#include <QPropertyAnimation>
@@ -202,25 +203,28 @@ void Camera::restoreState()
setZoom(m_savedZoom);
}
void Camera::animateToPlaneView(int plane)
void Camera::animateToPlaneView(const gp_Ax2& plane)
{
float targetXRot = xRotation();
float targetYRot = yRotation();
switch (static_cast<ViewportWidget::SketchPlane>(plane)) {
case ViewportWidget::SketchPlane::XY: // Top view
targetXRot = 90 * 16;
targetYRot = 0;
break;
case ViewportWidget::SketchPlane::XZ: // Front view
targetXRot = 0;
targetYRot = 0;
break;
case ViewportWidget::SketchPlane::YZ: // Right view
targetXRot = 0;
targetYRot = -90 * 16;
break;
case ViewportWidget::SketchPlane::NONE:
break;
const auto& normal = plane.Direction();
QVector3D n(normal.X(), normal.Y(), normal.Z());
QVector3D d;
// This logic preserves the quirky view directions of the original implementation.
// For XZ-like planes (normal is mostly along Y), the view is aligned WITH the plane normal.
// For other planes, it's aligned AGAINST the normal.
if (qAbs(n.y()) > 0.99) {
d = n;
} else {
d = -n;
}
float targetXRot = qRadiansToDegrees(asin(-d.z())) * 16.0f;
float targetYRot;
if (qAbs(d.z()) > 0.9999) { // Top/bottom-like view, Y rotation is arbitrary
targetYRot = 0; // Set to 0 for stability
} else {
targetYRot = qRadiansToDegrees(atan2(d.x(), d.y())) * 16.0f;
}
auto* animGroup = new QParallelAnimationGroup(this);
+4 -2
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -14,6 +14,8 @@
#include <QMouseEvent>
#include <QWheelEvent>
#include <gp_Ax2.hxx>
class Camera : public QObject
{
Q_OBJECT
@@ -46,7 +48,7 @@ public:
void saveState();
void restoreState();
void animateToPlaneView(int plane);
void animateToPlaneView(const gp_Ax2& plane);
void animateRestoreState();
void animateToHomeView();
+48 -54
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -15,6 +15,10 @@
#include <QOpenGLShaderProgram>
#include <cmath>
#include <QtMath>
#include <optional>
#include <gp_Ax2.hxx>
#include <gp_Pnt2d.hxx>
#include <ElCLib.hxx>
CircleTool::CircleTool(ViewportWidget* viewport)
: SketchTool(viewport)
@@ -35,14 +39,18 @@ void CircleTool::activate()
void CircleTool::mousePressEvent(QMouseEvent *event)
{
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
gp_Pnt p;
if (!m_isDefining) {
if (m_viewport->isSnappingOrigin()) {
p.SetCoord(0, 0, 0);
p = plane.Location();
} else if (m_viewport->isSnappingVertex()) {
p = m_viewport->snapVertex();
} else {
QVector3D worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
QVector3D worldPos = m_viewport->unproject(event->pos(), plane);
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
}
m_centerPoint = p;
@@ -70,24 +78,22 @@ void CircleTool::mousePressEvent(QMouseEvent *event)
}
if (diameterFromInput) {
QVector3D mousePos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
QVector3D mousePos = m_viewport->unproject(event->pos(), plane);
QVector3D mouseDir = mousePos - centerPos;
if (mouseDir.lengthSquared() < 1e-9) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
mouseDir = QVector3D(1, 0, 0);
} else { // YZ
mouseDir = QVector3D(0, 1, 0);
}
const auto& xDir = plane.XDirection();
mouseDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
double radius = inputDiameter / 2.0;
worldPos = centerPos + mouseDir.normalized() * radius;
} else {
if (m_viewport->isSnappingOrigin()) {
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
const auto& origin = plane.Location();
worldPos.setX(origin.X()); worldPos.setY(origin.Y()); worldPos.setZ(origin.Z());
} else if (m_viewport->isSnappingVertex()) {
worldPos = QVector3D(m_viewport->snapVertex().X(), m_viewport->snapVertex().Y(), m_viewport->snapVertex().Z());
} else {
worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
worldPos = m_viewport->unproject(event->pos(), plane);
}
}
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
@@ -103,6 +109,10 @@ void CircleTool::mouseMoveEvent(QMouseEvent *event)
void CircleTool::finalizeCreation()
{
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
QVector3D worldPos;
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
@@ -117,19 +127,16 @@ void CircleTool::finalizeCreation()
}
if (diameterFromInput) {
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D mouseDir = mousePos - centerPos;
if (mouseDir.lengthSquared() < 1e-9) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
mouseDir = QVector3D(1, 0, 0);
} else { // YZ
mouseDir = QVector3D(0, 1, 0);
}
const auto& xDir = plane.XDirection();
mouseDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
double radius = inputDiameter / 2.0;
worldPos = centerPos + mouseDir.normalized() * radius;
} else {
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
}
gp_Pnt p;
@@ -141,7 +148,10 @@ void CircleTool::finalizeCreation()
void CircleTool::paintGL()
{
if (m_isDefining) {
auto currentPlaneOpt = m_viewport->currentPlane();
if (!m_isDefining || !currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
QVector<GLfloat> vertices;
QVector3D worldPos;
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
@@ -156,7 +166,7 @@ void CircleTool::paintGL()
if (ok) diameterFromInput = true;
}
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
double radius;
if (diameterFromInput) {
@@ -164,7 +174,8 @@ void CircleTool::paintGL()
} else {
worldPos = mousePos;
if (m_viewport->isSnappingOrigin()) {
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
const auto& origin = plane.Location();
worldPos.setX(origin.X()); worldPos.setY(origin.Y()); worldPos.setZ(origin.Z());
} else if (m_viewport->isSnappingVertex()) {
worldPos.setX(m_viewport->snapVertex().X()); worldPos.setY(m_viewport->snapVertex().Y()); worldPos.setZ(m_viewport->snapVertex().Z());
}
@@ -172,34 +183,18 @@ void CircleTool::paintGL()
}
const int segments = 64;
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
QVector3D u_axis(xDir.X(), xDir.Y(), xDir.Z());
QVector3D v_axis(yDir.X(), yDir.Y(), yDir.Z());
for (int i = 0; i < segments; ++i) {
double angle1 = i * 2.0 * M_PI / segments;
double angle2 = (i + 1) * 2.0 * M_PI / segments;
QVector3D p1, p2;
QVector3D p1 = centerPos + radius * (qCos(angle1) * u_axis + qSin(angle1) * v_axis);
QVector3D p2 = centerPos + radius * (qCos(angle2) * u_axis + qSin(angle2) * v_axis);
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
p1.setX(centerPos.x() + radius * qCos(angle1));
p1.setY(centerPos.y() + radius * qSin(angle1));
p1.setZ(centerPos.z());
p2.setX(centerPos.x() + radius * qCos(angle2));
p2.setY(centerPos.y() + radius * qSin(angle2));
p2.setZ(centerPos.z());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
p1.setX(centerPos.x() + radius * qCos(angle1));
p1.setY(centerPos.y());
p1.setZ(centerPos.z() + radius * qSin(angle1));
p2.setX(centerPos.x() + radius * qCos(angle2));
p2.setY(centerPos.y());
p2.setZ(centerPos.z() + radius * qSin(angle2));
} else { // YZ
p1.setX(centerPos.x());
p1.setY(centerPos.y() + radius * qCos(angle1));
p1.setZ(centerPos.z() + radius * qSin(angle1));
p2.setX(centerPos.x());
p2.setY(centerPos.y() + radius * qCos(angle2));
p2.setZ(centerPos.z() + radius * qSin(angle2));
}
vertices << p1.x() << p1.y() << p1.z();
vertices << p2.x() << p2.y() << p2.z();
}
@@ -208,12 +203,14 @@ void CircleTool::paintGL()
m_viewport->vbo().bind();
m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
glDrawArrays(GL_LINES, 0, segments * 2);
}
}
void CircleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection)
{
if (m_isDefining) {
auto currentPlaneOpt = m_viewport->currentPlane();
if (!m_isDefining || !currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
QVector3D worldPos;
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
@@ -227,7 +224,7 @@ void CircleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const Q
if (ok) diameterFromInput = true;
}
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D edgePos;
double diameter;
@@ -235,17 +232,15 @@ void CircleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const Q
diameter = inputDiameter;
QVector3D mouseDir = mousePos - centerPos;
if (mouseDir.lengthSquared() < 1e-9) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
mouseDir = QVector3D(1, 0, 0);
} else { // YZ
mouseDir = QVector3D(0, 1, 0);
}
const auto& xDir = plane.XDirection();
mouseDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
edgePos = centerPos + mouseDir.normalized() * (diameter / 2.0);
} else {
edgePos = mousePos;
if (m_viewport->isSnappingOrigin()) {
edgePos.setX(0); edgePos.setY(0); edgePos.setZ(0);
const auto& origin = plane.Location();
edgePos.setX(origin.X()); edgePos.setY(origin.Y()); edgePos.setZ(origin.Z());
} else if (m_viewport->isSnappingVertex()) {
edgePos.setX(m_viewport->snapVertex().X()); edgePos.setY(m_viewport->snapVertex().Y()); edgePos.setZ(m_viewport->snapVertex().Z());
}
@@ -270,5 +265,4 @@ void CircleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const Q
painter.setPen(Qt::white);
painter.drawText(textRect, Qt::AlignCenter, diameterText);
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+241 -236
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -13,6 +13,13 @@
#include <QPainter>
#include <QOpenGLShaderProgram>
#include <QtMath>
#include <optional>
#include <gp_Ax2.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Vec2d.hxx>
#include <gp_Vec.hxx>
#include <ElCLib.hxx>
LineTool::LineTool(ViewportWidget* viewport)
: SketchTool(viewport)
@@ -34,6 +41,10 @@ void LineTool::activate()
void LineTool::mousePressEvent(QMouseEvent *event)
{
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
gp_Pnt p;
QString dimInput = m_viewport->property("dimensionInput").toString();
QString angleInput = m_viewport->property("angleInput").toString();
@@ -64,19 +75,23 @@ void LineTool::mousePressEvent(QMouseEvent *event)
if (m_viewport->property("isChainedLine").toBool()) {
refDir = m_viewport->property("previousLineDirection").value<QVector3D>();
} else {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refDir = QVector3D(1, 0, 0);
else refDir = QVector3D(0, 1, 0);
const auto& xDir = plane.XDirection();
refDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
QVector3D currentMouseWorldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
QVector3D currentMouseWorldPos = m_viewport->unproject(event->pos(), plane);
QVector3D mouseVec = currentMouseWorldPos - startPos;
double mouseAngle;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x()));
else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y()));
double refAngle;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
gp_Pnt startPnt(startPos.x(), startPos.y(), startPos.z());
gp_Pnt mousePnt(currentMouseWorldPos.x(), currentMouseWorldPos.y(), currentMouseWorldPos.z());
gp_Dir refDirGp(refDir.x(), refDir.y(), refDir.z());
gp_Pnt2d startPnt2d(gp_Vec(plane.Location(), startPnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), startPnt).Dot(plane.YDirection()));
gp_Pnt2d mousePnt2d(gp_Vec(plane.Location(), mousePnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), mousePnt).Dot(plane.YDirection()));
gp_Dir2d refDir2d(refDirGp.Dot(plane.XDirection()), refDirGp.Dot(plane.YDirection()));
gp_Vec2d mouseVec2d(startPnt2d, mousePnt2d);
double mouseAngle = qRadiansToDegrees(atan2(mouseVec2d.Y(), mouseVec2d.X()));
double refAngle = qRadiansToDegrees(atan2(refDir2d.Y(), refDir2d.X()));
double relativeMouseAngle = mouseAngle - refAngle;
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0;
@@ -91,10 +106,9 @@ void LineTool::mousePressEvent(QMouseEvent *event)
snappedAngle = -inputAngleDegrees;
}
double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle);
QVector3D finalDir;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0);
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad));
else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad));
gp_Dir2d finalDir2d(cos(finalAngleRad), sin(finalAngleRad));
gp_Dir finalDir3d = ElCLib::To3d(plane, finalDir2d);
QVector3D finalDir(finalDir3d.X(), finalDir3d.Y(), finalDir3d.Z());
double lineLength;
if (lengthFromInput) lineLength = inputLength;
else {
@@ -103,34 +117,37 @@ void LineTool::mousePressEvent(QMouseEvent *event)
}
worldPos = startPos + lineLength * finalDir;
} else if (lengthFromInput) {
QVector3D currentMouseWorldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
QVector3D currentMouseWorldPos = m_viewport->unproject(event->pos(), plane);
QVector3D dir = (currentMouseWorldPos - startPos);
if (dir.length() > 1e-6) {
dir.normalize();
worldPos = startPos + inputLength * dir;
} else {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos = startPos + QVector3D(inputLength, 0, 0);
else worldPos = startPos + QVector3D(0, inputLength, 0);
const auto& xDir = plane.XDirection();
worldPos = startPos + inputLength * QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
}
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
} else {
if (m_viewport->isSnappingOrigin()) {
p.SetCoord(0, 0, 0);
p = plane.Location();
} else if (m_viewport->isSnappingVertex()) {
p = m_viewport->snapVertex();
} else {
QVector3D worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
QVector3D worldPosQ = m_viewport->unproject(event->pos(), plane);
gp_Pnt worldPos(worldPosQ.x(), worldPosQ.y(), worldPosQ.z());
if (m_viewport->isSnappingHorizontal() || m_viewport->isSnappingVertical()) {
gp_Pnt2d worldPos2d(gp_Vec(plane.Location(), worldPos).Dot(plane.XDirection()), gp_Vec(plane.Location(), worldPos).Dot(plane.YDirection()));
gp_Pnt2d firstPoint2d(gp_Vec(plane.Location(), m_firstLinePoint).Dot(plane.XDirection()), gp_Vec(plane.Location(), m_firstLinePoint).Dot(plane.YDirection()));
if (m_viewport->isSnappingHorizontal()) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setY(m_firstLinePoint.Y());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setZ(m_firstLinePoint.Z());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) worldPos.setZ(m_firstLinePoint.Z());
} else if (m_viewport->isSnappingVertical()) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setX(m_firstLinePoint.X());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setX(m_firstLinePoint.X());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) worldPos.setY(m_firstLinePoint.Y());
worldPos2d.SetY(firstPoint2d.Y());
} else { // vertical
worldPos2d.SetX(firstPoint2d.X());
}
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
worldPos = ElCLib::To3d(plane, worldPos2d);
}
p = worldPos;
}
}
@@ -157,27 +174,27 @@ void LineTool::mousePressEvent(QMouseEvent *event)
void LineTool::mouseMoveEvent(QMouseEvent *event)
{
auto currentPlaneOpt = m_viewport->currentPlane();
if (!m_isDefining || !currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
bool oldIsSnappingHorizontal = m_viewport->isSnappingHorizontal();
bool oldIsSnappingVertical = m_viewport->isSnappingVertical();
m_viewport->setSnappingHorizontal(false);
m_viewport->setSnappingVertical(false);
if (m_isDefining && !m_viewport->isSnappingOrigin() && !m_viewport->isSnappingVertex()) {
QVector3D worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
QVector3D delta = worldPos - startPos;
QVector3D worldPosQ = m_viewport->unproject(m_viewport->currentMousePos(), plane);
gp_Pnt worldPos(worldPosQ.x(), worldPosQ.y(), worldPosQ.z());
gp_Pnt startPos = m_firstLinePoint;
if (delta.length() > 1e-6) {
gp_Pnt2d worldPos2d(gp_Vec(plane.Location(), worldPos).Dot(plane.XDirection()), gp_Vec(plane.Location(), worldPos).Dot(plane.YDirection()));
gp_Pnt2d startPos2d(gp_Vec(plane.Location(), startPos).Dot(plane.XDirection()), gp_Vec(plane.Location(), startPos).Dot(plane.YDirection()));
gp_Vec2d delta(startPos2d, worldPos2d);
if (delta.Magnitude() > 1e-6) {
const double snapAngleThreshold = qDegreesToRadians(2.0);
double angle = 0;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
angle = atan2(delta.y(), delta.x());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
angle = atan2(delta.z(), delta.x());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) {
angle = atan2(delta.z(), delta.y());
}
double angle = atan2(delta.Y(), delta.X());
if (qAbs(sin(angle)) < sin(snapAngleThreshold)) {
m_viewport->setSnappingHorizontal(true);
@@ -194,6 +211,10 @@ void LineTool::mouseMoveEvent(QMouseEvent *event)
void LineTool::finalizeCreation()
{
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
QVector3D worldPos;
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
// This is duplicated from paintGL to ensure consistent line creation
@@ -220,19 +241,23 @@ void LineTool::finalizeCreation()
if (m_viewport->property("isChainedLine").toBool()) {
refDir = m_viewport->property("previousLineDirection").value<QVector3D>();
} else {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refDir = QVector3D(1, 0, 0);
else refDir = QVector3D(0, 1, 0);
const auto& xDir = plane.XDirection();
refDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D mouseVec = currentMouseWorldPos - startPos;
double mouseAngle;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x()));
else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y()));
double refAngle;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
gp_Pnt startPnt(startPos.x(), startPos.y(), startPos.z());
gp_Pnt mousePnt(currentMouseWorldPos.x(), currentMouseWorldPos.y(), currentMouseWorldPos.z());
gp_Dir refDirGp(refDir.x(), refDir.y(), refDir.z());
gp_Pnt2d startPnt2d(gp_Vec(plane.Location(), startPnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), startPnt).Dot(plane.YDirection()));
gp_Pnt2d mousePnt2d(gp_Vec(plane.Location(), mousePnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), mousePnt).Dot(plane.YDirection()));
gp_Dir2d refDir2d(refDirGp.Dot(plane.XDirection()), refDirGp.Dot(plane.YDirection()));
gp_Vec2d mouseVec2d(startPnt2d, mousePnt2d);
double mouseAngle = qRadiansToDegrees(atan2(mouseVec2d.Y(), mouseVec2d.X()));
double refAngle = qRadiansToDegrees(atan2(refDir2d.Y(), refDir2d.X()));
double relativeMouseAngle = mouseAngle - refAngle;
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0;
@@ -247,10 +272,9 @@ void LineTool::finalizeCreation()
snappedAngle = -inputAngleDegrees;
}
double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle);
QVector3D finalDir;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0);
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad));
else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad));
gp_Dir2d finalDir2d(cos(finalAngleRad), sin(finalAngleRad));
gp_Dir finalDir3d = ElCLib::To3d(plane, finalDir2d);
QVector3D finalDir(finalDir3d.X(), finalDir3d.Y(), finalDir3d.Z());
double lineLength;
if (lengthFromInput) lineLength = inputLength;
else {
@@ -259,17 +283,17 @@ void LineTool::finalizeCreation()
}
worldPos = startPos + lineLength * finalDir;
} else if (lengthFromInput) {
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D dir = (currentMouseWorldPos - startPos);
if (dir.length() > 1e-6) {
dir.normalize();
worldPos = startPos + inputLength * dir;
} else {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos = startPos + QVector3D(inputLength, 0, 0);
else worldPos = startPos + QVector3D(0, inputLength, 0);
const auto& xDir = plane.XDirection();
worldPos = startPos + inputLength * QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
} else {
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
}
gp_Pnt p;
@@ -289,6 +313,10 @@ void LineTool::finalizeCreation()
void LineTool::paintGL()
{
if (m_isDefining) {
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
QVector<GLfloat> vertices;
QVector3D worldPos;
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
@@ -316,26 +344,24 @@ void LineTool::paintGL()
if (m_viewport->property("isChainedLine").toBool()) {
refDir = m_viewport->property("previousLineDirection").value<QVector3D>();
} else {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
refDir = QVector3D(1, 0, 0);
} else { // YZ
refDir = QVector3D(0, 1, 0);
}
const auto& xDir = plane.XDirection();
refDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D mouseVec = currentMouseWorldPos - startPos;
// Quadrant snapping
double mouseAngle;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x()));
else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y()));
gp_Pnt startPnt(startPos.x(), startPos.y(), startPos.z());
gp_Pnt mousePnt(currentMouseWorldPos.x(), currentMouseWorldPos.y(), currentMouseWorldPos.z());
gp_Dir refDirGp(refDir.x(), refDir.y(), refDir.z());
double refAngle;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
gp_Pnt2d startPnt2d(gp_Vec(plane.Location(), startPnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), startPnt).Dot(plane.YDirection()));
gp_Pnt2d mousePnt2d(gp_Vec(plane.Location(), mousePnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), mousePnt).Dot(plane.YDirection()));
gp_Dir2d refDir2d(refDirGp.Dot(plane.XDirection()), refDirGp.Dot(plane.YDirection()));
gp_Vec2d mouseVec2d(startPnt2d, mousePnt2d);
double mouseAngle = qRadiansToDegrees(atan2(mouseVec2d.Y(), mouseVec2d.X()));
double refAngle = qRadiansToDegrees(atan2(refDir2d.Y(), refDir2d.X()));
double relativeMouseAngle = mouseAngle - refAngle;
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
@@ -353,10 +379,9 @@ void LineTool::paintGL()
}
double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle);
QVector3D finalDir;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0);
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad));
else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad));
gp_Dir2d finalDir2d(cos(finalAngleRad), sin(finalAngleRad));
gp_Dir finalDir3d = ElCLib::To3d(plane, finalDir2d);
QVector3D finalDir(finalDir3d.X(), finalDir3d.Y(), finalDir3d.Z());
double lineLength;
if (lengthFromInput) {
@@ -368,33 +393,34 @@ void LineTool::paintGL()
worldPos = startPos + lineLength * finalDir;
} else if (lengthFromInput) {
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D dir = (currentMouseWorldPos - startPos);
if (dir.length() > 1e-6) {
dir.normalize();
worldPos = startPos + inputLength * dir;
} else {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
worldPos = startPos + QVector3D(inputLength, 0, 0);
} else {
worldPos = startPos + QVector3D(0, inputLength, 0);
}
const auto& xDir = plane.XDirection();
worldPos = startPos + inputLength * QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
} else {
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
gp_Pnt worldPosPnt(worldPos.x(), worldPos.y(), worldPos.z());
if (m_viewport->isSnappingOrigin()) {
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
worldPosPnt = plane.Location();
} else if (m_viewport->isSnappingVertex()) {
worldPos.setX(m_viewport->snapVertex().X()); worldPos.setY(m_viewport->snapVertex().Y()); worldPos.setZ(m_viewport->snapVertex().Z());
} else if (m_viewport->isSnappingHorizontal()) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setY(m_firstLinePoint.Y());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setZ(m_firstLinePoint.Z());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) worldPos.setZ(m_firstLinePoint.Z());
} else if (m_viewport->isSnappingVertical()) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setX(m_firstLinePoint.X());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setX(m_firstLinePoint.X());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) worldPos.setY(m_firstLinePoint.Y());
worldPosPnt = m_viewport->snapVertex();
} else if (m_viewport->isSnappingHorizontal() || m_viewport->isSnappingVertical()) {
gp_Pnt2d worldPos2d(gp_Vec(plane.Location(), worldPosPnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), worldPosPnt).Dot(plane.YDirection()));
gp_Pnt2d firstPoint2d(gp_Vec(plane.Location(), m_firstLinePoint).Dot(plane.XDirection()), gp_Vec(plane.Location(), m_firstLinePoint).Dot(plane.YDirection()));
if (m_viewport->isSnappingHorizontal()) {
worldPos2d.SetY(firstPoint2d.Y());
} else { // vertical
worldPos2d.SetX(firstPoint2d.X());
}
worldPosPnt = ElCLib::To3d(plane, worldPos2d);
}
worldPos.setX(worldPosPnt.X()); worldPos.setY(worldPosPnt.Y()); worldPos.setZ(worldPosPnt.Z());
}
vertices << m_firstLinePoint.X() << m_firstLinePoint.Y() << m_firstLinePoint.Z();
@@ -416,23 +442,25 @@ void LineTool::paintGL()
if (m_viewport->property("isChainedLine").toBool()) {
refDir = m_viewport->property("previousLineDirection").value<QVector3D>();
} else {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refDir = QVector3D(1, 0, 0);
else refDir = QVector3D(0, 1, 0);
const auto& xDir = plane.XDirection();
refDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
if (angleFromInput) {
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D mouseVec = currentMouseWorldPos - startPos;
double mouseAngle;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x()));
else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y()));
gp_Pnt startPnt(startPos.x(), startPos.y(), startPos.z());
gp_Pnt mousePnt(currentMouseWorldPos.x(), currentMouseWorldPos.y(), currentMouseWorldPos.z());
gp_Dir refDirGp(refDir.x(), refDir.y(), refDir.z());
double refAngleForQuadrant;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
else refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
gp_Pnt2d startPnt2d(gp_Vec(plane.Location(), startPnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), startPnt).Dot(plane.YDirection()));
gp_Pnt2d mousePnt2d(gp_Vec(plane.Location(), mousePnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), mousePnt).Dot(plane.YDirection()));
gp_Dir2d refDir2d(refDirGp.Dot(plane.XDirection()), refDirGp.Dot(plane.YDirection()));
gp_Vec2d mouseVec2d(startPnt2d, mousePnt2d);
double mouseAngle = qRadiansToDegrees(atan2(mouseVec2d.Y(), mouseVec2d.X()));
double refAngleForQuadrant = qRadiansToDegrees(atan2(refDir2d.Y(), refDir2d.X()));
double relativeMouseAngle = mouseAngle - refAngleForQuadrant;
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
@@ -447,16 +475,14 @@ void LineTool::paintGL()
}
}
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
refAngle = atan2(refDir.y(), refDir.x());
lineAngle = atan2(lineVec.y(), lineVec.x());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
refAngle = atan2(refDir.z(), refDir.x());
lineAngle = atan2(lineVec.z(), lineVec.x());
} else { // YZ
refAngle = atan2(refDir.z(), refDir.y());
lineAngle = atan2(lineVec.z(), lineVec.y());
}
gp_Dir refDirGp(refDir.x(), refDir.y(), refDir.z());
gp_Dir lineDirGp(lineVec.x(), lineVec.y(), lineVec.z());
gp_Dir2d refDir2d(refDirGp.Dot(plane.XDirection()), refDirGp.Dot(plane.YDirection()));
gp_Dir2d lineDir2d(lineDirGp.Dot(plane.XDirection()), lineDirGp.Dot(plane.YDirection()));
refAngle = atan2(refDir2d.Y(), refDir2d.X());
lineAngle = atan2(lineDir2d.Y(), lineDir2d.X());
angleDiff = lineAngle - refAngle;
while (angleDiff <= -M_PI) angleDiff += 2 * M_PI;
@@ -466,14 +492,11 @@ void LineTool::paintGL()
vertices.clear();
QVector3D perpVec;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
perpVec = QVector3D(-lineVec.y(), lineVec.x(), 0).normalized();
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
perpVec = QVector3D(-lineVec.z(), 0, lineVec.x()).normalized();
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) {
perpVec = QVector3D(0, -lineVec.z(), lineVec.y()).normalized();
}
gp_Dir lineDirGp(lineVec.x(), lineVec.y(), lineVec.z());
gp_Dir2d lineDir2d(lineDirGp.Dot(plane.XDirection()), lineDirGp.Dot(plane.YDirection()));
gp_Dir2d perpDir2d(-lineDir2d.Y(), lineDir2d.X());
gp_Dir perpDir3d = ElCLib::To3d(plane, perpDir2d);
QVector3D perpVec(perpDir3d.X(), perpDir3d.Y(), perpDir3d.Z());
if (angleDiff < 0) {
perpVec = -perpVec;
@@ -520,9 +543,11 @@ void LineTool::paintGL()
for (int i = 0; i <= numSegments; ++i) {
double angle = refAngle + (lineAngle - refAngle) * i / numSegments;
QVector3D p;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) p = startPos + radius * QVector3D(cos(angle), sin(angle), 0);
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) p = startPos + radius * QVector3D(cos(angle), 0, sin(angle));
else p = startPos + radius * QVector3D(0, cos(angle), sin(angle));
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
QVector3D u_axis(xDir.X(), xDir.Y(), xDir.Z());
QVector3D v_axis(yDir.X(), yDir.Y(), yDir.Z());
p = startPos + radius * (cos(angle) * u_axis + sin(angle) * v_axis);
vertices << p.x() << p.y() << p.z();
}
glLineWidth(1.0f);
@@ -540,17 +565,13 @@ void LineTool::paintGL()
// End arrowhead
QVector3D endPoint(vertices[vertices.size()-3], vertices[vertices.size()-2], vertices[vertices.size()-1]);
double endAngle = lineAngle;
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
QVector3D u_axis(xDir.X(), xDir.Y(), xDir.Z());
QVector3D v_axis(yDir.X(), yDir.Y(), yDir.Z());
QVector3D radialDir_end, tangentDir_end;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
radialDir_end = QVector3D(cos(endAngle), sin(endAngle), 0);
tangentDir_end = QVector3D(-sin(endAngle), cos(endAngle), 0);
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
radialDir_end = QVector3D(cos(endAngle), 0, sin(endAngle));
tangentDir_end = QVector3D(-sin(endAngle), 0, cos(endAngle));
} else {
radialDir_end = QVector3D(0, cos(endAngle), sin(endAngle));
tangentDir_end = QVector3D(0, -sin(endAngle), cos(endAngle));
}
radialDir_end = cos(endAngle) * u_axis + sin(endAngle) * v_axis;
tangentDir_end = -sin(endAngle) * u_axis + cos(endAngle) * v_axis;
QVector3D arc_arrow_base_end = endPoint - sign * arcArrowLength * tangentDir_end;
QVector3D arc_arrowP1_end = arc_arrow_base_end + arcArrowWidth * radialDir_end;
QVector3D arc_arrowP2_end = arc_arrow_base_end - arcArrowWidth * radialDir_end;
@@ -561,16 +582,8 @@ void LineTool::paintGL()
QVector3D startPoint(vertices[0], vertices[1], vertices[2]);
double startAngle = refAngle;
QVector3D radialDir_start, tangentDir_start;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
radialDir_start = QVector3D(cos(startAngle), sin(startAngle), 0);
tangentDir_start = QVector3D(-sin(startAngle), cos(startAngle), 0);
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
radialDir_start = QVector3D(cos(startAngle), 0, sin(startAngle));
tangentDir_start = QVector3D(-sin(startAngle), 0, cos(startAngle));
} else {
radialDir_start = QVector3D(0, cos(startAngle), sin(startAngle));
tangentDir_start = QVector3D(0, -sin(startAngle), cos(startAngle));
}
radialDir_start = cos(startAngle) * u_axis + sin(startAngle) * v_axis;
tangentDir_start = -sin(startAngle) * u_axis + cos(startAngle) * v_axis;
QVector3D arc_arrow_base_start = startPoint + sign * arcArrowLength * tangentDir_start;
QVector3D arc_arrowP1_start = arc_arrow_base_start + arcArrowWidth * radialDir_start;
QVector3D arc_arrowP2_start = arc_arrow_base_start - arcArrowWidth * radialDir_start;
@@ -589,29 +602,21 @@ void LineTool::paintGL()
QVector3D midPoint = (startPos + worldPos) / 2.0;
const float indicatorSize = 0.02f * -m_viewport->camera()->zoom();
const float indicatorOffset = 0.02f * -m_viewport->camera()->zoom();
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
QVector3D u_axis(xDir.X(), xDir.Y(), xDir.Z());
QVector3D v_axis(yDir.X(), yDir.Y(), yDir.Z());
QVector3D p1, p2;
if (m_viewport->isSnappingHorizontal()) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
vertices << midPoint.x() - indicatorSize << midPoint.y() + indicatorOffset << midPoint.z();
vertices << midPoint.x() + indicatorSize << midPoint.y() + indicatorOffset << midPoint.z();
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
vertices << midPoint.x() - indicatorSize << midPoint.y() << midPoint.z() + indicatorOffset;
vertices << midPoint.x() + indicatorSize << midPoint.y() << midPoint.z() + indicatorOffset;
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) {
vertices << midPoint.x() << midPoint.y() - indicatorSize << midPoint.z() + indicatorOffset;
vertices << midPoint.x() << midPoint.y() + indicatorSize << midPoint.z() + indicatorOffset;
}
p1 = midPoint - indicatorSize * v_axis + indicatorOffset * u_axis;
p2 = midPoint + indicatorSize * v_axis + indicatorOffset * u_axis;
} else { // m_isSnappingVertical
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
vertices << midPoint.x() + indicatorOffset << midPoint.y() - indicatorSize << midPoint.z();
vertices << midPoint.x() + indicatorOffset << midPoint.y() + indicatorSize << midPoint.z();
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
vertices << midPoint.x() + indicatorOffset << midPoint.y() << midPoint.z() - indicatorSize;
vertices << midPoint.x() + indicatorOffset << midPoint.y() << midPoint.z() + indicatorSize;
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) {
vertices << midPoint.x() << midPoint.y() + indicatorOffset << midPoint.z() - indicatorSize;
vertices << midPoint.x() << midPoint.y() + indicatorOffset << midPoint.z() + indicatorSize;
}
p1 = midPoint - indicatorSize * u_axis + indicatorOffset * v_axis;
p2 = midPoint + indicatorSize * u_axis + indicatorOffset * v_axis;
}
vertices << p1.x() << p1.y() << p1.z();
vertices << p2.x() << p2.y() << p2.z();
m_viewport->vbo().bind();
m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
glDrawArrays(GL_LINES, 0, 2);
@@ -622,6 +627,10 @@ void LineTool::paintGL()
void LineTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection)
{
if (m_isDefining) {
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
QVector3D worldPos;
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
QString dimText;
@@ -651,25 +660,24 @@ void LineTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMa
if (m_viewport->property("isChainedLine").toBool()) {
refDir = m_viewport->property("previousLineDirection").value<QVector3D>();
} else {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
refDir = QVector3D(1, 0, 0);
} else { // YZ
refDir = QVector3D(0, 1, 0);
}
const auto& xDir = plane.XDirection();
refDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D mouseVec = currentMouseWorldPos - startPos;
double mouseAngle;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x()));
else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y()));
gp_Pnt startPnt(startPos.x(), startPos.y(), startPos.z());
gp_Pnt mousePnt(currentMouseWorldPos.x(), currentMouseWorldPos.y(), currentMouseWorldPos.z());
gp_Dir refDirGp(refDir.x(), refDir.y(), refDir.z());
double refAngle;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
gp_Pnt2d startPnt2d(gp_Vec(plane.Location(), startPnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), startPnt).Dot(plane.YDirection()));
gp_Pnt2d mousePnt2d(gp_Vec(plane.Location(), mousePnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), mousePnt).Dot(plane.YDirection()));
gp_Dir2d refDir2d(refDirGp.Dot(plane.XDirection()), refDirGp.Dot(plane.YDirection()));
gp_Vec2d mouseVec2d(startPnt2d, mousePnt2d);
double mouseAngle = qRadiansToDegrees(atan2(mouseVec2d.Y(), mouseVec2d.X()));
double refAngle = qRadiansToDegrees(atan2(refDir2d.Y(), refDir2d.X()));
double relativeMouseAngle = mouseAngle - refAngle;
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
@@ -687,10 +695,9 @@ void LineTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMa
}
double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle);
QVector3D finalDir;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0);
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad));
else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad));
gp_Dir2d finalDir2d(cos(finalAngleRad), sin(finalAngleRad));
gp_Dir finalDir3d = ElCLib::To3d(plane, finalDir2d);
QVector3D finalDir(finalDir3d.X(), finalDir3d.Y(), finalDir3d.Z());
if (lengthFromInput) {
lineLength = inputLength;
@@ -702,33 +709,34 @@ void LineTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMa
} else if (lengthFromInput) {
lineLength = inputLength;
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D dir = (currentMouseWorldPos - startPos);
if (dir.length() > 1e-6) {
dir.normalize();
worldPos = startPos + inputLength * dir;
} else {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
worldPos = startPos + QVector3D(inputLength, 0, 0);
} else {
worldPos = startPos + QVector3D(0, inputLength, 0);
}
const auto& xDir = plane.XDirection();
worldPos = startPos + inputLength * QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
} else {
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), plane);
gp_Pnt worldPosPnt(worldPos.x(), worldPos.y(), worldPos.z());
if (m_viewport->isSnappingOrigin()) {
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
worldPosPnt = plane.Location();
} else if (m_viewport->isSnappingVertex()) {
worldPos.setX(m_viewport->snapVertex().X()); worldPos.setY(m_viewport->snapVertex().Y()); worldPos.setZ(m_viewport->snapVertex().Z());
} else if (m_viewport->isSnappingHorizontal()) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setY(m_firstLinePoint.Y());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setZ(m_firstLinePoint.Z());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) worldPos.setZ(m_firstLinePoint.Z());
} else if (m_viewport->isSnappingVertical()) {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setX(m_firstLinePoint.X());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setX(m_firstLinePoint.X());
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) worldPos.setY(m_firstLinePoint.Y());
worldPosPnt = m_viewport->snapVertex();
} else if (m_viewport->isSnappingHorizontal() || m_viewport->isSnappingVertical()) {
gp_Pnt2d worldPos2d(gp_Vec(plane.Location(), worldPosPnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), worldPosPnt).Dot(plane.YDirection()));
gp_Pnt2d firstPoint2d(gp_Vec(plane.Location(), m_firstLinePoint).Dot(plane.XDirection()), gp_Vec(plane.Location(), m_firstLinePoint).Dot(plane.YDirection()));
if (m_viewport->isSnappingHorizontal()) {
worldPos2d.SetY(firstPoint2d.Y());
} else { // vertical
worldPos2d.SetX(firstPoint2d.X());
}
worldPosPnt = ElCLib::To3d(plane, worldPos2d);
}
worldPos.setX(worldPosPnt.X()); worldPos.setY(worldPosPnt.Y()); worldPos.setZ(worldPosPnt.Z());
lineLength = (worldPos - startPos).length();
}
@@ -739,24 +747,26 @@ void LineTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMa
if (m_viewport->property("isChainedLine").toBool()) {
refDir = m_viewport->property("previousLineDirection").value<QVector3D>();
} else {
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY || m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refDir = QVector3D(1, 0, 0);
else refDir = QVector3D(0, 1, 0);
const auto& xDir = plane.XDirection();
refDir = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
}
QVector3D currentMouseWorldPosForText = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D currentMouseWorldPosForText = m_viewport->unproject(m_viewport->currentMousePos(), plane);
QVector3D mouseVecForText = currentMouseWorldPosForText - startPos;
if (angleFromInput) {
if (mouseVecForText.length() > 1e-6) {
double mouseAngle;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.y(), mouseVecForText.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.z(), mouseVecForText.x()));
else mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.z(), mouseVecForText.y()));
gp_Pnt startPnt(startPos.x(), startPos.y(), startPos.z());
gp_Pnt mousePnt(currentMouseWorldPosForText.x(), currentMouseWorldPosForText.y(), currentMouseWorldPosForText.z());
gp_Dir refDirGp(refDir.x(), refDir.y(), refDir.z());
double refAngleForQuadrant;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
else refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
gp_Pnt2d startPnt2d(gp_Vec(plane.Location(), startPnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), startPnt).Dot(plane.YDirection()));
gp_Pnt2d mousePnt2d(gp_Vec(plane.Location(), mousePnt).Dot(plane.XDirection()), gp_Vec(plane.Location(), mousePnt).Dot(plane.YDirection()));
gp_Dir2d refDir2d(refDirGp.Dot(plane.XDirection()), refDirGp.Dot(plane.YDirection()));
gp_Vec2d mouseVec2d(startPnt2d, mousePnt2d);
double mouseAngle = qRadiansToDegrees(atan2(mouseVec2d.Y(), mouseVec2d.X()));
double refAngleForQuadrant = qRadiansToDegrees(atan2(refDir2d.Y(), refDir2d.X()));
double relativeMouseAngle = mouseAngle - refAngleForQuadrant;
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
@@ -772,30 +782,23 @@ void LineTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMa
}
}
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
refAngle = atan2(refDir.y(), refDir.x());
lineAngle = atan2(lineVec.y(), lineVec.x());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
refAngle = atan2(refDir.z(), refDir.x());
lineAngle = atan2(lineVec.z(), lineVec.x());
} else { // YZ
refAngle = atan2(refDir.z(), refDir.y());
lineAngle = atan2(lineVec.z(), lineVec.y());
}
gp_Dir refDirGp(refDir.x(), refDir.y(), refDir.z());
gp_Dir lineDirGp(lineVec.x(), lineVec.y(), lineVec.z());
gp_Dir2d refDir2d(refDirGp.Dot(plane.XDirection()), refDirGp.Dot(plane.YDirection()));
gp_Dir2d lineDir2d(lineDirGp.Dot(plane.XDirection()), lineDirGp.Dot(plane.YDirection()));
refAngle = atan2(refDir2d.Y(), refDir2d.X());
lineAngle = atan2(lineDir2d.Y(), lineDir2d.X());
double angleDiff = lineAngle - refAngle;
while (angleDiff <= -M_PI) angleDiff += 2 * M_PI;
while (angleDiff > M_PI) angleDiff -= 2 * M_PI;
lineAngle = refAngle + angleDiff;
QVector3D perpVec;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
perpVec = QVector3D(-lineVec.y(), lineVec.x(), 0).normalized();
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
perpVec = QVector3D(-lineVec.z(), 0, lineVec.x()).normalized();
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) {
perpVec = QVector3D(0, -lineVec.z(), lineVec.y()).normalized();
}
gp_Dir2d perpDir2d(-lineDir2d.Y(), lineDir2d.X());
gp_Dir perpDir3d = ElCLib::To3d(plane, perpDir2d);
QVector3D perpVec(perpDir3d.X(), perpDir3d.Y(), perpDir3d.Z());
if (angleDiff < 0) {
perpVec = -perpVec;
@@ -838,9 +841,11 @@ void LineTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMa
QVector3D textPos3DAngle;
float textOffset = 0.035f * -m_viewport->camera()->zoom();
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) textPos3DAngle = startPos + (radius + textOffset) * QVector3D(cos(midAngle), sin(midAngle), 0);
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) textPos3DAngle = startPos + (radius + textOffset) * QVector3D(cos(midAngle), 0, sin(midAngle));
else textPos3DAngle = startPos + (radius + textOffset) * QVector3D(0, cos(midAngle), sin(midAngle));
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
QVector3D u_axis(xDir.X(), xDir.Y(), xDir.Z());
QVector3D v_axis(yDir.X(), yDir.Y(), yDir.Z());
textPos3DAngle = startPos + (radius + textOffset) * (cos(midAngle) * u_axis + sin(midAngle) * v_axis);
QVector3D screenPosAngle = m_viewport->project(textPos3DAngle, modelView, projection, m_viewport->rect());
if (screenPosAngle.z() < 1.0f) {
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+143 -179
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -15,6 +15,11 @@
#include <QOpenGLShaderProgram>
#include <cmath>
#include <QtMath>
#include <optional>
#include <gp_Ax2.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Vec.hxx>
#include <ElCLib.hxx>
RectangleTool::RectangleTool(ViewportWidget* viewport)
: SketchTool(viewport)
@@ -35,14 +40,18 @@ void RectangleTool::activate()
void RectangleTool::mousePressEvent(QMouseEvent *event)
{
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
gp_Pnt p;
if (!m_isDefining) {
if (m_viewport->isSnappingOrigin()) {
p.SetCoord(0, 0, 0);
p = plane.Location();
} else if (m_viewport->isSnappingVertex()) {
p = m_viewport->snapVertex();
} else {
QVector3D worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
QVector3D worldPos = m_viewport->unproject(event->pos(), plane);
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
}
m_firstRectanglePoint = p;
@@ -51,8 +60,8 @@ void RectangleTool::mousePressEvent(QMouseEvent *event)
m_viewport->setProperty("heightInput", "");
m_viewport->setProperty("dimensionEditMode", "height");
} else {
QVector3D worldPos;
QVector3D startPos(m_firstRectanglePoint.X(), m_firstRectanglePoint.Y(), m_firstRectanglePoint.Z());
QVector3D worldPosQ;
gp_Pnt startPos = m_firstRectanglePoint;
QString widthInput = m_viewport->property("widthInput").toString();
QString heightInput = m_viewport->property("heightInput").toString();
@@ -71,45 +80,35 @@ void RectangleTool::mousePressEvent(QMouseEvent *event)
}
if (widthFromInput || heightFromInput) {
QVector3D mousePos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
QVector3D mouseDir = mousePos - startPos;
double current_w, current_h;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
current_w = qAbs(mouseDir.x());
current_h = qAbs(mouseDir.y());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
current_w = qAbs(mouseDir.x());
current_h = qAbs(mouseDir.z());
} else { // YZ
current_w = qAbs(mouseDir.y());
current_h = qAbs(mouseDir.z());
}
QVector3D mousePosQ = m_viewport->unproject(event->pos(), plane);
gp_Pnt mousePos(mousePosQ.x(), mousePosQ.y(), mousePosQ.z());
gp_Pnt2d startPos2d(gp_Vec(plane.Location(), startPos).Dot(plane.XDirection()), gp_Vec(plane.Location(), startPos).Dot(plane.YDirection()));
gp_Pnt2d mousePos2d(gp_Vec(plane.Location(), mousePos).Dot(plane.XDirection()), gp_Vec(plane.Location(), mousePos).Dot(plane.YDirection()));
double current_w = qAbs(mousePos2d.X() - startPos2d.X());
double current_h = qAbs(mousePos2d.Y() - startPos2d.Y());
double rect_w = widthFromInput ? inputWidth : current_w;
double rect_h = heightFromInput ? inputHeight : current_h;
int signX = (mouseDir.x() >= 0) ? 1 : -1;
int signY = (mouseDir.y() >= 0) ? 1 : -1;
int signZ = (mouseDir.z() >= 0) ? 1 : -1;
worldPos = startPos;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
worldPos.setX(startPos.x() + signX * rect_w);
worldPos.setY(startPos.y() + signY * rect_h);
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
worldPos.setX(startPos.x() + signX * rect_w);
worldPos.setZ(startPos.z() + signZ * rect_h);
} else { // YZ
worldPos.setY(startPos.y() + signY * rect_w);
worldPos.setZ(startPos.z() + signZ * rect_h);
}
int signX = (mousePos2d.X() >= startPos2d.X()) ? 1 : -1;
int signY = (mousePos2d.Y() >= startPos2d.Y()) ? 1 : -1;
gp_Pnt2d endPos2d(startPos2d.X() + signX * rect_w, startPos2d.Y() + signY * rect_h);
gp_Pnt endPos3d = ElCLib::To3d(plane, endPos2d);
worldPosQ = QVector3D(endPos3d.X(), endPos3d.Y(), endPos3d.Z());
} else {
if (m_viewport->isSnappingOrigin()) {
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
const auto& origin = plane.Location();
worldPosQ.setX(origin.X()); worldPosQ.setY(origin.Y()); worldPosQ.setZ(origin.Z());
} else if (m_viewport->isSnappingVertex()) {
worldPos = QVector3D(m_viewport->snapVertex().X(), m_viewport->snapVertex().Y(), m_viewport->snapVertex().Z());
worldPosQ = QVector3D(m_viewport->snapVertex().X(), m_viewport->snapVertex().Y(), m_viewport->snapVertex().Z());
} else {
worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
worldPosQ = m_viewport->unproject(event->pos(), plane);
}
}
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
p.SetCoord(worldPosQ.x(), worldPosQ.y(), worldPosQ.z());
emit m_viewport->rectangleAdded(m_firstRectanglePoint, p);
deactivate();
}
@@ -122,8 +121,12 @@ void RectangleTool::mouseMoveEvent(QMouseEvent *event)
void RectangleTool::finalizeCreation()
{
QVector3D worldPos;
QVector3D startPos(m_firstRectanglePoint.X(), m_firstRectanglePoint.Y(), m_firstRectanglePoint.Z());
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
QVector3D worldPosQ;
gp_Pnt startPos = m_firstRectanglePoint;
QString widthInput = m_viewport->property("widthInput").toString();
QString heightInput = m_viewport->property("heightInput").toString();
@@ -142,41 +145,30 @@ void RectangleTool::finalizeCreation()
}
if (widthFromInput || heightFromInput) {
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D mouseDir = mousePos - startPos;
double current_w, current_h;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
current_w = qAbs(mouseDir.x());
current_h = qAbs(mouseDir.y());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
current_w = qAbs(mouseDir.x());
current_h = qAbs(mouseDir.z());
} else { // YZ
current_w = qAbs(mouseDir.y());
current_h = qAbs(mouseDir.z());
}
QVector3D mousePosQ = m_viewport->unproject(m_viewport->currentMousePos(), plane);
gp_Pnt mousePos(mousePosQ.x(), mousePosQ.y(), mousePosQ.z());
gp_Pnt2d startPos2d(gp_Vec(plane.Location(), startPos).Dot(plane.XDirection()), gp_Vec(plane.Location(), startPos).Dot(plane.YDirection()));
gp_Pnt2d mousePos2d(gp_Vec(plane.Location(), mousePos).Dot(plane.XDirection()), gp_Vec(plane.Location(), mousePos).Dot(plane.YDirection()));
double current_w = qAbs(mousePos2d.X() - startPos2d.X());
double current_h = qAbs(mousePos2d.Y() - startPos2d.Y());
double rect_w = widthFromInput ? inputWidth : current_w;
double rect_h = heightFromInput ? inputHeight : current_h;
int signX = (mouseDir.x() >= 0) ? 1 : -1;
int signY = (mouseDir.y() >= 0) ? 1 : -1;
int signZ = (mouseDir.z() >= 0) ? 1 : -1;
worldPos = startPos;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
worldPos.setX(startPos.x() + signX * rect_w);
worldPos.setY(startPos.y() + signY * rect_h);
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
worldPos.setX(startPos.x() + signX * rect_w);
worldPos.setZ(startPos.z() + signZ * rect_h);
} else { // YZ
worldPos.setY(startPos.y() + signY * rect_w);
worldPos.setZ(startPos.z() + signZ * rect_h);
}
int signX = (mousePos2d.X() >= startPos2d.X()) ? 1 : -1;
int signY = (mousePos2d.Y() >= startPos2d.Y()) ? 1 : -1;
gp_Pnt2d endPos2d(startPos2d.X() + signX * rect_w, startPos2d.Y() + signY * rect_h);
gp_Pnt endPos3d = ElCLib::To3d(plane, endPos2d);
worldPosQ = QVector3D(endPos3d.X(), endPos3d.Y(), endPos3d.Z());
} else {
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
worldPosQ = m_viewport->unproject(m_viewport->currentMousePos(), plane);
}
gp_Pnt p;
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
p.SetCoord(worldPosQ.x(), worldPosQ.y(), worldPosQ.z());
emit m_viewport->rectangleAdded(m_firstRectanglePoint, p);
deactivate();
@@ -184,10 +176,14 @@ void RectangleTool::finalizeCreation()
void RectangleTool::paintGL()
{
if (m_isDefining) {
if (!m_isDefining) return;
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
QVector<GLfloat> vertices;
QVector3D worldPos;
QVector3D startPos(m_firstRectanglePoint.X(), m_firstRectanglePoint.Y(), m_firstRectanglePoint.Z());
QVector3D worldPosQ;
gp_Pnt startPos = m_firstRectanglePoint;
QString widthInput = m_viewport->property("widthInput").toString();
QString heightInput = m_viewport->property("heightInput").toString();
@@ -206,82 +202,70 @@ void RectangleTool::paintGL()
if (ok) heightFromInput = true;
}
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D mousePosQ = m_viewport->unproject(m_viewport->currentMousePos(), plane);
gp_Pnt mousePos(mousePosQ.x(), mousePosQ.y(), mousePosQ.z());
if (widthFromInput || heightFromInput) {
QVector3D mouseDir = mousePos - startPos;
double current_w, current_h;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
current_w = qAbs(mouseDir.x());
current_h = qAbs(mouseDir.y());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
current_w = qAbs(mouseDir.x());
current_h = qAbs(mouseDir.z());
} else { // YZ
current_w = qAbs(mouseDir.y());
current_h = qAbs(mouseDir.z());
}
gp_Pnt2d startPos2d(gp_Vec(plane.Location(), startPos).Dot(plane.XDirection()), gp_Vec(plane.Location(), startPos).Dot(plane.YDirection()));
gp_Pnt2d mousePos2d(gp_Vec(plane.Location(), mousePos).Dot(plane.XDirection()), gp_Vec(plane.Location(), mousePos).Dot(plane.YDirection()));
double current_w = qAbs(mousePos2d.X() - startPos2d.X());
double current_h = qAbs(mousePos2d.Y() - startPos2d.Y());
double rect_w = widthFromInput ? inputWidth : current_w;
double rect_h = heightFromInput ? inputHeight : current_h;
int signX = (mouseDir.x() >= 0) ? 1 : -1;
int signY = (mouseDir.y() >= 0) ? 1 : -1;
int signZ = (mouseDir.z() >= 0) ? 1 : -1;
worldPos = startPos;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
worldPos.setX(startPos.x() + signX * rect_w);
worldPos.setY(startPos.y() + signY * rect_h);
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
worldPos.setX(startPos.x() + signX * rect_w);
worldPos.setZ(startPos.z() + signZ * rect_h);
} else { // YZ
worldPos.setY(startPos.y() + signY * rect_w);
worldPos.setZ(startPos.z() + signZ * rect_h);
}
int signX = (mousePos2d.X() >= startPos2d.X()) ? 1 : -1;
int signY = (mousePos2d.Y() >= startPos2d.Y()) ? 1 : -1;
gp_Pnt2d endPos2d(startPos2d.X() + signX * rect_w, startPos2d.Y() + signY * rect_h);
gp_Pnt endPos3d = ElCLib::To3d(plane, endPos2d);
worldPosQ = QVector3D(endPos3d.X(), endPos3d.Y(), endPos3d.Z());
} else {
worldPos = mousePos;
worldPosQ = mousePosQ;
if (m_viewport->isSnappingOrigin()) {
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
const auto& origin = plane.Location();
worldPosQ.setX(origin.X()); worldPosQ.setY(origin.Y()); worldPosQ.setZ(origin.Z());
} else if (m_viewport->isSnappingVertex()) {
worldPos.setX(m_viewport->snapVertex().X()); worldPos.setY(m_viewport->snapVertex().Y()); worldPos.setZ(m_viewport->snapVertex().Z());
worldPosQ.setX(m_viewport->snapVertex().X()); worldPosQ.setY(m_viewport->snapVertex().Y()); worldPosQ.setZ(m_viewport->snapVertex().Z());
}
}
QVector3D p1 = startPos;
QVector3D p2, p3, p4;
p3 = worldPos;
gp_Pnt p1_3d = startPos;
gp_Pnt p3_3d(worldPosQ.x(), worldPosQ.y(), worldPosQ.z());
gp_Pnt2d p1_2d(gp_Vec(plane.Location(), p1_3d).Dot(plane.XDirection()), gp_Vec(plane.Location(), p1_3d).Dot(plane.YDirection()));
gp_Pnt2d p3_2d(gp_Vec(plane.Location(), p3_3d).Dot(plane.XDirection()), gp_Vec(plane.Location(), p3_3d).Dot(plane.YDirection()));
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
p2.setX(p3.x()); p2.setY(p1.y()); p2.setZ(p1.z());
p4.setX(p1.x()); p4.setY(p3.y()); p4.setZ(p1.z());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
p2.setX(p3.x()); p2.setY(p1.y()); p2.setZ(p1.z());
p4.setX(p1.x()); p4.setY(p1.y()); p4.setZ(p3.z());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) {
p2.setX(p1.x()); p2.setY(p3.y()); p2.setZ(p1.z());
p4.setX(p1.x()); p4.setY(p1.y()); p4.setZ(p3.z());
}
gp_Pnt2d p2_2d(p3_2d.X(), p1_2d.Y());
gp_Pnt2d p4_2d(p1_2d.X(), p3_2d.Y());
vertices << p1.x() << p1.y() << p1.z();
vertices << p2.x() << p2.y() << p2.z();
vertices << p2.x() << p2.y() << p2.z();
vertices << p3.x() << p3.y() << p3.z();
vertices << p3.x() << p3.y() << p3.z();
vertices << p4.x() << p4.y() << p4.z();
vertices << p4.x() << p4.y() << p4.z();
vertices << p1.x() << p1.y() << p1.z();
gp_Pnt p2_3d = ElCLib::To3d(plane, p2_2d);
gp_Pnt p4_3d = ElCLib::To3d(plane, p4_2d);
vertices << p1_3d.X() << p1_3d.Y() << p1_3d.Z();
vertices << p2_3d.X() << p2_3d.Y() << p2_3d.Z();
vertices << p2_3d.X() << p2_3d.Y() << p2_3d.Z();
vertices << p3_3d.X() << p3_3d.Y() << p3_3d.Z();
vertices << p3_3d.X() << p3_3d.Y() << p3_3d.Z();
vertices << p4_3d.X() << p4_3d.Y() << p4_3d.Z();
vertices << p4_3d.X() << p4_3d.Y() << p4_3d.Z();
vertices << p1_3d.X() << p1_3d.Y() << p1_3d.Z();
m_viewport->shaderProgram()->setUniformValue(m_viewport->colorLoc(), QVector4D(1.0f, 1.0f, 0.0f, 1.0f));
m_viewport->vbo().bind();
m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
glDrawArrays(GL_LINES, 0, 8);
}
}
void RectangleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection)
{
if (m_isDefining) {
QVector3D worldPos;
QVector3D p1_3d(m_firstRectanglePoint.X(), m_firstRectanglePoint.Y(), m_firstRectanglePoint.Z());
if (!m_isDefining) return;
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) return;
const auto& plane = currentPlaneOpt.value();
QVector3D worldPosQ;
gp_Pnt p1_3d = m_firstRectanglePoint;
QString widthInput = m_viewport->property("widthInput").toString();
QString heightInput = m_viewport->property("heightInput").toString();
@@ -300,77 +284,58 @@ void RectangleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, cons
if (ok) heightFromInput = true;
}
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
QVector3D mousePosQ = m_viewport->unproject(m_viewport->currentMousePos(), plane);
gp_Pnt mousePos(mousePosQ.x(), mousePosQ.y(), mousePosQ.z());
if (widthFromInput || heightFromInput) {
QVector3D mouseDir = mousePos - p1_3d;
double current_w, current_h;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
current_w = qAbs(mouseDir.x());
current_h = qAbs(mouseDir.y());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
current_w = qAbs(mouseDir.x());
current_h = qAbs(mouseDir.z());
} else { // YZ
current_w = qAbs(mouseDir.y());
current_h = qAbs(mouseDir.z());
}
gp_Pnt2d startPos2d(gp_Vec(plane.Location(), p1_3d).Dot(plane.XDirection()), gp_Vec(plane.Location(), p1_3d).Dot(plane.YDirection()));
gp_Pnt2d mousePos2d(gp_Vec(plane.Location(), mousePos).Dot(plane.XDirection()), gp_Vec(plane.Location(), mousePos).Dot(plane.YDirection()));
double current_w = qAbs(mousePos2d.X() - startPos2d.X());
double current_h = qAbs(mousePos2d.Y() - startPos2d.Y());
double rect_w = widthFromInput ? inputWidth : current_w;
double rect_h = heightFromInput ? inputHeight : current_h;
int signX = (mouseDir.x() >= 0) ? 1 : -1;
int signY = (mouseDir.y() >= 0) ? 1 : -1;
int signZ = (mouseDir.z() >= 0) ? 1 : -1;
worldPos = p1_3d;
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
worldPos.setX(p1_3d.x() + signX * rect_w);
worldPos.setY(p1_3d.y() + signY * rect_h);
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
worldPos.setX(p1_3d.x() + signX * rect_w);
worldPos.setZ(p1_3d.z() + signZ * rect_h);
} else { // YZ
worldPos.setY(p1_3d.y() + signY * rect_w);
worldPos.setZ(p1_3d.z() + signZ * rect_h);
}
int signX = (mousePos2d.X() >= startPos2d.X()) ? 1 : -1;
int signY = (mousePos2d.Y() >= startPos2d.Y()) ? 1 : -1;
gp_Pnt2d endPos2d(startPos2d.X() + signX * rect_w, startPos2d.Y() + signY * rect_h);
gp_Pnt endPos3d = ElCLib::To3d(plane, endPos2d);
worldPosQ = QVector3D(endPos3d.X(), endPos3d.Y(), endPos3d.Z());
} else {
worldPos = mousePos;
worldPosQ = mousePosQ;
if (m_viewport->isSnappingOrigin()) {
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
const auto& origin = plane.Location();
worldPosQ.setX(origin.X()); worldPosQ.setY(origin.Y()); worldPosQ.setZ(origin.Z());
} else if (m_viewport->isSnappingVertex()) {
worldPos.setX(m_viewport->snapVertex().X()); worldPos.setY(m_viewport->snapVertex().Y()); worldPos.setZ(m_viewport->snapVertex().Z());
worldPosQ.setX(m_viewport->snapVertex().X()); worldPosQ.setY(m_viewport->snapVertex().Y()); worldPosQ.setZ(m_viewport->snapVertex().Z());
}
}
QVector3D p3_3d = worldPos;
QVector3D p2_3d, p4_3d;
gp_Pnt p3_3d(worldPosQ.x(), worldPosQ.y(), worldPosQ.z());
gp_Pnt p2_3d, p4_3d;
double w, h;
gp_Pnt2d p1_2d(gp_Vec(plane.Location(), p1_3d).Dot(plane.XDirection()), gp_Vec(plane.Location(), p1_3d).Dot(plane.YDirection()));
gp_Pnt2d p3_2d(gp_Vec(plane.Location(), p3_3d).Dot(plane.XDirection()), gp_Vec(plane.Location(), p3_3d).Dot(plane.YDirection()));
gp_Pnt2d p2_2d(p3_2d.X(), p1_2d.Y());
p2_3d = ElCLib::To3d(plane, p2_2d);
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
p2_3d.setX(p3_3d.x()); p2_3d.setY(p1_3d.y()); p2_3d.setZ(p1_3d.z());
p4_3d.setX(p1_3d.x()); p4_3d.setY(p3_3d.y()); p4_3d.setZ(p1_3d.z());
w = qAbs(p3_3d.x() - p1_3d.x());
h = qAbs(p3_3d.y() - p1_3d.y());
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
p2_3d.setX(p3_3d.x()); p2_3d.setY(p1_3d.y()); p2_3d.setZ(p1_3d.z());
p4_3d.setX(p1_3d.x()); p4_3d.setY(p1_3d.y()); p4_3d.setZ(p3_3d.z());
w = qAbs(p3_3d.x() - p1_3d.x());
h = qAbs(p3_3d.z() - p1_3d.z());
} else { // YZ
p2_3d.setX(p1_3d.x()); p2_3d.setY(p3_3d.y()); p2_3d.setZ(p1_3d.z());
p4_3d.setX(p1_3d.x()); p4_3d.setY(p1_3d.y()); p4_3d.setZ(p3_3d.z());
w = qAbs(p3_3d.y() - p1_3d.y());
h = qAbs(p3_3d.z() - p1_3d.z());
}
double w = qAbs(p3_2d.X() - p1_2d.X());
double h = qAbs(p3_2d.Y() - p1_2d.Y());
painter.setRenderHint(QPainter::Antialiasing);
QFontMetrics fm(painter.font());
QVector3D p1_q(p1_3d.X(), p1_3d.Y(), p1_3d.Z());
QVector3D p2_q(p2_3d.X(), p2_3d.Y(), p2_3d.Z());
QVector3D p3_q(p3_3d.X(), p3_3d.Y(), p3_3d.Z());
// Width dimension
QVector3D widthTextPos3D = (p1_3d + p2_3d) / 2.0f;
QVector3D widthTextPos3D = (p1_q + p2_q) / 2.0f;
QVector3D screenPosW = m_viewport->project(widthTextPos3D, modelView, projection, m_viewport->rect());
if (screenPosW.z() < 1.0f) {
QString widthText = widthFromInput ? widthInput : QString::number(w, 'f', 2);
QRect textRect = fm.boundingRect(widthText + "__");
textRect.moveCenter(screenPosW.toPoint() + QPoint(0, (p3_3d.z() > p1_3d.z() || p3_3d.y() > p1_3d.y()) ? -15 : 15));
textRect.moveCenter(screenPosW.toPoint() + QPoint(0, (p3_2d.Y() > p1_2d.Y()) ? -15 : 15));
if (m_viewport->property("dimensionEditMode").toString() == "width") {
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255));
} else {
@@ -381,12 +346,12 @@ void RectangleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, cons
}
// Height dimension
QVector3D heightTextPos3D = (p2_3d + p3_3d) / 2.0f;
QVector3D heightTextPos3D = (p2_q + p3_q) / 2.0f;
QVector3D screenPosH = m_viewport->project(heightTextPos3D, modelView, projection, m_viewport->rect());
if (screenPosH.z() < 1.0f) {
QString heightText = heightFromInput ? heightInput : QString::number(h, 'f', 2);
QRect textRect = fm.boundingRect(heightText + "__");
textRect.moveCenter(screenPosH.toPoint() + QPoint((p3_3d.x() > p1_3d.x()) ? 15 : -15, 0));
textRect.moveCenter(screenPosH.toPoint() + QPoint((p3_2d.X() > p1_2d.X()) ? 15 : -15, 0));
if (m_viewport->property("dimensionEditMode").toString() == "height") {
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255));
} else {
@@ -395,5 +360,4 @@ void RectangleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, cons
painter.setPen(Qt::white);
painter.drawText(textRect, Qt::AlignCenter, heightText);
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+67 -16
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -10,11 +10,44 @@
#include "SketchLine.h"
#include "SketchRectangle.h"
#include <BRep_Builder.hxx>
#include <TopoDS_Compound.hxx>
#include <gp_Pnt.hxx>
#include <gp_Dir.hxx>
#include <QJsonArray>
#include <QJsonObject>
namespace
{
void gpPntToJson(const gp_Pnt& p, QJsonObject& json) {
json["x"] = p.X();
json["y"] = p.Y();
json["z"] = p.Z();
}
gp_Pnt jsonToGpPnt(const QJsonObject& json) {
return gp_Pnt(json["x"].toDouble(), json["y"].toDouble(), json["z"].toDouble());
}
void gpDirToJson(const gp_Dir& d, QJsonObject& json) {
json["x"] = d.X();
json["y"] = d.Y();
json["z"] = d.Z();
}
gp_Dir jsonToGpDir(const QJsonObject& json) {
return gp_Dir(json["x"].toDouble(), json["y"].toDouble(), json["z"].toDouble());
}
}
SketchFeature::SketchFeature(const QString& name)
: Feature(name)
: Feature(name), m_plane(gp_Pnt(0, 0, 0), gp::DZ())
{
BRep_Builder builder;
TopoDS_Compound compound;
builder.MakeCompound(compound);
m_shape = compound;
}
SketchFeature::~SketchFeature()
@@ -27,12 +60,12 @@ QString SketchFeature::type() const
return "Sketch";
}
void SketchFeature::setPlane(SketchPlane plane)
void SketchFeature::setPlane(const gp_Ax2& plane)
{
m_plane = plane;
}
SketchFeature::SketchPlane SketchFeature::plane() const
const gp_Ax2& SketchFeature::plane() const
{
return m_plane;
}
@@ -42,6 +75,12 @@ const TopoDS_Shape& SketchFeature::shape() const
return m_shape;
}
void SketchFeature::addShape(const TopoDS_Shape& shape)
{
BRep_Builder builder;
builder.Add(m_shape, shape);
}
void SketchFeature::addObject(SketchObject* object)
{
m_objects.append(object);
@@ -55,11 +94,14 @@ const QList<SketchObject*>& SketchFeature::objects() const
void SketchFeature::read(const QJsonObject& json)
{
Feature::read(json);
if (json.contains("plane") && json["plane"].isString()) {
QString planeStr = json["plane"].toString();
if (planeStr == "XY") m_plane = SketchPlane::XY;
else if (planeStr == "XZ") m_plane = SketchPlane::XZ;
else if (planeStr == "YZ") m_plane = SketchPlane::YZ;
if (json.contains("plane") && json["plane"].isObject()) {
QJsonObject planeJson = json["plane"].toObject();
if (planeJson.contains("origin") && planeJson.contains("normal") && planeJson.contains("x_direction")) {
gp_Pnt origin = jsonToGpPnt(planeJson["origin"].toObject());
gp_Dir normal = jsonToGpDir(planeJson["normal"].toObject());
gp_Dir x_dir = jsonToGpDir(planeJson["x_direction"].toObject());
m_plane = gp_Ax2(origin, normal, x_dir);
}
}
if (json.contains("objects") && json["objects"].isArray()) {
@@ -87,13 +129,22 @@ void SketchFeature::read(const QJsonObject& json)
void SketchFeature::write(QJsonObject& json) const
{
Feature::write(json);
QString planeStr;
switch (m_plane) {
case SketchPlane::XY: planeStr = "XY"; break;
case SketchPlane::XZ: planeStr = "XZ"; break;
case SketchPlane::YZ: planeStr = "YZ"; break;
}
json["plane"] = planeStr;
QJsonObject planeJson;
QJsonObject originJson;
gpPntToJson(m_plane.Location(), originJson);
planeJson["origin"] = originJson;
QJsonObject normalJson;
gpDirToJson(m_plane.Direction(), normalJson);
planeJson["normal"] = normalJson;
QJsonObject xDirJson;
gpDirToJson(m_plane.XDirection(), xDirJson);
planeJson["x_direction"] = xDirJson;
json["plane"] = planeJson;
QJsonArray objectsArray;
for (const auto& object : m_objects) {
+6 -10
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -9,6 +9,7 @@
#define SKETCHFEATURE_H
#include <TopoDS_Shape.hxx>
#include <gp_Ax2.hxx>
#include <QList>
#include "Feature.h"
@@ -17,21 +18,16 @@ class SketchObject;
class SketchFeature : public Feature
{
public:
enum class SketchPlane {
XY,
XZ,
YZ
};
SketchFeature(const QString& name);
~SketchFeature();
QString type() const override;
void setPlane(SketchPlane plane);
SketchPlane plane() const;
void setPlane(const gp_Ax2& plane);
const gp_Ax2& plane() const;
const TopoDS_Shape& shape() const;
void addShape(const TopoDS_Shape& shape);
void addObject(SketchObject* object);
const QList<SketchObject*>& objects() const;
@@ -40,7 +36,7 @@ public:
void write(QJsonObject &json) const override;
private:
SketchPlane m_plane;
gp_Ax2 m_plane;
TopoDS_Shape m_shape;
QList<SketchObject*> m_objects;
};
+48 -43
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -13,6 +13,7 @@
#include <QOpenGLShaderProgram>
#include <QPainter>
#include <QVector>
#include <gp_Ax2.hxx>
namespace {
struct GridParams {
@@ -62,7 +63,7 @@ void SketchGrid::initializeGL()
m_vbo.release();
}
void SketchGrid::paintGL(SketchPlane plane, QOpenGLShaderProgram* shaderProgram, int colorLoc)
void SketchGrid::paintGL(const gp_Ax2& plane, QOpenGLShaderProgram* shaderProgram, int colorLoc)
{
GLint previous_vao = 0;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &previous_vao);
@@ -77,12 +78,19 @@ void SketchGrid::paintGL(SketchPlane plane, QOpenGLShaderProgram* shaderProgram,
QOpenGLContext::currentContext()->extraFunctions()->glBindVertexArray(previous_vao);
}
void SketchGrid::drawGridLines(SketchPlane plane, QOpenGLShaderProgram* shaderProgram, int colorLoc)
void SketchGrid::drawGridLines(const gp_Ax2& plane, QOpenGLShaderProgram* shaderProgram, int colorLoc)
{
auto params = getGridParams(-m_viewport->camera()->uiCameraDistance());
const float minorIncrement = params.minorIncrement;
const int gridSize = params.gridSize;
const auto& origin = plane.Location();
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
QVector3D originVec(origin.X(), origin.Y(), origin.Z());
QVector3D xDirVec(xDir.X(), xDir.Y(), xDir.Z());
QVector3D yDirVec(yDir.X(), yDir.Y(), yDir.Z());
QVector<GLfloat> minorLines;
QVector<GLfloat> majorLines;
@@ -93,16 +101,14 @@ void SketchGrid::drawGridLines(SketchPlane plane, QOpenGLShaderProgram* shaderPr
float pos = i * minorIncrement;
QVector<GLfloat>& current_vector = (i % 5 == 0) ? majorLines : minorLines;
if (plane == XY) {
current_vector << pos << -gridSize << 0 << pos << gridSize << 0;
current_vector << -gridSize << pos << 0 << gridSize << pos << 0;
} else if (plane == XZ) {
current_vector << pos << 0 << -gridSize << pos << 0 << gridSize;
current_vector << -gridSize << 0 << pos << gridSize << 0 << pos;
} else { // YZ
current_vector << 0 << pos << -gridSize << 0 << pos << gridSize;
current_vector << 0 << -gridSize << pos << 0 << gridSize << pos;
}
QVector3D p1 = originVec + pos * xDirVec - gridSize * yDirVec;
QVector3D p2 = originVec + pos * xDirVec + gridSize * yDirVec;
current_vector << p1.x() << p1.y() << p1.z() << p2.x() << p2.y() << p2.z();
QVector3D p3 = originVec - gridSize * xDirVec + pos * yDirVec;
QVector3D p4 = originVec + gridSize * xDirVec + pos * yDirVec;
current_vector << p3.x() << p3.y() << p3.z() << p4.x() << p4.y() << p4.z();
}
m_vbo.bind();
@@ -120,50 +126,51 @@ void SketchGrid::drawGridLines(SketchPlane plane, QOpenGLShaderProgram* shaderPr
glDrawArrays(GL_LINES, 0, majorLines.size() / 3);
}
void SketchGrid::drawAxes(SketchPlane plane, QOpenGLShaderProgram* shaderProgram, int colorLoc)
void SketchGrid::drawAxes(const gp_Ax2& plane, QOpenGLShaderProgram* shaderProgram, int colorLoc)
{
auto params = getGridParams(-m_viewport->camera()->uiCameraDistance());
const int axisLength = params.gridSize;
const auto& origin = plane.Location();
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
QVector3D originVec(origin.X(), origin.Y(), origin.Z());
QVector3D xDirVec(xDir.X(), xDir.Y(), xDir.Z());
QVector3D yDirVec(yDir.X(), yDir.Y(), yDir.Z());
QVector<GLfloat> vertices;
glLineWidth(2.0f);
m_vbo.bind();
// X Axis (Red)
if (plane == XY || plane == XZ) {
vertices.clear();
vertices << -axisLength << 0 << 0 << axisLength << 0 << 0;
QVector3D p1 = originVec - axisLength * xDirVec;
QVector3D p2 = originVec + axisLength * xDirVec;
vertices << p1.x() << p1.y() << p1.z() << p2.x() << p2.y() << p2.z();
shaderProgram->setUniformValue(colorLoc, QVector4D(1.0f, 0.0f, 0.0f, 1.0f));
m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
glDrawArrays(GL_LINES, 0, 2);
}
// Y Axis (Green)
if (plane == XY || plane == YZ) {
vertices.clear();
vertices << 0 << -axisLength << 0 << 0 << axisLength << 0;
QVector3D p3 = originVec - axisLength * yDirVec;
QVector3D p4 = originVec + axisLength * yDirVec;
vertices << p3.x() << p3.y() << p3.z() << p4.x() << p4.y() << p4.z();
shaderProgram->setUniformValue(colorLoc, QVector4D(0.0f, 1.0f, 0.0f, 1.0f));
m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
glDrawArrays(GL_LINES, 0, 2);
}
// Z Axis (Blue)
if (plane == XZ || plane == YZ) {
vertices.clear();
vertices << 0 << 0 << -axisLength << 0 << 0 << axisLength;
shaderProgram->setUniformValue(colorLoc, QVector4D(0.0f, 0.0f, 1.0f, 1.0f));
m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
glDrawArrays(GL_LINES, 0, 2);
}
// Origin dot
glPointSize(5.0f);
vertices.clear();
vertices << 0.0f << 0.0f << 0.0f;
vertices << originVec.x() << originVec.y() << originVec.z();
shaderProgram->setUniformValue(colorLoc, QVector4D(1.0f, 1.0f, 1.0f, 1.0f)); // White
m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
glDrawArrays(GL_POINTS, 0, 1);
}
void SketchGrid::paintAxisLabels(QPainter& painter, SketchGrid::SketchPlane plane, const QMatrix4x4& modelView, const QMatrix4x4& projection)
void SketchGrid::paintAxisLabels(QPainter& painter, const gp_Ax2& plane, const QMatrix4x4& modelView, const QMatrix4x4& projection)
{
painter.setPen(Qt::white);
painter.setFont(QFont("Arial", 10));
@@ -172,15 +179,21 @@ void SketchGrid::paintAxisLabels(QPainter& painter, SketchGrid::SketchPlane plan
const float majorIncrement = params.majorIncrement;
const int range = params.gridSize;
auto drawLabelsForAxis = [&](int axis_idx) {
const auto& origin = plane.Location();
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
QVector3D originVec(origin.X(), origin.Y(), origin.Z());
QVector3D xDirVec(xDir.X(), xDir.Y(), xDir.Z());
QVector3D yDirVec(yDir.X(), yDir.Y(), yDir.Z());
auto drawLabelsForAxis = [&](const QVector3D& axisDir) {
int numLabels = range / majorIncrement;
for (int i = -numLabels; i <= numLabels; ++i) {
if (i == 0)
continue;
float val = i * majorIncrement;
QVector3D worldCoord;
worldCoord[axis_idx] = val;
QVector3D worldCoord = originVec + val * axisDir;
QVector3D screenPos = m_viewport->project(worldCoord, modelView, projection, m_viewport->rect());
if (screenPos.z() < 1.0f) { // Not clipped
@@ -189,14 +202,6 @@ void SketchGrid::paintAxisLabels(QPainter& painter, SketchGrid::SketchPlane plan
}
};
if (plane == SketchGrid::XY) {
drawLabelsForAxis(0); // X
drawLabelsForAxis(1); // Y
} else if (plane == SketchGrid::XZ) {
drawLabelsForAxis(0); // X
drawLabelsForAxis(2); // Z
} else if (plane == SketchGrid::YZ) {
drawLabelsForAxis(1); // Y
drawLabelsForAxis(2); // Z
}
drawLabelsForAxis(xDirVec);
drawLabelsForAxis(yDirVec);
}
+6 -11
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -12,6 +12,7 @@
#include <QMatrix4x4>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <gp_Ax2.hxx>
class QOpenGLShaderProgram;
class QPainter;
@@ -20,22 +21,16 @@ class ViewportWidget;
class SketchGrid : protected QOpenGLFunctions
{
public:
enum SketchPlane {
XY = 1,
XZ = 2,
YZ = 3
};
explicit SketchGrid(ViewportWidget* viewport);
~SketchGrid();
void initializeGL();
void paintGL(SketchPlane plane, QOpenGLShaderProgram* shaderProgram, int colorLoc);
void paintAxisLabels(QPainter& painter, SketchPlane plane, const QMatrix4x4& modelView, const QMatrix4x4& projection);
void paintGL(const gp_Ax2& plane, QOpenGLShaderProgram* shaderProgram, int colorLoc);
void paintAxisLabels(QPainter& painter, const gp_Ax2& plane, const QMatrix4x4& modelView, const QMatrix4x4& projection);
private:
void drawGridLines(SketchPlane plane, QOpenGLShaderProgram* shaderProgram, int colorLoc);
void drawAxes(SketchPlane plane, QOpenGLShaderProgram* shaderProgram, int colorLoc);
void drawGridLines(const gp_Ax2& plane, QOpenGLShaderProgram* shaderProgram, int colorLoc);
void drawAxes(const gp_Ax2& plane, QOpenGLShaderProgram* shaderProgram, int colorLoc);
QOpenGLVertexArrayObject m_vao;
QOpenGLBuffer m_vbo;
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+63 -91
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -19,6 +19,11 @@
#include <QOpenGLShaderProgram>
#include <QVector>
#include <QtMath>
#include <optional>
#include <gp_Ax2.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Vec.hxx>
#include <ElCLib.hxx>
Snapping::Snapping(ViewportWidget* viewport) : m_viewport(viewport)
{
@@ -30,23 +35,15 @@ bool Snapping::update(const QPoint& mousePos)
bool oldIsSnappingVertex = m_isSnappingVertex;
bool shouldSnap = false;
if (m_viewport->currentPlane() != ViewportWidget::SketchPlane::NONE && m_viewport->activeTool() != static_cast<int>(ApplicationController::ToolType::None)) {
QVector3D worldPos = m_viewport->unproject(mousePos, m_viewport->currentPlane());
auto currentPlaneOpt = m_viewport->currentPlane();
if (currentPlaneOpt && m_viewport->activeTool() != static_cast<int>(ApplicationController::ToolType::None)) {
const auto& plane = currentPlaneOpt.value();
QVector3D worldPosQ = m_viewport->unproject(mousePos, plane);
gp_Pnt worldPos(worldPosQ.x(), worldPosQ.y(), worldPosQ.z());
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;
}
gp_Pnt2d worldPos2d(gp_Vec(plane.Location(), worldPos).Dot(plane.XDirection()), gp_Vec(plane.Location(), worldPos).Dot(plane.YDirection()));
shouldSnap = qAbs(worldPos2d.X()) < snapRectHalfSize && qAbs(worldPos2d.Y()) < snapRectHalfSize;
}
m_isSnappingOrigin = shouldSnap;
@@ -55,8 +52,11 @@ bool Snapping::update(const QPoint& mousePos)
}
m_isSnappingVertex = false;
if (!m_isSnappingOrigin && m_viewport->document() && m_viewport->currentPlane() != ViewportWidget::SketchPlane::NONE && m_viewport->activeTool() != static_cast<int>(ApplicationController::ToolType::None)) {
QVector3D worldPos = m_viewport->unproject(mousePos, m_viewport->currentPlane());
if (!m_isSnappingOrigin && m_viewport->document() && currentPlaneOpt && m_viewport->activeTool() != static_cast<int>(ApplicationController::ToolType::None)) {
const auto& plane = currentPlaneOpt.value();
QVector3D worldPosQ = m_viewport->unproject(mousePos, plane);
gp_Pnt worldPos(worldPosQ.x(), worldPosQ.y(), worldPosQ.z());
gp_Pnt2d worldPos2d(gp_Vec(plane.Location(), worldPos).Dot(plane.XDirection()), gp_Vec(plane.Location(), worldPos).Dot(plane.YDirection()));
const float snapRectHalfSize = 0.0075f * -m_viewport->camera()->zoom();
for (Feature* feature : m_viewport->document()->features()) {
@@ -66,20 +66,8 @@ bool Snapping::update(const QPoint& mousePos)
auto line = static_cast<const SketchLine*>(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;
}
gp_Pnt2d vertex2d(gp_Vec(plane.Location(), vertex).Dot(plane.XDirection()), gp_Vec(plane.Location(), vertex).Dot(plane.YDirection()));
bool isClose = qAbs(worldPos2d.X() - vertex2d.X()) < snapRectHalfSize && qAbs(worldPos2d.Y() - vertex2d.Y()) < snapRectHalfSize;
if (isClose) {
m_isSnappingVertex = true;
@@ -89,37 +77,23 @@ bool Snapping::update(const QPoint& mousePos)
}
} else if (obj->type() == SketchObject::ObjectType::Rectangle) {
auto rect = static_cast<const SketchRectangle*>(obj);
const auto& rectPlane = sketch->plane();
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());
}
gp_Pnt2d p1_2d(gp_Vec(rectPlane.Location(), p1).Dot(rectPlane.XDirection()), gp_Vec(rectPlane.Location(), p1).Dot(rectPlane.YDirection()));
gp_Pnt2d p3_2d(gp_Vec(rectPlane.Location(), p3).Dot(rectPlane.XDirection()), gp_Vec(rectPlane.Location(), p3).Dot(rectPlane.YDirection()));
gp_Pnt2d p2_2d(p3_2d.X(), p1_2d.Y());
gp_Pnt2d p4_2d(p1_2d.X(), p3_2d.Y());
gp_Pnt p2 = ElCLib::To3d(rectPlane, p2_2d);
gp_Pnt p4 = ElCLib::To3d(rectPlane, p4_2d);
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;
}
gp_Pnt2d vertex2d(gp_Vec(plane.Location(), vertex).Dot(plane.XDirection()), gp_Vec(plane.Location(), vertex).Dot(plane.YDirection()));
bool isClose = qAbs(worldPos2d.X() - vertex2d.X()) < snapRectHalfSize && qAbs(worldPos2d.Y() - vertex2d.Y()) < snapRectHalfSize;
if (isClose) {
m_isSnappingVertex = true;
@@ -144,43 +118,41 @@ void Snapping::paintGL() const
}
QVector<GLfloat> vertices;
auto currentPlaneOpt = m_viewport->currentPlane();
if (!currentPlaneOpt) {
return;
}
const auto& plane = currentPlaneOpt.value();
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
QVector3D X(xDir.X(), xDir.Y(), xDir.Z());
QVector3D Y(yDir.X(), yDir.Y(), yDir.Z());
const float rectSize = 0.0075f * -m_viewport->camera()->zoom();
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;
}
const auto& o = plane.Location();
QVector3D O(o.X(), o.Y(), o.Z());
QVector3D p1 = O - rectSize * X - rectSize * Y;
QVector3D p2 = O + rectSize * X - rectSize * Y;
QVector3D p3 = O + rectSize * X + rectSize * Y;
QVector3D p4 = O - rectSize * X + rectSize * Y;
vertices << p1.x() << p1.y() << p1.z() << p2.x() << p2.y() << p2.z();
vertices << p2.x() << p2.y() << p2.z() << p3.x() << p3.y() << p3.z();
vertices << p3.x() << p3.y() << p3.z() << p4.x() << p4.y() << p4.z();
vertices << p4.x() << p4.y() << p4.z() << p1.x() << p1.y() << p1.z();
} 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;
}
QVector3D V(v.X(), v.Y(), v.Z());
QVector3D p1 = V - rectSize * X - rectSize * Y;
QVector3D p2 = V + rectSize * X - rectSize * Y;
QVector3D p3 = V + rectSize * X + rectSize * Y;
QVector3D p4 = V - rectSize * X + rectSize * Y;
vertices << p1.x() << p1.y() << p1.z() << p2.x() << p2.y() << p2.z();
vertices << p2.x() << p2.y() << p2.z() << p3.x() << p3.y() << p3.z();
vertices << p3.x() << p3.y() << p3.z() << p4.x() << p4.y() << p4.z();
vertices << p4.x() << p4.y() << p4.z() << p1.x() << p1.y() << p1.z();
}
m_viewport->shaderProgram()->setUniformValue(m_viewport->colorLoc(), QVector4D(1.0f, 1.0f, 0.0f, 0.5f));
m_viewport->vbo().bind();
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
+89 -81
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -33,9 +33,15 @@
#include <QApplication>
#include <cmath>
#include <QtMath>
#include <ElCLib.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Vec.hxx>
#include <QOpenGLShaderProgram>
#include <QVector>
#include <map>
#include <optional>
#include <gp_Ax2.hxx>
#include <gp_Dir.hxx>
struct PntComparator {
bool operator()(const gp_Pnt& a, const gp_Pnt& b) const {
@@ -60,7 +66,7 @@ ViewportWidget::ViewportWidget(QWidget *parent)
setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);
m_currentPlane = SketchPlane::XY;
m_currentPlane = gp_Ax2(gp::Origin(), gp::DZ());
m_toolIcons.insert(static_cast<int>(ApplicationController::ToolType::Line), new QSvgRenderer(QString(":/icons/line.svg"), this));
m_toolIcons.insert(static_cast<int>(ApplicationController::ToolType::Rectangle), new QSvgRenderer(QString(":/icons/rectangle.svg"), this));
@@ -143,12 +149,12 @@ void ViewportWidget::paintGL()
// Sketch grid rendering
if (m_isSelectingPlane) {
if (m_highlightedPlane != SketchPlane::NONE) {
m_sketchGrid->paintGL(static_cast<SketchGrid::SketchPlane>(m_highlightedPlane), m_shaderProgram, m_colorLoc);
if (m_highlightedPlane) {
m_sketchGrid->paintGL(m_highlightedPlane.value(), m_shaderProgram, m_colorLoc);
}
}
if (m_currentPlane != SketchPlane::NONE) {
m_sketchGrid->paintGL(static_cast<SketchGrid::SketchPlane>(m_currentPlane), m_shaderProgram, m_colorLoc);
if (m_currentPlane) {
m_sketchGrid->paintGL(m_currentPlane.value(), m_shaderProgram, m_colorLoc);
}
if (m_isSelectingPlane) {
@@ -205,8 +211,8 @@ void ViewportWidget::paintGL()
glDisable(GL_CULL_FACE);
QPainter painter(this);
if (m_currentPlane != SketchPlane::NONE) {
m_sketchGrid->paintAxisLabels(painter, static_cast<SketchGrid::SketchPlane>(m_currentPlane), model, projection);
if (m_currentPlane) {
m_sketchGrid->paintAxisLabels(painter, m_currentPlane.value(), model, projection);
}
m_featureBrowser->paint(painter, width(), height());
m_viewCube->paint2D(painter, width(), height());
@@ -233,9 +239,11 @@ void ViewportWidget::mousePressEvent(QMouseEvent *event)
m_camera->mousePressEvent(event);
if (event->button() == Qt::MiddleButton && !(QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
m_camera->startRotation(unproject(event->pos(), m_currentPlane));
if (m_currentPlane) {
m_camera->startRotation(unproject(event->pos(), m_currentPlane.value()));
update();
}
}
if (event->button() == Qt::LeftButton) {
if (m_viewCube->handleMousePress(event->pos(), width(), height())) {
@@ -245,10 +253,10 @@ void ViewportWidget::mousePressEvent(QMouseEvent *event)
}
if (m_isSelectingPlane) {
if (m_highlightedPlane != SketchPlane::NONE) {
emit planeSelected(m_highlightedPlane);
if (m_highlightedPlane) {
emit planeSelected(m_highlightedPlane.value());
m_isSelectingPlane = false;
m_highlightedPlane = SketchPlane::NONE;
m_highlightedPlane.reset();
update();
}
return;
@@ -268,8 +276,17 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
m_currentMousePos = event->pos();
if (m_isSelectingPlane) {
SketchPlane newHighlight = checkPlaneSelection(m_currentMousePos);
if (newHighlight != m_highlightedPlane) {
auto newHighlight = checkPlaneSelection(m_currentMousePos);
bool needsUpdate = newHighlight.has_value() != m_highlightedPlane.has_value();
if (!needsUpdate && newHighlight) {
// Very basic check, assumes planes are one of the 3 cardinal ones
// and checkPlaneSelection returns them consistently.
if (!newHighlight->Direction().IsEqual(m_highlightedPlane->Direction(), 1e-9)) {
needsUpdate = true;
}
}
if (needsUpdate) {
m_highlightedPlane = newHighlight;
update();
}
@@ -289,8 +306,10 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
void ViewportWidget::wheelEvent(QWheelEvent *event)
{
QVector3D worldPos = unproject(event->position().toPoint(), m_currentPlane);
if (m_currentPlane) {
QVector3D worldPos = unproject(event->position().toPoint(), m_currentPlane.value());
m_camera->wheelEvent(event, worldPos);
}
}
void ViewportWidget::keyPressEvent(QKeyEvent *event)
@@ -303,8 +322,8 @@ void ViewportWidget::keyPressEvent(QKeyEvent *event)
if (event->key() == Qt::Key_Escape) {
if (m_isSelectingPlane) {
m_isSelectingPlane = false;
m_highlightedPlane = SketchPlane::NONE;
m_currentPlane = SketchPlane::XY;
m_highlightedPlane.reset();
m_currentPlane = gp_Ax2(gp::Origin(), gp::DZ());
update();
return;
}
@@ -369,19 +388,19 @@ bool ViewportWidget::focusNextPrevChild(bool next)
return QOpenGLWidget::focusNextPrevChild(next);
}
void ViewportWidget::onSketchModeStarted(SketchPlane plane)
void ViewportWidget::onSketchModeStarted(const gp_Ax2& plane)
{
m_currentPlane = plane;
m_camera->saveState();
m_camera->animateToPlaneView(static_cast<int>(plane));
m_camera->animateToPlaneView(plane);
}
void ViewportWidget::onPlaneSelectionModeStarted()
{
m_isSelectingPlane = true;
m_highlightedPlane = SketchPlane::NONE;
m_currentPlane = SketchPlane::NONE;
m_highlightedPlane.reset();
m_currentPlane.reset();
update();
}
@@ -393,7 +412,7 @@ void ViewportWidget::onSketchModeEnded()
void ViewportWidget::onRestoreStateAnimationFinished()
{
// Return to showing the base XY grid when not in a sketch
m_currentPlane = SketchPlane::XY;
m_currentPlane = gp_Ax2(gp::Origin(), gp::DZ());
update();
}
@@ -465,7 +484,7 @@ void ViewportWidget::onActiveToolChanged(int tool)
}
}
QVector3D ViewportWidget::unproject(const QPoint& screenPos, SketchPlane plane)
QVector3D ViewportWidget::unproject(const QPoint& screenPos, const gp_Ax2& plane)
{
QMatrix4x4 model = m_camera->modelViewMatrix();
@@ -494,17 +513,13 @@ QVector3D ViewportWidget::unproject(const QPoint& screenPos, SketchPlane plane)
QVector3D rayOrigin(nearPoint_world);
QVector3D rayDir = (QVector3D(farPoint_world) - rayOrigin).normalized();
QVector3D planeNormal;
switch (plane) {
case SketchPlane::XY: planeNormal = QVector3D(0, 0, 1); break;
case SketchPlane::XZ: planeNormal = QVector3D(0, 1, 0); break;
case SketchPlane::YZ: planeNormal = QVector3D(1, 0, 0); break;
case SketchPlane::NONE: return QVector3D();
}
const auto& planeNormalDir = plane.Direction();
QVector3D planeNormal(planeNormalDir.X(), planeNormalDir.Y(), planeNormalDir.Z());
float denom = QVector3D::dotProduct(planeNormal, rayDir);
if (qAbs(denom) > 1e-6) {
QVector3D p0(0,0,0);
const auto& planeOriginPnt = plane.Location();
QVector3D p0(planeOriginPnt.X(), planeOriginPnt.Y(), planeOriginPnt.Z());
float t = QVector3D::dotProduct(p0 - rayOrigin, planeNormal) / denom;
return rayOrigin + t * rayDir;
}
@@ -538,20 +553,11 @@ void ViewportWidget::drawSketch(const SketchFeature* sketch)
const int numSegments = 64;
QVector3D u_axis, v_axis;
switch (sketch->plane()) {
case SketchFeature::SketchPlane::XY: // Top
u_axis = QVector3D(1, 0, 0);
v_axis = QVector3D(0, 1, 0);
break;
case SketchFeature::SketchPlane::XZ: // Front
u_axis = QVector3D(1, 0, 0);
v_axis = QVector3D(0, 0, 1);
break;
case SketchFeature::SketchPlane::YZ: // Right
u_axis = QVector3D(0, 1, 0);
v_axis = QVector3D(0, 0, 1);
break;
}
const auto& plane = sketch->plane();
const auto& xDir = plane.XDirection();
const auto& yDir = plane.YDirection();
u_axis = QVector3D(xDir.X(), xDir.Y(), xDir.Z());
v_axis = QVector3D(yDir.X(), yDir.Y(), yDir.Z());
for (int i = 0; i < numSegments; ++i) {
double angle1 = 2.0 * M_PI * double(i) / double(numSegments);
@@ -565,34 +571,32 @@ void ViewportWidget::drawSketch(const SketchFeature* sketch)
}
} else if (obj->type() == SketchObject::ObjectType::Rectangle) {
auto rect = static_cast<const SketchRectangle*>(obj);
const auto& p1 = rect->corner1();
const auto& p3 = rect->corner2();
const auto& plane = sketch->plane();
const auto& p1_3d = rect->corner1();
const auto& p3_3d = 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());
}
gp_Pnt2d p1_2d(gp_Vec(plane.Location(), p1_3d).Dot(plane.XDirection()), gp_Vec(plane.Location(), p1_3d).Dot(plane.YDirection()));
gp_Pnt2d p3_2d(gp_Vec(plane.Location(), p3_3d).Dot(plane.XDirection()), gp_Vec(plane.Location(), p3_3d).Dot(plane.YDirection()));
lineVertices << p1.X() << p1.Y() << p1.Z();
lineVertices << p2.X() << p2.Y() << p2.Z();
lineVertices << p2.X() << p2.Y() << p2.Z();
lineVertices << p3.X() << p3.Y() << p3.Z();
lineVertices << p3.X() << p3.Y() << p3.Z();
lineVertices << p4.X() << p4.Y() << p4.Z();
lineVertices << p4.X() << p4.Y() << p4.Z();
lineVertices << p1.X() << p1.Y() << p1.Z();
gp_Pnt2d p2_2d(p3_2d.X(), p1_2d.Y());
gp_Pnt2d p4_2d(p1_2d.X(), p3_2d.Y());
vertexCounts[p1] += 2;
vertexCounts[p2] += 2;
vertexCounts[p3] += 2;
vertexCounts[p4] += 2;
gp_Pnt p2_3d = ElCLib::To3d(plane, p2_2d);
gp_Pnt p4_3d = ElCLib::To3d(plane, p4_2d);
lineVertices << p1_3d.X() << p1_3d.Y() << p1_3d.Z();
lineVertices << p2_3d.X() << p2_3d.Y() << p2_3d.Z();
lineVertices << p2_3d.X() << p2_3d.Y() << p2_3d.Z();
lineVertices << p3_3d.X() << p3_3d.Y() << p3_3d.Z();
lineVertices << p3_3d.X() << p3_3d.Y() << p3_3d.Z();
lineVertices << p4_3d.X() << p4_3d.Y() << p4_3d.Z();
lineVertices << p4_3d.X() << p4_3d.Y() << p4_3d.Z();
lineVertices << p1_3d.X() << p1_3d.Y() << p1_3d.Z();
vertexCounts[p1_3d] += 2;
vertexCounts[p2_3d] += 2;
vertexCounts[p3_3d] += 2;
vertexCounts[p4_3d] += 2;
}
}
@@ -711,52 +715,56 @@ void ViewportWidget::drawSelectionPlanes()
// XY Plane (Top)
QVector<GLfloat> xyQuad = { planeOffset, -planeOffset, 0, planeOffset + planeSize, -planeOffset, 0,
planeOffset + planeSize, -planeOffset - planeSize, 0, planeOffset, -planeOffset - planeSize, 0 };
drawPlane(xyQuad, m_highlightedPlane == SketchPlane::XY);
drawPlane(xyQuad, m_highlightedPlane && m_highlightedPlane->Direction().IsEqual(gp::DZ(), 1e-9));
// XZ Plane (Front)
QVector<GLfloat> xzQuad = { planeOffset, 0, planeOffset, planeOffset + planeSize, 0, planeOffset,
planeOffset + planeSize, 0, planeOffset + planeSize, planeOffset, 0, planeOffset + planeSize };
drawPlane(xzQuad, m_highlightedPlane == SketchPlane::XZ);
drawPlane(xzQuad, m_highlightedPlane && m_highlightedPlane->Direction().IsEqual(gp::DY(), 1e-9));
// YZ Plane (Right)
QVector<GLfloat> yzQuad = { 0, -planeOffset, planeOffset, 0, -planeOffset, planeOffset + planeSize,
0, -planeOffset - planeSize, planeOffset + planeSize, 0, -planeOffset - planeSize, planeOffset };
drawPlane(yzQuad, m_highlightedPlane == SketchPlane::YZ);
drawPlane(yzQuad, m_highlightedPlane && m_highlightedPlane->Direction().IsEqual(gp::DX(), 1e-9));
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
}
ViewportWidget::SketchPlane ViewportWidget::checkPlaneSelection(const QPoint& screenPos)
std::optional<gp_Ax2> ViewportWidget::checkPlaneSelection(const QPoint& screenPos)
{
const float planeSize = 5.0f;
const float planeOffset = 1.0f;
gp_Ax2 yzPlane(gp_Pnt(0, 0, 0), gp::DX());
gp_Ax2 xzPlane(gp_Pnt(0, 0, 0), gp::DY());
gp_Ax2 xyPlane(gp_Pnt(0, 0, 0), gp::DZ());
QVector3D intersection;
// Check front to back to handle overlaps
// YZ plane (Right)
intersection = unproject(screenPos, SketchPlane::YZ);
intersection = unproject(screenPos, yzPlane);
if (intersection.y() >= -planeOffset - planeSize && intersection.y() <= -planeOffset &&
intersection.z() >= planeOffset && intersection.z() <= planeOffset + planeSize) {
return SketchPlane::YZ;
return gp_Ax2(gp_Pnt(0, -planeOffset, planeOffset), gp::DX());
}
// XZ plane (Front)
intersection = unproject(screenPos, SketchPlane::XZ);
intersection = unproject(screenPos, xzPlane);
if (intersection.x() >= planeOffset && intersection.x() <= planeOffset + planeSize &&
intersection.z() >= planeOffset && intersection.z() <= planeOffset + planeSize) {
return SketchPlane::XZ;
return gp_Ax2(gp_Pnt(planeOffset, 0, planeOffset), gp::DY());
}
// XY plane (Top)
intersection = unproject(screenPos, SketchPlane::XY);
intersection = unproject(screenPos, xyPlane);
if (intersection.x() >= planeOffset && intersection.x() <= planeOffset + planeSize &&
intersection.y() >= -planeOffset - planeSize && intersection.y() <= -planeOffset) {
return SketchPlane::XY;
return gp_Ax2(gp_Pnt(planeOffset, -planeOffset, 0), gp::DZ());
}
return SketchPlane::NONE;
return std::nullopt;
}
+10 -15
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE
@@ -15,6 +15,8 @@
#include <QVector3D>
#include <QRect>
#include <gp_Pnt.hxx>
#include <gp_Ax2.hxx>
#include <optional>
#include <QMap>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
@@ -35,26 +37,19 @@ class ViewportWidget : public QOpenGLWidget, protected QOpenGLFunctions
Q_OBJECT
public:
enum class SketchPlane {
NONE,
XY,
XZ,
YZ
};
explicit ViewportWidget(QWidget *parent = nullptr);
~ViewportWidget();
void setDocument(Document* document);
QVector3D project(const QVector3D& worldCoord, const QMatrix4x4& modelView, const QMatrix4x4& projection, const QRect& viewport);
QVector3D unproject(const QPoint& screenPos, SketchPlane plane);
QVector3D unproject(const QPoint& screenPos, const gp_Ax2& plane);
QOpenGLShaderProgram* shaderProgram() { return m_shaderProgram; }
QOpenGLBuffer& vbo() { return m_vbo; }
int colorLoc() const { return m_colorLoc; }
Camera* camera() const { return m_camera; }
Document* document() const { return m_document; }
SketchPlane currentPlane() const { return m_currentPlane; }
std::optional<gp_Ax2> currentPlane() const { return m_currentPlane; }
const QPoint& currentMousePos() const { return m_currentMousePos; }
bool isSnappingOrigin() const;
bool isSnappingVertex() const;
@@ -69,7 +64,7 @@ public:
void deactivateActiveTool();
public slots:
void onSketchModeStarted(SketchPlane plane);
void onSketchModeStarted(const gp_Ax2& plane);
void onSketchModeEnded();
void onPlaneSelectionModeStarted();
void onActiveToolChanged(int tool);
@@ -78,7 +73,7 @@ signals:
void lineAdded(const gp_Pnt& start, const gp_Pnt& end);
void rectangleAdded(const gp_Pnt& corner1, const gp_Pnt& corner2);
void circleAdded(const gp_Pnt& center, double radius);
void planeSelected(SketchPlane plane);
void planeSelected(const gp_Ax2& plane);
void toolDeactivated();
private slots:
@@ -100,7 +95,7 @@ private:
void initShaders();
void drawSketch(const SketchFeature* sketch);
void drawSelectionPlanes();
ViewportWidget::SketchPlane checkPlaneSelection(const QPoint& screenPos);
std::optional<gp_Ax2> checkPlaneSelection(const QPoint& screenPos);
QMatrix4x4 projection;
QOpenGLShaderProgram* m_shaderProgram = nullptr;
@@ -116,10 +111,10 @@ private:
SketchGrid* m_sketchGrid = nullptr;
FeatureBrowser* m_featureBrowser = nullptr;
Document* m_document = nullptr;
SketchPlane m_currentPlane = SketchPlane::NONE;
std::optional<gp_Ax2> m_currentPlane;
bool m_isSelectingPlane = false;
SketchPlane m_highlightedPlane = SketchPlane::NONE;
std::optional<gp_Ax2> m_highlightedPlane;
int m_activeTool = 0;
SketchTool* m_activeSketchTool = nullptr;
+1 -1
View File
@@ -1,6 +1,6 @@
// Unnamed CAD Software
//
// License: GPLv3, see LICENSE.txt
// License: GPLv3 (or later)
// Language: C++17
// Notes:
// - use a right-handed, Z-up coordinate system to match Open CASCADE