Compare commits
98 Commits
1ebe10dcee
...
failed-fac
| Author | SHA1 | Date | |
|---|---|---|---|
| 1be782b88d | |||
| 3444e9e183 | |||
| fdd972b286 | |||
| 768eed2f39 | |||
| 41639882db | |||
| fa6e4662a6 | |||
| a7ad78e103 | |||
| 0798cd2a6c | |||
| ce6975cc44 | |||
| b9860f3de0 | |||
| 6e729183ef | |||
| d5d430e80d | |||
| 95b4db5191 | |||
| e2dfdf1600 | |||
| 2333a7cdb5 | |||
| 8a75dab453 | |||
| 6396e49f9b | |||
| 22efad4684 | |||
| d40ae7e670 | |||
| 2fb73d91ef | |||
| a6d46a8280 | |||
| c28c080009 | |||
| 405e151f12 | |||
| 5e20822df4 | |||
| 34ecee0fa2 | |||
| 7010d221d0 | |||
| d43b49a12f | |||
| 2407957fb6 | |||
| 8a90b17b1f | |||
| e8aef2427b | |||
| 2ab4bbf805 | |||
| 7f6c01c8a0 | |||
| 4b0a903052 | |||
| 37399bd7c1 | |||
| 9d9c658cda | |||
| e1327b2caa | |||
| d66f7aaf56 | |||
| 64b48c7ed1 | |||
| 7ce318b402 | |||
| 08820659d4 | |||
| 94304bd2e3 | |||
| 88199a9d51 | |||
| 68eeeb11ec | |||
| e00af9a8e3 | |||
| ef55eac997 | |||
| 483b673229 | |||
| e8afc0a4b4 | |||
| 1779725d53 | |||
| b056ccbfec | |||
| 3a7cd78fb2 | |||
| d274b4f59f | |||
| a0dbc537cf | |||
| ddf6f6fd85 | |||
| f3a1f73f45 | |||
| 3bb8d65fd4 | |||
| 6721caca9f | |||
| 38e931bc79 | |||
| a66dc50daf | |||
| 2e2f99f2c2 | |||
| f29f40277d | |||
| a5e8257eb4 | |||
| ec63a23247 | |||
| d708ab9827 | |||
| e86a775b46 | |||
| 9334508b67 | |||
| b93f6158ef | |||
| 35cad74367 | |||
| 2394727724 | |||
| 95a651b6b0 | |||
| d015d171ec | |||
| 1584bfd5a0 | |||
| 3e2f464de9 | |||
| e64755ea0c | |||
| b144efbe05 | |||
| 966ab037b5 | |||
| 246372b847 | |||
| 2b455f57d4 | |||
| ed88730edb | |||
| 7f304bf1f3 | |||
| f5c7f6d326 | |||
| c43330fe5e | |||
| 9cacbf4e0e | |||
| 8febc50fec | |||
| c9c1b38f45 | |||
| c117ff3a8a | |||
| 34cecc38d5 | |||
| 2f7d2a4189 | |||
| 1fb211cc34 | |||
| b2d5cd19d4 | |||
| d89b7e42bc | |||
| 9d72fe2155 | |||
| 93282fce52 | |||
| a18435bb15 | |||
| ec75ab41c5 | |||
| aaaf9a44ff | |||
| bc2e84a537 | |||
| f842555582 | |||
| a980ad52be |
@@ -28,19 +28,10 @@ endif()
|
||||
message(STATUS "OpenCASCADE_INCLUDE_DIRS: ${OpenCASCADE_INCLUDE_DIRS}")
|
||||
message(STATUS "OpenCASCADE_LIBRARIES: ${OpenCASCADE_LIBRARIES}")
|
||||
|
||||
file(GLOB SOURCES "src/*.cpp")
|
||||
|
||||
add_executable(OpenCAD
|
||||
src/main.cpp
|
||||
src/MainWindow.cpp
|
||||
src/ViewportWidget.cpp
|
||||
src/ViewCube.cpp
|
||||
src/SketchGrid.cpp
|
||||
src/Document.cpp
|
||||
src/Feature.cpp
|
||||
src/SketchFeature.cpp
|
||||
src/SketchLine.cpp
|
||||
src/FeatureBrowser.cpp
|
||||
src/ApplicationController.cpp
|
||||
src/Camera.cpp
|
||||
${SOURCES}
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
|
||||
19
README.md
Normal file
19
README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Unnamed CAD Software
|
||||
|
||||
## Development
|
||||
|
||||
### Building
|
||||
|
||||
On Debian 12:
|
||||
|
||||
```bash
|
||||
$ sudo apt install cmake qt6-base-dev qt6-svg-dev libtbb-dev
|
||||
$ sudo apt install libocct-foundation-dev libocct-modeling-data-dev libocct-modeling-algorithms-dev libocct-visualization-dev
|
||||
|
||||
$ mkdir build
|
||||
$ cd build/
|
||||
$ cmake ..
|
||||
$ make
|
||||
|
||||
$ ./OpenCAD
|
||||
```
|
||||
42
icons/home.svg
Normal file
42
icons/home.svg
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-6"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
sodipodi:docname="home.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="namedview2"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="14.574147"
|
||||
inkscape:cx="6.9643869"
|
||||
inkscape:cy="8.6797532"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="831"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<path
|
||||
d="M11.47 3.841a.75.75 0 0 1 1.06 0l8.69 8.69a.75.75 0 1 0 1.06-1.061l-8.689-8.69a2.25 2.25 0 0 0-3.182 0l-8.69 8.69a.75.75 0 1 0 1.061 1.06l8.69-8.689Z"
|
||||
id="path1"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
d="m12 5.432 8.159 8.159c.03.03.06.058.091.086v6.198c0 1.035-.84 1.875-1.875 1.875H15a.75.75 0 0 1-.75-.75v-4.5a.75.75 0 0 0-.75-.75h-3a.75.75 0 0 0-.75.75V21a.75.75 0 0 1-.75.75H5.625a1.875 1.875 0 0 1-1.875-1.875v-6.198a2.29 2.29 0 0 0 .091-.086L12 5.432Z"
|
||||
id="path2"
|
||||
style="fill:#ffffff" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -7,11 +7,14 @@
|
||||
<file>icons/circle.svg</file>
|
||||
<file>icons/save-sketch.svg</file>
|
||||
<file>icons/cursor.svg</file>
|
||||
<file>icons/home.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/shaders">
|
||||
<file alias="simple.vert">src/shaders/simple.vert</file>
|
||||
<file alias="simple.frag">src/shaders/simple.frag</file>
|
||||
<file alias="texture.vert">src/shaders/texture.vert</file>
|
||||
<file alias="texture.frag">src/shaders/texture.frag</file>
|
||||
<file alias="lit.vert">src/shaders/lit.vert</file>
|
||||
<file alias="lit.frag">src/shaders/lit.frag</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "Document.h"
|
||||
#include "SketchFeature.h"
|
||||
#include "SketchLine.h"
|
||||
#include "SketchRectangle.h"
|
||||
#include "SketchCircle.h"
|
||||
#include "MainWindow.h"
|
||||
|
||||
#include <QInputDialog>
|
||||
@@ -140,8 +142,25 @@ void ApplicationController::addLine(const gp_Pnt& start, const gp_Pnt& end)
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationController::addRectangle(const gp_Pnt& corner1, const gp_Pnt& corner2)
|
||||
{
|
||||
if (m_activeSketch) {
|
||||
m_activeSketch->addObject(new SketchRectangle(corner1, corner2));
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationController::addCircle(const gp_Pnt& center, double radius)
|
||||
{
|
||||
if (m_activeSketch) {
|
||||
m_activeSketch->addObject(new SketchCircle(center, radius));
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationController::endSketch()
|
||||
{
|
||||
if (m_activeSketch) {
|
||||
m_activeSketch->buildShape();
|
||||
}
|
||||
m_activeSketch = nullptr;
|
||||
setActiveTool(ToolType::None);
|
||||
emit sketchModeEnded();
|
||||
|
||||
@@ -32,6 +32,8 @@ public:
|
||||
public slots:
|
||||
void setActiveTool(ToolType tool);
|
||||
void addLine(const gp_Pnt& start, const gp_Pnt& end);
|
||||
void addRectangle(const gp_Pnt& corner1, const gp_Pnt& corner2);
|
||||
void addCircle(const gp_Pnt& center, double radius);
|
||||
void newDocument();
|
||||
bool openDocument();
|
||||
bool saveDocument();
|
||||
|
||||
355
src/Camera.cpp
355
src/Camera.cpp
@@ -1,9 +1,14 @@
|
||||
#include "Camera.h"
|
||||
#include "ViewportWidget.h"
|
||||
#include <QApplication>
|
||||
#include <QWheelEvent>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QParallelAnimationGroup>
|
||||
#include <QtMath>
|
||||
|
||||
Camera::Camera(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_lastPos = QPoint();
|
||||
// Set initial view to an isometric angle on the XY plane
|
||||
m_xRot = 30 * 16;
|
||||
m_yRot = -45 * 16;
|
||||
@@ -12,29 +17,71 @@ Camera::Camera(QObject *parent) : QObject(parent)
|
||||
m_panY = 0.0f;
|
||||
}
|
||||
|
||||
void Camera::processMouseMovement(QMouseEvent* event, const QPoint& lastPos)
|
||||
void Camera::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
int dx = event->pos().x() - lastPos.x();
|
||||
int dy = event->pos().y() - lastPos.y();
|
||||
m_lastPos = event->pos();
|
||||
}
|
||||
|
||||
if (event->buttons() & Qt::MiddleButton) {
|
||||
void Camera::mouseMoveEvent(QMouseEvent* event, int viewportHeight)
|
||||
{
|
||||
int dx = event->pos().x() - m_lastPos.x();
|
||||
int dy = event->pos().y() - m_lastPos.y();
|
||||
|
||||
if ((event->buttons() & Qt::MiddleButton) || (event->buttons() == (Qt::LeftButton | Qt::RightButton))) {
|
||||
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) {
|
||||
// Pan
|
||||
setPanX(m_panX + dx / 100.0f);
|
||||
setPanY(m_panY - dy / 100.0f);
|
||||
if (viewportHeight == 0) viewportHeight = 1;
|
||||
|
||||
// This logic is based on a 45-degree field of view.
|
||||
float fov = 45.0f;
|
||||
float dist = -m_zoom;
|
||||
|
||||
float world_height = 2.0f * dist * tan(qDegreesToRadians(fov / 2.0f));
|
||||
float pixels_to_world = world_height / viewportHeight;
|
||||
|
||||
setPanX(m_panX + dx * pixels_to_world);
|
||||
setPanY(m_panY - dy * pixels_to_world);
|
||||
} else {
|
||||
// Rotate
|
||||
setXRotation(m_xRot + 8 * dy);
|
||||
setYRotation(m_yRot + 8 * dx);
|
||||
}
|
||||
}
|
||||
m_lastPos = event->pos();
|
||||
}
|
||||
|
||||
void Camera::processWheel(QWheelEvent* event)
|
||||
void Camera::wheelEvent(QWheelEvent* event, const QVector3D& worldPos)
|
||||
{
|
||||
QPoint numDegrees = event->angleDelta() / 8;
|
||||
if (!numDegrees.isNull()) {
|
||||
setZoom(m_zoom + numDegrees.y() / 5.0f);
|
||||
// Make zoom speed proportional to distance, with a minimum speed and a cap.
|
||||
// The factors are chosen to match the original zoom speed at the default zoom level.
|
||||
float dist = -m_zoom;
|
||||
dist = qMin(dist, 200.0f); // Cap distance to avoid crazy fast zoom out.
|
||||
float zoomFactor = dist * 0.009f + 0.02f;
|
||||
float zoomAmount = numDegrees.y() * zoomFactor;
|
||||
|
||||
float oldZoom = m_zoom;
|
||||
float newZoom = oldZoom + zoomAmount;
|
||||
|
||||
QMatrix4x4 rotation;
|
||||
rotation.rotate(m_xRot / 16.0f, 1, 0, 0);
|
||||
rotation.rotate(m_yRot / 16.0f, 0, 1, 0);
|
||||
QVector3D p_camera = rotation.map(worldPos);
|
||||
|
||||
if (std::abs(p_camera.z() + oldZoom) < 1e-6) {
|
||||
setZoom(newZoom);
|
||||
return;
|
||||
}
|
||||
|
||||
float ratio = (p_camera.z() + newZoom) / (p_camera.z() + oldZoom);
|
||||
|
||||
float newPanX = (p_camera.x() + m_panX) * ratio - p_camera.x();
|
||||
float newPanY = (p_camera.y() + m_panY) * ratio - p_camera.y();
|
||||
|
||||
setZoom(newZoom);
|
||||
setPanX(newPanX);
|
||||
setPanY(newPanY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +89,15 @@ QMatrix4x4 Camera::modelViewMatrix() const
|
||||
{
|
||||
QMatrix4x4 model;
|
||||
model.translate(m_panX, m_panY, m_zoom);
|
||||
|
||||
if (m_isRotating) {
|
||||
model.translate(m_rotationPivot);
|
||||
}
|
||||
model.rotate(m_xRot / 16.0f, 1, 0, 0);
|
||||
model.rotate(m_yRot / 16.0f, 0, 1, 0);
|
||||
if (m_isRotating) {
|
||||
model.translate(-m_rotationPivot);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -68,6 +122,14 @@ void Camera::setYRotation(float angle)
|
||||
float Camera::zoom() const { return m_zoom; }
|
||||
void Camera::setZoom(float value)
|
||||
{
|
||||
const float max_dist = 5000.0f;
|
||||
float remaining_dist_sq = max_dist * max_dist - m_panX * m_panX - m_panY * m_panY;
|
||||
if (remaining_dist_sq < 0) {
|
||||
remaining_dist_sq = 0;
|
||||
}
|
||||
float max_val = qSqrt(remaining_dist_sq);
|
||||
value = qBound(-max_val, value, 0.0f); // Zoom is negative or zero
|
||||
|
||||
if (value != m_zoom) {
|
||||
m_zoom = value;
|
||||
emit cameraChanged();
|
||||
@@ -77,6 +139,14 @@ void Camera::setZoom(float value)
|
||||
float Camera::panX() const { return m_panX; }
|
||||
void Camera::setPanX(float value)
|
||||
{
|
||||
const float max_dist = 5000.0f;
|
||||
float remaining_dist_sq = max_dist * max_dist - m_panY * m_panY - m_zoom * m_zoom;
|
||||
if (remaining_dist_sq < 0) {
|
||||
remaining_dist_sq = 0;
|
||||
}
|
||||
float max_val = qSqrt(remaining_dist_sq);
|
||||
value = qBound(-max_val, value, max_val);
|
||||
|
||||
if (value != m_panX) {
|
||||
m_panX = value;
|
||||
emit cameraChanged();
|
||||
@@ -86,6 +156,14 @@ void Camera::setPanX(float value)
|
||||
float Camera::panY() const { return m_panY; }
|
||||
void Camera::setPanY(float value)
|
||||
{
|
||||
const float max_dist = 5000.0f;
|
||||
float remaining_dist_sq = max_dist * max_dist - m_panX * m_panX - m_zoom * m_zoom;
|
||||
if (remaining_dist_sq < 0) {
|
||||
remaining_dist_sq = 0;
|
||||
}
|
||||
float max_val = qSqrt(remaining_dist_sq);
|
||||
value = qBound(-max_val, value, max_val);
|
||||
|
||||
if (value != m_panY) {
|
||||
m_panY = value;
|
||||
emit cameraChanged();
|
||||
@@ -109,3 +187,264 @@ void Camera::restoreState()
|
||||
setPanY(m_savedPanY);
|
||||
setZoom(m_savedZoom);
|
||||
}
|
||||
|
||||
void Camera::animateToPlaneView(int 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;
|
||||
}
|
||||
|
||||
auto* animGroup = new QParallelAnimationGroup(this);
|
||||
|
||||
const float full_circle = 360.0f * 16.0f;
|
||||
|
||||
float currentXRot = xRotation();
|
||||
float diffX = targetXRot - currentXRot;
|
||||
diffX = fmod(diffX, full_circle);
|
||||
if (diffX > full_circle / 2.0f) {
|
||||
diffX -= full_circle;
|
||||
} else if (diffX < -full_circle / 2.0f) {
|
||||
diffX += full_circle;
|
||||
}
|
||||
|
||||
auto* xRotAnim = new QPropertyAnimation(this, "xRotation");
|
||||
xRotAnim->setDuration(300);
|
||||
xRotAnim->setStartValue(currentXRot);
|
||||
xRotAnim->setEndValue(currentXRot + diffX);
|
||||
xRotAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(xRotAnim);
|
||||
|
||||
float currentYRot = yRotation();
|
||||
float diffY = targetYRot - currentYRot;
|
||||
diffY = fmod(diffY, full_circle);
|
||||
if (diffY > full_circle / 2.0f) {
|
||||
diffY -= full_circle;
|
||||
} else if (diffY < -full_circle / 2.0f) {
|
||||
diffY += full_circle;
|
||||
}
|
||||
|
||||
auto* yRotAnim = new QPropertyAnimation(this, "yRotation");
|
||||
yRotAnim->setDuration(300);
|
||||
yRotAnim->setStartValue(currentYRot);
|
||||
yRotAnim->setEndValue(currentYRot + diffY);
|
||||
yRotAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(yRotAnim);
|
||||
|
||||
auto* panXAnim = new QPropertyAnimation(this, "panX");
|
||||
panXAnim->setDuration(300);
|
||||
panXAnim->setStartValue(panX());
|
||||
panXAnim->setEndValue(0.0f);
|
||||
panXAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(panXAnim);
|
||||
|
||||
auto* panYAnim = new QPropertyAnimation(this, "panY");
|
||||
panYAnim->setDuration(300);
|
||||
panYAnim->setStartValue(panY());
|
||||
panYAnim->setEndValue(0.0f);
|
||||
panYAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(panYAnim);
|
||||
|
||||
auto* zoomAnim = new QPropertyAnimation(this, "zoom");
|
||||
zoomAnim->setDuration(300);
|
||||
zoomAnim->setStartValue(zoom());
|
||||
zoomAnim->setEndValue(-20.0f);
|
||||
zoomAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(zoomAnim);
|
||||
|
||||
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
}
|
||||
|
||||
void Camera::animateToHomeView()
|
||||
{
|
||||
auto* animGroup = new QParallelAnimationGroup(this);
|
||||
|
||||
const float full_circle = 360.0f * 16.0f;
|
||||
|
||||
float currentXRot = xRotation();
|
||||
float targetXRot = 30 * 16;
|
||||
float diffX = targetXRot - currentXRot;
|
||||
diffX = fmod(diffX, full_circle);
|
||||
if (diffX > full_circle / 2.0f) {
|
||||
diffX -= full_circle;
|
||||
} else if (diffX < -full_circle / 2.0f) {
|
||||
diffX += full_circle;
|
||||
}
|
||||
|
||||
auto* xRotAnim = new QPropertyAnimation(this, "xRotation");
|
||||
xRotAnim->setDuration(300);
|
||||
xRotAnim->setStartValue(currentXRot);
|
||||
xRotAnim->setEndValue(currentXRot + diffX);
|
||||
xRotAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(xRotAnim);
|
||||
|
||||
float currentYRot = yRotation();
|
||||
float targetYRot = -45 * 16;
|
||||
float diffY = targetYRot - currentYRot;
|
||||
diffY = fmod(diffY, full_circle);
|
||||
if (diffY > full_circle / 2.0f) {
|
||||
diffY -= full_circle;
|
||||
} else if (diffY < -full_circle / 2.0f) {
|
||||
diffY += full_circle;
|
||||
}
|
||||
|
||||
auto* yRotAnim = new QPropertyAnimation(this, "yRotation");
|
||||
yRotAnim->setDuration(300);
|
||||
yRotAnim->setStartValue(currentYRot);
|
||||
yRotAnim->setEndValue(currentYRot + diffY);
|
||||
yRotAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(yRotAnim);
|
||||
|
||||
auto* panXAnim = new QPropertyAnimation(this, "panX");
|
||||
panXAnim->setDuration(300);
|
||||
panXAnim->setStartValue(panX());
|
||||
panXAnim->setEndValue(0.0f);
|
||||
panXAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(panXAnim);
|
||||
|
||||
auto* panYAnim = new QPropertyAnimation(this, "panY");
|
||||
panYAnim->setDuration(300);
|
||||
panYAnim->setStartValue(panY());
|
||||
panYAnim->setEndValue(0.0f);
|
||||
panYAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(panYAnim);
|
||||
|
||||
auto* zoomAnim = new QPropertyAnimation(this, "zoom");
|
||||
zoomAnim->setDuration(300);
|
||||
zoomAnim->setStartValue(zoom());
|
||||
zoomAnim->setEndValue(-20.0f);
|
||||
zoomAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(zoomAnim);
|
||||
|
||||
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
}
|
||||
|
||||
void Camera::startRotation(const QVector3D& pivot)
|
||||
{
|
||||
m_rotationPivot = pivot;
|
||||
if (m_rotationPivot.length() > 1000.0f) {
|
||||
m_rotationPivot = m_rotationPivot.normalized() * 1000.0f;
|
||||
}
|
||||
m_stableZoom = m_zoom;
|
||||
|
||||
QMatrix4x4 rotation;
|
||||
rotation.rotate(m_xRot / 16.0f, 1, 0, 0);
|
||||
rotation.rotate(m_yRot / 16.0f, 0, 1, 0);
|
||||
|
||||
QVector3D p_rotated = rotation.map(m_rotationPivot);
|
||||
QVector3D p_diff = p_rotated - m_rotationPivot;
|
||||
|
||||
setPanX(m_panX + p_diff.x());
|
||||
setPanY(m_panY + p_diff.y());
|
||||
setZoom(m_zoom + p_diff.z());
|
||||
|
||||
m_isRotating = true;
|
||||
}
|
||||
|
||||
void Camera::stopRotation()
|
||||
{
|
||||
if (!m_isRotating) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMatrix4x4 rotation;
|
||||
rotation.rotate(m_xRot / 16.0f, 1, 0, 0);
|
||||
rotation.rotate(m_yRot / 16.0f, 0, 1, 0);
|
||||
|
||||
QVector3D p_rotated = rotation.map(m_rotationPivot);
|
||||
QVector3D p_diff = p_rotated - m_rotationPivot;
|
||||
|
||||
setPanX(m_panX - p_diff.x());
|
||||
setPanY(m_panY - p_diff.y());
|
||||
setZoom(m_zoom - p_diff.z());
|
||||
|
||||
m_isRotating = false;
|
||||
}
|
||||
|
||||
void Camera::animateRestoreState()
|
||||
{
|
||||
auto* animGroup = new QParallelAnimationGroup(this);
|
||||
|
||||
const float full_circle = 360.0f * 16.0f;
|
||||
|
||||
float currentXRot = xRotation();
|
||||
float targetXRot = savedXRot();
|
||||
float diffX = targetXRot - currentXRot;
|
||||
diffX = fmod(diffX, full_circle);
|
||||
if (diffX > full_circle / 2.0f) {
|
||||
diffX -= full_circle;
|
||||
} else if (diffX < -full_circle / 2.0f) {
|
||||
diffX += full_circle;
|
||||
}
|
||||
|
||||
auto* xRotAnim = new QPropertyAnimation(this, "xRotation");
|
||||
xRotAnim->setDuration(300);
|
||||
xRotAnim->setStartValue(currentXRot);
|
||||
xRotAnim->setEndValue(currentXRot + diffX);
|
||||
xRotAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(xRotAnim);
|
||||
|
||||
float currentYRot = yRotation();
|
||||
float targetYRot = savedYRot();
|
||||
float diffY = targetYRot - currentYRot;
|
||||
diffY = fmod(diffY, full_circle);
|
||||
if (diffY > full_circle / 2.0f) {
|
||||
diffY -= full_circle;
|
||||
} else if (diffY < -full_circle / 2.0f) {
|
||||
diffY += full_circle;
|
||||
}
|
||||
|
||||
auto* yRotAnim = new QPropertyAnimation(this, "yRotation");
|
||||
yRotAnim->setDuration(300);
|
||||
yRotAnim->setStartValue(currentYRot);
|
||||
yRotAnim->setEndValue(currentYRot + diffY);
|
||||
yRotAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(yRotAnim);
|
||||
|
||||
auto* panXAnim = new QPropertyAnimation(this, "panX");
|
||||
panXAnim->setDuration(300);
|
||||
panXAnim->setStartValue(panX());
|
||||
panXAnim->setEndValue(savedPanX());
|
||||
panXAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(panXAnim);
|
||||
|
||||
auto* panYAnim = new QPropertyAnimation(this, "panY");
|
||||
panYAnim->setDuration(300);
|
||||
panYAnim->setStartValue(panY());
|
||||
panYAnim->setEndValue(savedPanY());
|
||||
panYAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(panYAnim);
|
||||
|
||||
auto* zoomAnim = new QPropertyAnimation(this, "zoom");
|
||||
zoomAnim->setDuration(300);
|
||||
zoomAnim->setStartValue(zoom());
|
||||
zoomAnim->setEndValue(savedZoom());
|
||||
zoomAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(zoomAnim);
|
||||
|
||||
connect(animGroup, &QParallelAnimationGroup::finished, this, &Camera::restoreStateAnimationFinished);
|
||||
|
||||
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
}
|
||||
|
||||
float Camera::uiCameraDistance() const
|
||||
{
|
||||
if (m_isRotating) {
|
||||
return m_stableZoom;
|
||||
}
|
||||
return m_zoom;
|
||||
}
|
||||
|
||||
21
src/Camera.h
21
src/Camera.h
@@ -19,8 +19,9 @@ class Camera : public QObject
|
||||
public:
|
||||
explicit Camera(QObject *parent = nullptr);
|
||||
|
||||
void processMouseMovement(QMouseEvent* event, const QPoint& lastPos);
|
||||
void processWheel(QWheelEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event, int viewportHeight);
|
||||
void wheelEvent(QWheelEvent* event, const QVector3D& worldPos);
|
||||
|
||||
QMatrix4x4 modelViewMatrix() const;
|
||||
|
||||
@@ -38,6 +39,16 @@ public:
|
||||
void saveState();
|
||||
void restoreState();
|
||||
|
||||
void animateToPlaneView(int plane);
|
||||
void animateRestoreState();
|
||||
void animateToHomeView();
|
||||
|
||||
void startRotation(const QVector3D& pivot);
|
||||
void stopRotation();
|
||||
bool isRotating() const { return m_isRotating; }
|
||||
const QVector3D& rotationPivot() const { return m_rotationPivot; }
|
||||
float uiCameraDistance() const;
|
||||
|
||||
float savedXRot() const { return m_savedXRot; }
|
||||
float savedYRot() const { return m_savedYRot; }
|
||||
float savedZoom() const { return m_savedZoom; }
|
||||
@@ -46,14 +57,20 @@ public:
|
||||
|
||||
signals:
|
||||
void cameraChanged();
|
||||
void restoreStateAnimationFinished();
|
||||
|
||||
private:
|
||||
QPoint m_lastPos;
|
||||
float m_xRot;
|
||||
float m_yRot;
|
||||
float m_zoom;
|
||||
float m_panX;
|
||||
float m_panY;
|
||||
|
||||
QVector3D m_rotationPivot;
|
||||
bool m_isRotating = false;
|
||||
float m_stableZoom = 0;
|
||||
|
||||
float m_savedXRot = 0;
|
||||
float m_savedYRot = 0;
|
||||
float m_savedZoom = -5.0f;
|
||||
|
||||
267
src/CircleTool.cpp
Normal file
267
src/CircleTool.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
#include "CircleTool.h"
|
||||
#include "ViewportWidget.h"
|
||||
#include "Camera.h"
|
||||
#include <QMouseEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
#include <QVector>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <cmath>
|
||||
#include <QtMath>
|
||||
|
||||
CircleTool::CircleTool(ViewportWidget* viewport)
|
||||
: SketchTool(viewport)
|
||||
{
|
||||
}
|
||||
|
||||
void CircleTool::activate()
|
||||
{
|
||||
SketchTool::activate();
|
||||
m_dimensionModes.clear();
|
||||
m_dimensionModes << "diameter";
|
||||
m_dimensionPropertyNames.clear();
|
||||
m_dimensionPropertyNames["diameter"] = "diameterInput";
|
||||
|
||||
m_viewport->setProperty("diameterInput", "");
|
||||
m_viewport->setProperty("dimensionEditMode", "diameter");
|
||||
}
|
||||
|
||||
void CircleTool::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
gp_Pnt p;
|
||||
if (!m_isDefining) {
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
p.SetCoord(0, 0, 0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
p = m_viewport->snapVertex();
|
||||
} else {
|
||||
QVector3D worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
}
|
||||
m_centerPoint = p;
|
||||
m_isDefining = true;
|
||||
|
||||
m_dimensionModes.clear();
|
||||
m_dimensionModes << "diameter";
|
||||
m_dimensionPropertyNames.clear();
|
||||
m_dimensionPropertyNames["diameter"] = "diameterInput";
|
||||
|
||||
m_viewport->setProperty("diameterInput", "");
|
||||
m_viewport->setProperty("dimensionEditMode", "diameter");
|
||||
} else {
|
||||
QVector3D worldPos;
|
||||
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
|
||||
|
||||
QString diameterInput = m_viewport->property("diameterInput").toString();
|
||||
bool diameterFromInput = false;
|
||||
double inputDiameter = 0;
|
||||
|
||||
if (!diameterInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputDiameter = diameterInput.toDouble(&ok);
|
||||
if (ok) diameterFromInput = true;
|
||||
}
|
||||
|
||||
if (diameterFromInput) {
|
||||
QVector3D mousePos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
|
||||
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);
|
||||
}
|
||||
}
|
||||
double radius = inputDiameter / 2.0;
|
||||
worldPos = centerPos + mouseDir.normalized() * radius;
|
||||
} else {
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
emit m_viewport->circleAdded(m_centerPoint, m_centerPoint.Distance(p));
|
||||
deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void CircleTool::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
// To be implemented
|
||||
}
|
||||
|
||||
void CircleTool::finalizeCreation()
|
||||
{
|
||||
QVector3D worldPos;
|
||||
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
|
||||
|
||||
QString diameterInput = m_viewport->property("diameterInput").toString();
|
||||
bool diameterFromInput = false;
|
||||
double inputDiameter = 0;
|
||||
|
||||
if (!diameterInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputDiameter = diameterInput.toDouble(&ok);
|
||||
if (ok) diameterFromInput = true;
|
||||
}
|
||||
|
||||
if (diameterFromInput) {
|
||||
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
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);
|
||||
}
|
||||
}
|
||||
double radius = inputDiameter / 2.0;
|
||||
worldPos = centerPos + mouseDir.normalized() * radius;
|
||||
} else {
|
||||
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
}
|
||||
|
||||
gp_Pnt p;
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
|
||||
emit m_viewport->circleAdded(m_centerPoint, m_centerPoint.Distance(p));
|
||||
deactivate();
|
||||
}
|
||||
|
||||
void CircleTool::paintGL()
|
||||
{
|
||||
if (m_isDefining) {
|
||||
QVector<GLfloat> vertices;
|
||||
QVector3D worldPos;
|
||||
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
|
||||
|
||||
QString diameterInput = m_viewport->property("diameterInput").toString();
|
||||
bool diameterFromInput = false;
|
||||
double inputDiameter = 0;
|
||||
|
||||
if (!diameterInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputDiameter = diameterInput.toDouble(&ok);
|
||||
if (ok) diameterFromInput = true;
|
||||
}
|
||||
|
||||
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
|
||||
double radius;
|
||||
if (diameterFromInput) {
|
||||
radius = inputDiameter / 2.0;
|
||||
} else {
|
||||
worldPos = mousePos;
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
worldPos.setX(m_viewport->snapVertex().X()); worldPos.setY(m_viewport->snapVertex().Y()); worldPos.setZ(m_viewport->snapVertex().Z());
|
||||
}
|
||||
radius = (worldPos - centerPos).length();
|
||||
}
|
||||
|
||||
const int segments = 64;
|
||||
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;
|
||||
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
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 if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
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 { // 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();
|
||||
}
|
||||
|
||||
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, segments * 2);
|
||||
}
|
||||
}
|
||||
|
||||
void CircleTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection)
|
||||
{
|
||||
if (m_isDefining) {
|
||||
QVector3D worldPos;
|
||||
QVector3D centerPos(m_centerPoint.X(), m_centerPoint.Y(), m_centerPoint.Z());
|
||||
|
||||
QString diameterInput = m_viewport->property("diameterInput").toString();
|
||||
bool diameterFromInput = false;
|
||||
double inputDiameter = 0;
|
||||
|
||||
if (!diameterInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputDiameter = diameterInput.toDouble(&ok);
|
||||
if (ok) diameterFromInput = true;
|
||||
}
|
||||
|
||||
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
QVector3D edgePos;
|
||||
double diameter;
|
||||
|
||||
if (diameterFromInput) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
edgePos = centerPos + mouseDir.normalized() * (diameter / 2.0);
|
||||
} else {
|
||||
edgePos = mousePos;
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
edgePos.setX(0); edgePos.setY(0); edgePos.setZ(0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
edgePos.setX(m_viewport->snapVertex().X()); edgePos.setY(m_viewport->snapVertex().Y()); edgePos.setZ(m_viewport->snapVertex().Z());
|
||||
}
|
||||
diameter = (edgePos - centerPos).length() * 2.0;
|
||||
}
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
QFontMetrics fm(painter.font());
|
||||
|
||||
// Diameter dimension
|
||||
QVector3D diameterTextPos3D = (centerPos + edgePos) / 2.0f;
|
||||
QVector3D screenPosD = m_viewport->project(diameterTextPos3D, modelView, projection, m_viewport->rect());
|
||||
if (screenPosD.z() < 1.0f) {
|
||||
QString diameterText = diameterFromInput ? diameterInput : QString::number(diameter, 'f', 2);
|
||||
QRect textRect = fm.boundingRect(diameterText + "__");
|
||||
textRect.moveCenter(screenPosD.toPoint());
|
||||
if (m_viewport->property("dimensionEditMode").toString() == "diameter") {
|
||||
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255));
|
||||
} else {
|
||||
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50));
|
||||
}
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(textRect, Qt::AlignCenter, diameterText);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/CircleTool.h
Normal file
28
src/CircleTool.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef CIRCLETOOL_H
|
||||
#define CIRCLETOOL_H
|
||||
|
||||
#include "SketchTool.h"
|
||||
#include <gp_Pnt.hxx>
|
||||
|
||||
class CircleTool : public SketchTool
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CircleTool(ViewportWidget* viewport);
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
void paintGL() override;
|
||||
void paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection) override;
|
||||
|
||||
void activate() override;
|
||||
|
||||
protected:
|
||||
void finalizeCreation() override;
|
||||
|
||||
private:
|
||||
gp_Pnt m_centerPoint;
|
||||
};
|
||||
|
||||
#endif // CIRCLETOOL_H
|
||||
852
src/LineTool.cpp
Normal file
852
src/LineTool.cpp
Normal file
@@ -0,0 +1,852 @@
|
||||
#include "LineTool.h"
|
||||
#include "ViewportWidget.h"
|
||||
#include "Camera.h"
|
||||
#include <QMouseEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QtMath>
|
||||
|
||||
LineTool::LineTool(ViewportWidget* viewport)
|
||||
: SketchTool(viewport)
|
||||
{
|
||||
}
|
||||
|
||||
void LineTool::activate()
|
||||
{
|
||||
SketchTool::activate();
|
||||
m_dimensionModes << "length" << "angle";
|
||||
m_dimensionPropertyNames["length"] = "dimensionInput";
|
||||
m_dimensionPropertyNames["angle"] = "angleInput";
|
||||
|
||||
m_viewport->setProperty("dimensionInput", "");
|
||||
m_viewport->setProperty("angleInput", "");
|
||||
m_viewport->setProperty("dimensionEditMode", "length");
|
||||
m_viewport->setProperty("isChainedLine", false);
|
||||
}
|
||||
|
||||
void LineTool::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
gp_Pnt p;
|
||||
QString dimInput = m_viewport->property("dimensionInput").toString();
|
||||
QString angleInput = m_viewport->property("angleInput").toString();
|
||||
bool lengthFromInput = false;
|
||||
bool angleFromInput = false;
|
||||
double inputLength = 0;
|
||||
double inputAngleDegrees = 0;
|
||||
|
||||
if (m_isDefining) {
|
||||
if (!dimInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputLength = dimInput.toDouble(&ok);
|
||||
if (ok) lengthFromInput = true;
|
||||
}
|
||||
if (!angleInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputAngleDegrees = angleInput.toDouble(&ok);
|
||||
if (ok) angleFromInput = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_isDefining && (lengthFromInput || angleFromInput)) {
|
||||
QVector3D worldPos;
|
||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
|
||||
if (angleFromInput) {
|
||||
QVector3D refDir;
|
||||
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, 0, -1);
|
||||
}
|
||||
QVector3D currentMouseWorldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
|
||||
QVector3D mouseVec = currentMouseWorldPos - startPos;
|
||||
double mouseAngle;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x()));
|
||||
else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y()));
|
||||
double refAngle;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
|
||||
else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
|
||||
double relativeMouseAngle = mouseAngle - refAngle;
|
||||
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
|
||||
while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0;
|
||||
double snappedAngle = 0;
|
||||
if (relativeMouseAngle >= 0 && relativeMouseAngle < 90) { // Quadrant 1
|
||||
snappedAngle = inputAngleDegrees;
|
||||
} else if (relativeMouseAngle >= 90 && relativeMouseAngle <= 180) { // Quadrant 2
|
||||
snappedAngle = 180.0 - inputAngleDegrees;
|
||||
} else if (relativeMouseAngle < -90) { // Quadrant 3
|
||||
snappedAngle = -180.0 + inputAngleDegrees;
|
||||
} else { // Quadrant 4
|
||||
snappedAngle = -inputAngleDegrees;
|
||||
}
|
||||
double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle);
|
||||
QVector3D finalDir;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0);
|
||||
else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad));
|
||||
double lineLength;
|
||||
if (lengthFromInput) lineLength = inputLength;
|
||||
else {
|
||||
lineLength = QVector3D::dotProduct(mouseVec, finalDir);
|
||||
if (lineLength < 0) lineLength = 0;
|
||||
}
|
||||
worldPos = startPos + lineLength * finalDir;
|
||||
} else if (lengthFromInput) {
|
||||
QVector3D currentMouseWorldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
|
||||
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);
|
||||
}
|
||||
}
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
} else {
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
p.SetCoord(0, 0, 0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
p = m_viewport->snapVertex();
|
||||
} else {
|
||||
QVector3D worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
|
||||
if (m_viewport->isSnappingHorizontal()) {
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) worldPos.setZ(m_firstLinePoint.Z());
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setY(m_firstLinePoint.Y());
|
||||
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());
|
||||
}
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_isDefining) {
|
||||
m_firstLinePoint = p;
|
||||
m_isDefining = true;
|
||||
m_viewport->setProperty("dimensionInput", QVariant(""));
|
||||
m_viewport->setProperty("angleInput", QVariant(""));
|
||||
m_viewport->setProperty("dimensionEditMode", "length");
|
||||
m_viewport->setProperty("isChainedLine", false);
|
||||
} else {
|
||||
QVector3D start(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
QVector3D end(p.X(), p.Y(), p.Z());
|
||||
m_viewport->setProperty("previousLineDirection", QVariant::fromValue((end - start).normalized()));
|
||||
|
||||
m_viewport->addLine(m_firstLinePoint, p);
|
||||
m_firstLinePoint = p;
|
||||
m_viewport->setProperty("dimensionInput", QVariant(""));
|
||||
m_viewport->setProperty("angleInput", QVariant(""));
|
||||
m_viewport->setProperty("dimensionEditMode", "length");
|
||||
m_viewport->setProperty("isChainedLine", true);
|
||||
}
|
||||
}
|
||||
|
||||
void LineTool::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
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;
|
||||
|
||||
if (delta.length() > 1e-6) {
|
||||
const double snapAngleThreshold = qDegreesToRadians(2.0);
|
||||
double angle = 0;
|
||||
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
angle = atan2(delta.z(), delta.x());
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
angle = atan2(delta.y(), delta.x());
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) {
|
||||
angle = atan2(delta.z(), delta.y());
|
||||
}
|
||||
|
||||
if (qAbs(sin(angle)) < sin(snapAngleThreshold)) {
|
||||
m_viewport->setSnappingHorizontal(true);
|
||||
} else if (qAbs(cos(angle)) < sin(snapAngleThreshold)) {
|
||||
m_viewport->setSnappingVertical(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oldIsSnappingHorizontal != m_viewport->isSnappingHorizontal() || oldIsSnappingVertical != m_viewport->isSnappingVertical()) {
|
||||
m_viewport->update();
|
||||
}
|
||||
}
|
||||
|
||||
void LineTool::finalizeCreation()
|
||||
{
|
||||
QVector3D worldPos;
|
||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
// This is duplicated from paintGL to ensure consistent line creation
|
||||
QString dimInput = m_viewport->property("dimensionInput").toString();
|
||||
QString angleInput = m_viewport->property("angleInput").toString();
|
||||
bool lengthFromInput = false;
|
||||
bool angleFromInput = false;
|
||||
double inputLength = 0;
|
||||
double inputAngleDegrees = 0;
|
||||
|
||||
if (!dimInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputLength = dimInput.toDouble(&ok);
|
||||
if (ok) lengthFromInput = true;
|
||||
}
|
||||
if (!angleInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputAngleDegrees = angleInput.toDouble(&ok);
|
||||
if (ok) angleFromInput = true;
|
||||
}
|
||||
|
||||
if (angleFromInput) {
|
||||
QVector3D refDir;
|
||||
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, 0, -1);
|
||||
}
|
||||
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
QVector3D mouseVec = currentMouseWorldPos - startPos;
|
||||
double mouseAngle;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x()));
|
||||
else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y()));
|
||||
double refAngle;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
|
||||
else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
|
||||
double relativeMouseAngle = mouseAngle - refAngle;
|
||||
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
|
||||
while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0;
|
||||
double snappedAngle = 0;
|
||||
if (relativeMouseAngle >= 0 && relativeMouseAngle < 90) { // Quadrant 1
|
||||
snappedAngle = inputAngleDegrees;
|
||||
} else if (relativeMouseAngle >= 90 && relativeMouseAngle <= 180) { // Quadrant 2
|
||||
snappedAngle = 180.0 - inputAngleDegrees;
|
||||
} else if (relativeMouseAngle < -90) { // Quadrant 3
|
||||
snappedAngle = -180.0 + inputAngleDegrees;
|
||||
} else { // Quadrant 4
|
||||
snappedAngle = -inputAngleDegrees;
|
||||
}
|
||||
double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle);
|
||||
QVector3D finalDir;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0);
|
||||
else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad));
|
||||
double lineLength;
|
||||
if (lengthFromInput) lineLength = inputLength;
|
||||
else {
|
||||
lineLength = QVector3D::dotProduct(mouseVec, finalDir);
|
||||
if (lineLength < 0) lineLength = 0;
|
||||
}
|
||||
worldPos = startPos + lineLength * finalDir;
|
||||
} else if (lengthFromInput) {
|
||||
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
}
|
||||
|
||||
gp_Pnt p;
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
|
||||
QVector3D prevDir = (worldPos - startPos).normalized();
|
||||
m_viewport->setProperty("previousLineDirection", QVariant::fromValue(prevDir));
|
||||
|
||||
m_viewport->addLine(m_firstLinePoint, p);
|
||||
m_firstLinePoint = p;
|
||||
m_viewport->setProperty("dimensionInput", QVariant(""));
|
||||
m_viewport->setProperty("angleInput", QVariant(""));
|
||||
m_viewport->setProperty("dimensionEditMode", "length");
|
||||
m_viewport->setProperty("isChainedLine", true);
|
||||
}
|
||||
|
||||
void LineTool::paintGL()
|
||||
{
|
||||
if (m_isDefining) {
|
||||
QVector<GLfloat> vertices;
|
||||
QVector3D worldPos;
|
||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
|
||||
QString dimInput = m_viewport->property("dimensionInput").toString();
|
||||
QString angleInput = m_viewport->property("angleInput").toString();
|
||||
bool lengthFromInput = false;
|
||||
bool angleFromInput = false;
|
||||
double inputLength = 0;
|
||||
double inputAngleDegrees = 0;
|
||||
|
||||
if (!dimInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputLength = dimInput.toDouble(&ok);
|
||||
if (ok) lengthFromInput = true;
|
||||
}
|
||||
if (!angleInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputAngleDegrees = angleInput.toDouble(&ok);
|
||||
if (ok) angleFromInput = true;
|
||||
}
|
||||
|
||||
if (angleFromInput) {
|
||||
QVector3D refDir;
|
||||
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, 0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
QVector3D mouseVec = currentMouseWorldPos - startPos;
|
||||
|
||||
// Quadrant snapping
|
||||
double mouseAngle;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x()));
|
||||
else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y()));
|
||||
|
||||
double refAngle;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
|
||||
else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
|
||||
|
||||
double relativeMouseAngle = mouseAngle - refAngle;
|
||||
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
|
||||
while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0;
|
||||
|
||||
double snappedAngle = 0;
|
||||
if (relativeMouseAngle >= 0 && relativeMouseAngle < 90) { // Quadrant 1
|
||||
snappedAngle = inputAngleDegrees;
|
||||
} else if (relativeMouseAngle >= 90 && relativeMouseAngle <= 180) { // Quadrant 2
|
||||
snappedAngle = 180.0 - inputAngleDegrees;
|
||||
} else if (relativeMouseAngle < -90) { // Quadrant 3
|
||||
snappedAngle = -180.0 + inputAngleDegrees;
|
||||
} else { // Quadrant 4
|
||||
snappedAngle = -inputAngleDegrees;
|
||||
}
|
||||
|
||||
double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle);
|
||||
QVector3D finalDir;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0);
|
||||
else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad));
|
||||
|
||||
double lineLength;
|
||||
if (lengthFromInput) {
|
||||
lineLength = inputLength;
|
||||
} else {
|
||||
lineLength = QVector3D::dotProduct(mouseVec, finalDir);
|
||||
if (lineLength < 0) lineLength = 0;
|
||||
}
|
||||
worldPos = startPos + lineLength * finalDir;
|
||||
|
||||
} else if (lengthFromInput) {
|
||||
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||
} 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.setZ(m_firstLinePoint.Z());
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setY(m_firstLinePoint.Y());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
vertices << m_firstLinePoint.X() << m_firstLinePoint.Y() << m_firstLinePoint.Z();
|
||||
vertices << worldPos.x() << worldPos.y() << worldPos.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, 2);
|
||||
|
||||
// Draw dimension line
|
||||
QVector3D lineVec = worldPos - startPos;
|
||||
float lineLength = lineVec.length();
|
||||
|
||||
if (lineLength > 1e-6) {
|
||||
double refAngle, lineAngle, angleDiff;
|
||||
{
|
||||
QVector3D refDir;
|
||||
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, 0, -1);
|
||||
}
|
||||
|
||||
if (angleFromInput) {
|
||||
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
QVector3D mouseVec = currentMouseWorldPos - startPos;
|
||||
|
||||
double mouseAngle;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x()));
|
||||
else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y()));
|
||||
|
||||
double refAngleForQuadrant;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
|
||||
else refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
|
||||
|
||||
double relativeMouseAngle = mouseAngle - refAngleForQuadrant;
|
||||
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
|
||||
while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0;
|
||||
|
||||
if (relativeMouseAngle >= 90 || relativeMouseAngle < -90) {
|
||||
refDir = -refDir;
|
||||
}
|
||||
} else {
|
||||
if (m_viewport->property("isChainedLine").toBool()) {
|
||||
refDir = -refDir;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
refAngle = atan2(refDir.z(), refDir.x());
|
||||
lineAngle = atan2(lineVec.z(), lineVec.x());
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
refAngle = atan2(refDir.y(), refDir.x());
|
||||
lineAngle = atan2(lineVec.y(), lineVec.x());
|
||||
} else { // YZ
|
||||
refAngle = atan2(refDir.z(), refDir.y());
|
||||
lineAngle = atan2(lineVec.z(), lineVec.y());
|
||||
}
|
||||
|
||||
angleDiff = lineAngle - refAngle;
|
||||
while (angleDiff <= -M_PI) angleDiff += 2 * M_PI;
|
||||
while (angleDiff > M_PI) angleDiff -= 2 * M_PI;
|
||||
lineAngle = refAngle + angleDiff;
|
||||
}
|
||||
|
||||
vertices.clear();
|
||||
|
||||
QVector3D perpVec;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
perpVec = QVector3D(-lineVec.z(), 0, lineVec.x()).normalized();
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
perpVec = QVector3D(-lineVec.y(), lineVec.x(), 0).normalized();
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) {
|
||||
perpVec = QVector3D(0, -lineVec.z(), lineVec.y()).normalized();
|
||||
}
|
||||
|
||||
if (angleDiff < 0) {
|
||||
perpVec = -perpVec;
|
||||
}
|
||||
|
||||
float offset = 0.05f * -m_viewport->camera()->zoom();
|
||||
QVector3D dimStart = startPos + offset * perpVec;
|
||||
QVector3D dimEnd = worldPos + offset * perpVec;
|
||||
|
||||
vertices << dimStart.x() << dimStart.y() << dimStart.z();
|
||||
vertices << dimEnd.x() << dimEnd.y() << dimEnd.z();
|
||||
|
||||
float arrowLength = 0.02f * -m_viewport->camera()->zoom();
|
||||
float arrowWidth = 0.005f * -m_viewport->camera()->zoom();
|
||||
QVector3D lineDir = lineVec.normalized();
|
||||
|
||||
QVector3D arrow_base_end = dimEnd - arrowLength * lineDir;
|
||||
QVector3D arrowP1_end = arrow_base_end + arrowWidth * perpVec;
|
||||
QVector3D arrowP2_end = arrow_base_end - arrowWidth * perpVec;
|
||||
vertices << dimEnd.x() << dimEnd.y() << dimEnd.z();
|
||||
vertices << arrowP1_end.x() << arrowP1_end.y() << arrowP1_end.z();
|
||||
vertices << dimEnd.x() << dimEnd.y() << dimEnd.z();
|
||||
vertices << arrowP2_end.x() << arrowP2_end.y() << arrowP2_end.z();
|
||||
|
||||
QVector3D arrow_base_start = dimStart + arrowLength * lineDir;
|
||||
QVector3D arrowP1_start = arrow_base_start + arrowWidth * perpVec;
|
||||
QVector3D arrowP2_start = arrow_base_start - arrowWidth * perpVec;
|
||||
vertices << dimStart.x() << dimStart.y() << dimStart.z();
|
||||
vertices << arrowP1_start.x() << arrowP1_start.y() << arrowP1_start.z();
|
||||
vertices << dimStart.x() << dimStart.y() << dimStart.z();
|
||||
vertices << arrowP2_start.x() << arrowP2_start.y() << arrowP2_start.z();
|
||||
|
||||
m_viewport->shaderProgram()->setUniformValue(m_viewport->colorLoc(), QVector4D(0.7f, 0.7f, 0.7f, 1.0f));
|
||||
glLineWidth(1.0f);
|
||||
m_viewport->vbo().bind();
|
||||
m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINES, 0, vertices.size() / 3);
|
||||
glLineWidth(2.0f);
|
||||
|
||||
// Draw angle dimension
|
||||
vertices.clear();
|
||||
const int numSegments = 30;
|
||||
const float radius = 0.1f * -m_viewport->camera()->zoom();
|
||||
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), 0, sin(angle));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) p = startPos + radius * QVector3D(cos(angle), sin(angle), 0);
|
||||
else p = startPos + radius * QVector3D(0, cos(angle), sin(angle));
|
||||
vertices << p.x() << p.y() << p.z();
|
||||
}
|
||||
glLineWidth(1.0f);
|
||||
m_viewport->vbo().bind();
|
||||
m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINE_STRIP, 0, vertices.size() / 3);
|
||||
|
||||
// Arrowheads for arc
|
||||
QVector<GLfloat> arrowVertices;
|
||||
float arcArrowLength = 0.02f * -m_viewport->camera()->zoom();
|
||||
float arcArrowWidth = 0.005f * -m_viewport->camera()->zoom();
|
||||
|
||||
double sign = (angleDiff >= 0) ? 1.0 : -1.0;
|
||||
|
||||
// End arrowhead
|
||||
QVector3D endPoint(vertices[vertices.size()-3], vertices[vertices.size()-2], vertices[vertices.size()-1]);
|
||||
double endAngle = lineAngle;
|
||||
QVector3D radialDir_end, tangentDir_end;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
radialDir_end = QVector3D(cos(endAngle), 0, sin(endAngle));
|
||||
tangentDir_end = QVector3D(-sin(endAngle), 0, cos(endAngle));
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
radialDir_end = QVector3D(cos(endAngle), sin(endAngle), 0);
|
||||
tangentDir_end = QVector3D(-sin(endAngle), cos(endAngle), 0);
|
||||
} else {
|
||||
radialDir_end = QVector3D(0, cos(endAngle), sin(endAngle));
|
||||
tangentDir_end = QVector3D(0, -sin(endAngle), cos(endAngle));
|
||||
}
|
||||
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;
|
||||
arrowVertices << endPoint.x() << endPoint.y() << endPoint.z() << arc_arrowP1_end.x() << arc_arrowP1_end.y() << arc_arrowP1_end.z();
|
||||
arrowVertices << endPoint.x() << endPoint.y() << endPoint.z() << arc_arrowP2_end.x() << arc_arrowP2_end.y() << arc_arrowP2_end.z();
|
||||
|
||||
// Start arrowhead
|
||||
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), 0, sin(startAngle));
|
||||
tangentDir_start = QVector3D(-sin(startAngle), 0, cos(startAngle));
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
radialDir_start = QVector3D(cos(startAngle), sin(startAngle), 0);
|
||||
tangentDir_start = QVector3D(-sin(startAngle), cos(startAngle), 0);
|
||||
} else {
|
||||
radialDir_start = QVector3D(0, cos(startAngle), sin(startAngle));
|
||||
tangentDir_start = QVector3D(0, -sin(startAngle), cos(startAngle));
|
||||
}
|
||||
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;
|
||||
arrowVertices << startPoint.x() << startPoint.y() << startPoint.z() << arc_arrowP1_start.x() << arc_arrowP1_start.y() << arc_arrowP1_start.z();
|
||||
arrowVertices << startPoint.x() << startPoint.y() << startPoint.z() << arc_arrowP2_start.x() << arc_arrowP2_start.y() << arc_arrowP2_start.z();
|
||||
|
||||
m_viewport->vbo().bind();
|
||||
m_viewport->vbo().allocate(arrowVertices.constData(), arrowVertices.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINES, 0, arrowVertices.size() / 3);
|
||||
glLineWidth(2.0f);
|
||||
}
|
||||
|
||||
if (!lengthFromInput && !angleFromInput && (m_viewport->isSnappingHorizontal() || m_viewport->isSnappingVertical())) {
|
||||
vertices.clear();
|
||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
QVector3D midPoint = (startPos + worldPos) / 2.0;
|
||||
const float indicatorSize = 0.02f * -m_viewport->camera()->zoom();
|
||||
const float indicatorOffset = 0.02f * -m_viewport->camera()->zoom();
|
||||
if (m_viewport->isSnappingHorizontal()) {
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
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::XZ) {
|
||||
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::YZ) {
|
||||
vertices << midPoint.x() << midPoint.y() - indicatorSize << midPoint.z() + indicatorOffset;
|
||||
vertices << midPoint.x() << midPoint.y() + indicatorSize << midPoint.z() + indicatorOffset;
|
||||
}
|
||||
} else { // m_isSnappingVertical
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
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::XZ) {
|
||||
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::YZ) {
|
||||
vertices << midPoint.x() << midPoint.y() + indicatorOffset << midPoint.z() - indicatorSize;
|
||||
vertices << midPoint.x() << midPoint.y() + indicatorOffset << midPoint.z() + indicatorSize;
|
||||
}
|
||||
}
|
||||
m_viewport->vbo().bind();
|
||||
m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINES, 0, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LineTool::paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection)
|
||||
{
|
||||
if (m_isDefining) {
|
||||
QVector3D worldPos;
|
||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
QString dimText;
|
||||
QString angleText;
|
||||
|
||||
QString dimInput = m_viewport->property("dimensionInput").toString();
|
||||
QString angleInput = m_viewport->property("angleInput").toString();
|
||||
bool lengthFromInput = false;
|
||||
bool angleFromInput = false;
|
||||
double inputLength = 0;
|
||||
double inputAngleDegrees = 0;
|
||||
double lineLength = 0;
|
||||
|
||||
if (!dimInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputLength = dimInput.toDouble(&ok);
|
||||
if (ok) lengthFromInput = true;
|
||||
}
|
||||
if (!angleInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputAngleDegrees = angleInput.toDouble(&ok);
|
||||
if (ok) angleFromInput = true;
|
||||
}
|
||||
|
||||
if (angleFromInput) {
|
||||
QVector3D refDir;
|
||||
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, 0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
QVector3D mouseVec = currentMouseWorldPos - startPos;
|
||||
|
||||
double mouseAngle;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVec.y(), mouseVec.x()));
|
||||
else mouseAngle = qRadiansToDegrees(atan2(mouseVec.z(), mouseVec.y()));
|
||||
|
||||
double refAngle;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngle = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
|
||||
else refAngle = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
|
||||
|
||||
double relativeMouseAngle = mouseAngle - refAngle;
|
||||
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
|
||||
while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0;
|
||||
|
||||
double snappedAngle = 0;
|
||||
if (relativeMouseAngle >= 0 && relativeMouseAngle < 90) { // Quadrant 1
|
||||
snappedAngle = inputAngleDegrees;
|
||||
} else if (relativeMouseAngle >= 90 && relativeMouseAngle <= 180) { // Quadrant 2
|
||||
snappedAngle = 180.0 - inputAngleDegrees;
|
||||
} else if (relativeMouseAngle < -90) { // Quadrant 3
|
||||
snappedAngle = -180.0 + inputAngleDegrees;
|
||||
} else { // Quadrant 4
|
||||
snappedAngle = -inputAngleDegrees;
|
||||
}
|
||||
|
||||
double finalAngleRad = qDegreesToRadians(refAngle + snappedAngle);
|
||||
QVector3D finalDir;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) finalDir = QVector3D(cos(finalAngleRad), 0, sin(finalAngleRad));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) finalDir = QVector3D(cos(finalAngleRad), sin(finalAngleRad), 0);
|
||||
else finalDir = QVector3D(0, cos(finalAngleRad), sin(finalAngleRad));
|
||||
|
||||
if (lengthFromInput) {
|
||||
lineLength = inputLength;
|
||||
} else {
|
||||
lineLength = QVector3D::dotProduct(mouseVec, finalDir);
|
||||
if (lineLength < 0) lineLength = 0;
|
||||
}
|
||||
worldPos = startPos + lineLength * finalDir;
|
||||
|
||||
} else if (lengthFromInput) {
|
||||
lineLength = inputLength;
|
||||
QVector3D currentMouseWorldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||
} 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.setZ(m_firstLinePoint.Z());
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) worldPos.setY(m_firstLinePoint.Y());
|
||||
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());
|
||||
}
|
||||
lineLength = (worldPos - startPos).length();
|
||||
}
|
||||
|
||||
QVector3D lineVec = worldPos - startPos;
|
||||
if (lineVec.length() > 1e-6) {
|
||||
double refAngle, lineAngle;
|
||||
QVector3D refDir;
|
||||
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, 0, -1);
|
||||
}
|
||||
|
||||
QVector3D currentMouseWorldPosForText = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
QVector3D mouseVecForText = currentMouseWorldPosForText - startPos;
|
||||
|
||||
if (angleFromInput) {
|
||||
if (mouseVecForText.length() > 1e-6) {
|
||||
double mouseAngle;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.z(), mouseVecForText.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.y(), mouseVecForText.x()));
|
||||
else mouseAngle = qRadiansToDegrees(atan2(mouseVecForText.z(), mouseVecForText.y()));
|
||||
|
||||
double refAngleForQuadrant;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.x()));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.y(), refDir.x()));
|
||||
else refAngleForQuadrant = qRadiansToDegrees(atan2(refDir.z(), refDir.y()));
|
||||
|
||||
double relativeMouseAngle = mouseAngle - refAngleForQuadrant;
|
||||
while (relativeMouseAngle <= -180.0) relativeMouseAngle += 360.0;
|
||||
while (relativeMouseAngle > 180.0) relativeMouseAngle -= 360.0;
|
||||
|
||||
if (relativeMouseAngle >= 90 || relativeMouseAngle < -90) {
|
||||
refDir = -refDir;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_viewport->property("isChainedLine").toBool()) {
|
||||
refDir = -refDir;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
refAngle = atan2(refDir.z(), refDir.x());
|
||||
lineAngle = atan2(lineVec.z(), lineVec.x());
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
refAngle = atan2(refDir.y(), refDir.x());
|
||||
lineAngle = atan2(lineVec.y(), lineVec.x());
|
||||
} else { // YZ
|
||||
refAngle = atan2(refDir.z(), refDir.y());
|
||||
lineAngle = atan2(lineVec.z(), lineVec.y());
|
||||
}
|
||||
|
||||
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.z(), 0, lineVec.x()).normalized();
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
perpVec = QVector3D(-lineVec.y(), lineVec.x(), 0).normalized();
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::YZ) {
|
||||
perpVec = QVector3D(0, -lineVec.z(), lineVec.y()).normalized();
|
||||
}
|
||||
|
||||
if (angleDiff < 0) {
|
||||
perpVec = -perpVec;
|
||||
}
|
||||
|
||||
float offset = 0.05f * -m_viewport->camera()->zoom();
|
||||
QVector3D dimStart = startPos + offset * perpVec;
|
||||
QVector3D dimEnd = worldPos + offset * perpVec;
|
||||
QVector3D textPos3D = (dimStart + dimEnd) / 2.0f + 0.015f * -m_viewport->camera()->zoom() * perpVec;
|
||||
|
||||
QVector3D screenPos = m_viewport->project(textPos3D, modelView, projection, m_viewport->rect());
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
QFontMetrics fm(painter.font());
|
||||
|
||||
if (screenPos.z() < 1.0f) {
|
||||
dimText = lengthFromInput ? dimInput : QString::number(lineLength, 'f', 2);
|
||||
|
||||
QRect textRect = fm.boundingRect(dimText + "_");
|
||||
textRect.moveCenter(screenPos.toPoint());
|
||||
|
||||
if (m_viewport->property("dimensionEditMode").toString() == "length") {
|
||||
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255));
|
||||
} else {
|
||||
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50));
|
||||
}
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(textRect, Qt::AlignCenter, dimText);
|
||||
}
|
||||
|
||||
// Angle dimension text
|
||||
double angleDiffDegrees = qRadiansToDegrees(angleDiff);
|
||||
while (angleDiffDegrees <= -180.0) angleDiffDegrees += 360.0;
|
||||
while (angleDiffDegrees > 180.0) angleDiffDegrees -= 360.0;
|
||||
|
||||
angleText = angleFromInput ? angleInput : QString::number(qAbs(angleDiffDegrees), 'f', 1) + QChar(0x00B0);
|
||||
|
||||
const float radius = 0.1f * -m_viewport->camera()->zoom();
|
||||
double midAngle = refAngle + (lineAngle - refAngle) / 2.0;
|
||||
QVector3D textPos3DAngle;
|
||||
float textOffset = 0.035f * -m_viewport->camera()->zoom();
|
||||
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) textPos3DAngle = startPos + (radius + textOffset) * QVector3D(cos(midAngle), 0, sin(midAngle));
|
||||
else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) textPos3DAngle = startPos + (radius + textOffset) * QVector3D(cos(midAngle), sin(midAngle), 0);
|
||||
else textPos3DAngle = startPos + (radius + textOffset) * QVector3D(0, cos(midAngle), sin(midAngle));
|
||||
|
||||
QVector3D screenPosAngle = m_viewport->project(textPos3DAngle, modelView, projection, m_viewport->rect());
|
||||
if (screenPosAngle.z() < 1.0f) {
|
||||
QRect angleTextRect = fm.boundingRect(angleText + "_");
|
||||
angleTextRect.moveCenter(screenPosAngle.toPoint());
|
||||
if (m_viewport->property("dimensionEditMode").toString() == "angle") {
|
||||
painter.fillRect(angleTextRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255));
|
||||
} else {
|
||||
painter.fillRect(angleTextRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50));
|
||||
}
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(angleTextRect, Qt::AlignCenter, angleText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/LineTool.h
Normal file
28
src/LineTool.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef LINETOOL_H
|
||||
#define LINETOOL_H
|
||||
|
||||
#include "SketchTool.h"
|
||||
#include <gp_Pnt.hxx>
|
||||
|
||||
class LineTool : public SketchTool
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LineTool(ViewportWidget* viewport);
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
void paintGL() override;
|
||||
void paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection) override;
|
||||
|
||||
void activate() override;
|
||||
|
||||
protected:
|
||||
void finalizeCreation() override;
|
||||
|
||||
private:
|
||||
gp_Pnt m_firstLinePoint;
|
||||
};
|
||||
|
||||
#endif // LINETOOL_H
|
||||
@@ -17,11 +17,14 @@
|
||||
#include <QIcon>
|
||||
#include <QInputDialog>
|
||||
#include <QStringList>
|
||||
#include <QKeyEvent>
|
||||
#include <QApplication>
|
||||
|
||||
MainWindow::MainWindow(ApplicationController* appController, QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
, m_appController(appController)
|
||||
{
|
||||
qApp->installEventFilter(this);
|
||||
setWindowTitle("OpenCAD");
|
||||
resize(1920, 1080);
|
||||
|
||||
@@ -143,6 +146,8 @@ MainWindow::MainWindow(ApplicationController* appController, QWidget *parent)
|
||||
connect(m_appController, &ApplicationController::planeSelectionModeStarted, m_viewport, &ViewportWidget::onPlaneSelectionModeStarted);
|
||||
|
||||
connect(m_viewport, &ViewportWidget::lineAdded, m_appController, &ApplicationController::addLine);
|
||||
connect(m_viewport, &ViewportWidget::rectangleAdded, m_appController, &ApplicationController::addRectangle);
|
||||
connect(m_viewport, &ViewportWidget::circleAdded, m_appController, &ApplicationController::addCircle);
|
||||
connect(m_viewport, &ViewportWidget::planeSelected, m_appController, &ApplicationController::onPlaneSelected);
|
||||
connect(m_viewport, &ViewportWidget::toolDeactivated, m_appController, [this]() { m_appController->setActiveTool(ApplicationController::ToolType::None); });
|
||||
|
||||
@@ -204,3 +209,19 @@ void MainWindow::updateWindowTitle(const QString& filePath)
|
||||
shownName = "Untitled";
|
||||
setWindowTitle(tr("%1[*] - %2").arg(QFileInfo(shownName).fileName(), tr("OpenCAD")));
|
||||
}
|
||||
|
||||
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (m_appController->activeTool() != ApplicationController::ToolType::None && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
if (keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab) {
|
||||
if (watched == m_viewport) {
|
||||
return false; // Let the viewport handle its own event
|
||||
}
|
||||
// Forward event to viewport and consume it
|
||||
QApplication::sendEvent(m_viewport, keyEvent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QMainWindow::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
class QEvent;
|
||||
class ViewportWidget;
|
||||
class Document;
|
||||
class Feature;
|
||||
@@ -29,6 +30,9 @@ private slots:
|
||||
void exitSketchMode();
|
||||
void updateWindowTitle(const QString& filePath);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
private:
|
||||
ApplicationController* m_appController;
|
||||
ViewportWidget *m_viewport;
|
||||
|
||||
392
src/RectangleTool.cpp
Normal file
392
src/RectangleTool.cpp
Normal file
@@ -0,0 +1,392 @@
|
||||
#include "RectangleTool.h"
|
||||
#include "ViewportWidget.h"
|
||||
#include "Camera.h"
|
||||
#include <QMouseEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
#include <QVector>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <cmath>
|
||||
#include <QtMath>
|
||||
|
||||
RectangleTool::RectangleTool(ViewportWidget* viewport)
|
||||
: SketchTool(viewport)
|
||||
{
|
||||
}
|
||||
|
||||
void RectangleTool::activate()
|
||||
{
|
||||
SketchTool::activate();
|
||||
m_dimensionModes << "height" << "width";
|
||||
m_dimensionPropertyNames["height"] = "heightInput";
|
||||
m_dimensionPropertyNames["width"] = "widthInput";
|
||||
|
||||
m_viewport->setProperty("widthInput", "");
|
||||
m_viewport->setProperty("heightInput", "");
|
||||
m_viewport->setProperty("dimensionEditMode", "height");
|
||||
}
|
||||
|
||||
void RectangleTool::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
gp_Pnt p;
|
||||
if (!m_isDefining) {
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
p.SetCoord(0, 0, 0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
p = m_viewport->snapVertex();
|
||||
} else {
|
||||
QVector3D worldPos = m_viewport->unproject(event->pos(), m_viewport->currentPlane());
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
}
|
||||
m_firstRectanglePoint = p;
|
||||
m_isDefining = true;
|
||||
m_viewport->setProperty("widthInput", "");
|
||||
m_viewport->setProperty("heightInput", "");
|
||||
m_viewport->setProperty("dimensionEditMode", "height");
|
||||
} else {
|
||||
QVector3D worldPos;
|
||||
QVector3D startPos(m_firstRectanglePoint.X(), m_firstRectanglePoint.Y(), m_firstRectanglePoint.Z());
|
||||
|
||||
QString widthInput = m_viewport->property("widthInput").toString();
|
||||
QString heightInput = m_viewport->property("heightInput").toString();
|
||||
bool widthFromInput = false, heightFromInput = false;
|
||||
double inputWidth = 0, inputHeight = 0;
|
||||
|
||||
if (!widthInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputWidth = widthInput.toDouble(&ok);
|
||||
if (ok) widthFromInput = true;
|
||||
}
|
||||
if (!heightInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputHeight = heightInput.toDouble(&ok);
|
||||
if (ok) heightFromInput = true;
|
||||
}
|
||||
|
||||
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.z());
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
current_w = qAbs(mouseDir.x());
|
||||
current_h = qAbs(mouseDir.y());
|
||||
} else { // YZ
|
||||
current_w = qAbs(mouseDir.y());
|
||||
current_h = qAbs(mouseDir.z());
|
||||
}
|
||||
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.setZ(startPos.z() + signZ * rect_h);
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
worldPos.setX(startPos.x() + signX * rect_w);
|
||||
worldPos.setY(startPos.y() + signY * rect_h);
|
||||
} else { // YZ
|
||||
worldPos.setY(startPos.y() + signY * rect_w);
|
||||
worldPos.setZ(startPos.z() + signZ * rect_h);
|
||||
}
|
||||
} else {
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
emit m_viewport->rectangleAdded(m_firstRectanglePoint, p);
|
||||
deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void RectangleTool::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
// To be implemented
|
||||
}
|
||||
|
||||
void RectangleTool::finalizeCreation()
|
||||
{
|
||||
QVector3D worldPos;
|
||||
QVector3D startPos(m_firstRectanglePoint.X(), m_firstRectanglePoint.Y(), m_firstRectanglePoint.Z());
|
||||
|
||||
QString widthInput = m_viewport->property("widthInput").toString();
|
||||
QString heightInput = m_viewport->property("heightInput").toString();
|
||||
bool widthFromInput = false, heightFromInput = false;
|
||||
double inputWidth = 0, inputHeight = 0;
|
||||
|
||||
if (!widthInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputWidth = widthInput.toDouble(&ok);
|
||||
if (ok) widthFromInput = true;
|
||||
}
|
||||
if (!heightInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputHeight = heightInput.toDouble(&ok);
|
||||
if (ok) heightFromInput = true;
|
||||
}
|
||||
|
||||
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.z());
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
current_w = qAbs(mouseDir.x());
|
||||
current_h = qAbs(mouseDir.y());
|
||||
} else { // YZ
|
||||
current_w = qAbs(mouseDir.y());
|
||||
current_h = qAbs(mouseDir.z());
|
||||
}
|
||||
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.setZ(startPos.z() + signZ * rect_h);
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
worldPos.setX(startPos.x() + signX * rect_w);
|
||||
worldPos.setY(startPos.y() + signY * rect_h);
|
||||
} else { // YZ
|
||||
worldPos.setY(startPos.y() + signY * rect_w);
|
||||
worldPos.setZ(startPos.z() + signZ * rect_h);
|
||||
}
|
||||
} else {
|
||||
worldPos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
}
|
||||
|
||||
gp_Pnt p;
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
|
||||
emit m_viewport->rectangleAdded(m_firstRectanglePoint, p);
|
||||
deactivate();
|
||||
}
|
||||
|
||||
void RectangleTool::paintGL()
|
||||
{
|
||||
if (m_isDefining) {
|
||||
QVector<GLfloat> vertices;
|
||||
QVector3D worldPos;
|
||||
QVector3D startPos(m_firstRectanglePoint.X(), m_firstRectanglePoint.Y(), m_firstRectanglePoint.Z());
|
||||
|
||||
QString widthInput = m_viewport->property("widthInput").toString();
|
||||
QString heightInput = m_viewport->property("heightInput").toString();
|
||||
bool widthFromInput = false;
|
||||
bool heightFromInput = false;
|
||||
double inputWidth = 0, inputHeight = 0;
|
||||
|
||||
if (!widthInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputWidth = widthInput.toDouble(&ok);
|
||||
if (ok) widthFromInput = true;
|
||||
}
|
||||
if (!heightInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputHeight = heightInput.toDouble(&ok);
|
||||
if (ok) heightFromInput = true;
|
||||
}
|
||||
|
||||
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
|
||||
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.z());
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
current_w = qAbs(mouseDir.x());
|
||||
current_h = qAbs(mouseDir.y());
|
||||
} else { // YZ
|
||||
current_w = qAbs(mouseDir.y());
|
||||
current_h = qAbs(mouseDir.z());
|
||||
}
|
||||
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.setZ(startPos.z() + signZ * rect_h);
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
worldPos.setX(startPos.x() + signX * rect_w);
|
||||
worldPos.setY(startPos.y() + signY * rect_h);
|
||||
} else { // YZ
|
||||
worldPos.setY(startPos.y() + signY * rect_w);
|
||||
worldPos.setZ(startPos.z() + signZ * rect_h);
|
||||
}
|
||||
} else {
|
||||
worldPos = mousePos;
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
worldPos.setX(m_viewport->snapVertex().X()); worldPos.setY(m_viewport->snapVertex().Y()); worldPos.setZ(m_viewport->snapVertex().Z());
|
||||
}
|
||||
}
|
||||
|
||||
QVector3D p1 = startPos;
|
||||
QVector3D p2, p3, p4;
|
||||
p3 = worldPos;
|
||||
|
||||
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(p1.y()); p4.setZ(p3.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(p3.y()); p4.setZ(p1.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());
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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());
|
||||
|
||||
QString widthInput = m_viewport->property("widthInput").toString();
|
||||
QString heightInput = m_viewport->property("heightInput").toString();
|
||||
bool widthFromInput = false;
|
||||
bool heightFromInput = false;
|
||||
double inputWidth = 0, inputHeight = 0;
|
||||
|
||||
if (!widthInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputWidth = widthInput.toDouble(&ok);
|
||||
if (ok) widthFromInput = true;
|
||||
}
|
||||
if (!heightInput.isEmpty()) {
|
||||
bool ok;
|
||||
inputHeight = heightInput.toDouble(&ok);
|
||||
if (ok) heightFromInput = true;
|
||||
}
|
||||
|
||||
QVector3D mousePos = m_viewport->unproject(m_viewport->currentMousePos(), m_viewport->currentPlane());
|
||||
|
||||
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.z());
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
current_w = qAbs(mouseDir.x());
|
||||
current_h = qAbs(mouseDir.y());
|
||||
} else { // YZ
|
||||
current_w = qAbs(mouseDir.y());
|
||||
current_h = qAbs(mouseDir.z());
|
||||
}
|
||||
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.setZ(p1_3d.z() + signZ * rect_h);
|
||||
} else if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XZ) {
|
||||
worldPos.setX(p1_3d.x() + signX * rect_w);
|
||||
worldPos.setY(p1_3d.y() + signY * rect_h);
|
||||
} else { // YZ
|
||||
worldPos.setY(p1_3d.y() + signY * rect_w);
|
||||
worldPos.setZ(p1_3d.z() + signZ * rect_h);
|
||||
}
|
||||
} else {
|
||||
worldPos = mousePos;
|
||||
if (m_viewport->isSnappingOrigin()) {
|
||||
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||
} else if (m_viewport->isSnappingVertex()) {
|
||||
worldPos.setX(m_viewport->snapVertex().X()); worldPos.setY(m_viewport->snapVertex().Y()); worldPos.setZ(m_viewport->snapVertex().Z());
|
||||
}
|
||||
}
|
||||
QVector3D p3_3d = worldPos;
|
||||
QVector3D p2_3d, p4_3d;
|
||||
|
||||
double w, h;
|
||||
|
||||
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(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 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(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 { // 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());
|
||||
}
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
QFontMetrics fm(painter.font());
|
||||
|
||||
// Width dimension
|
||||
QVector3D widthTextPos3D = (p1_3d + p2_3d) / 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));
|
||||
if (m_viewport->property("dimensionEditMode").toString() == "width") {
|
||||
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255));
|
||||
} else {
|
||||
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50));
|
||||
}
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(textRect, Qt::AlignCenter, widthText);
|
||||
}
|
||||
|
||||
// Height dimension
|
||||
QVector3D heightTextPos3D = (p2_3d + p3_3d) / 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));
|
||||
if (m_viewport->property("dimensionEditMode").toString() == "height") {
|
||||
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(64, 128, 255));
|
||||
} else {
|
||||
painter.fillRect(textRect.adjusted(-4, -2, 4, 2), QColor(50, 50, 50));
|
||||
}
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(textRect, Qt::AlignCenter, heightText);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/RectangleTool.h
Normal file
28
src/RectangleTool.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef RECTANGLETOOL_H
|
||||
#define RECTANGLETOOL_H
|
||||
|
||||
#include "SketchTool.h"
|
||||
#include <gp_Pnt.hxx>
|
||||
|
||||
class RectangleTool : public SketchTool
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RectangleTool(ViewportWidget* viewport);
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
void paintGL() override;
|
||||
void paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection) override;
|
||||
|
||||
void activate() override;
|
||||
|
||||
protected:
|
||||
void finalizeCreation() override;
|
||||
|
||||
private:
|
||||
gp_Pnt m_firstRectanglePoint;
|
||||
};
|
||||
|
||||
#endif // RECTANGLETOOL_H
|
||||
56
src/SketchCircle.cpp
Normal file
56
src/SketchCircle.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "SketchCircle.h"
|
||||
#include <QJsonArray>
|
||||
|
||||
namespace
|
||||
{
|
||||
void pointToJson(const gp_Pnt& p, QJsonArray& arr)
|
||||
{
|
||||
arr.append(p.X());
|
||||
arr.append(p.Y());
|
||||
arr.append(p.Z());
|
||||
}
|
||||
|
||||
gp_Pnt jsonToPoint(const QJsonArray& arr)
|
||||
{
|
||||
return gp_Pnt(arr[0].toDouble(), arr[1].toDouble(), arr[2].toDouble());
|
||||
}
|
||||
}
|
||||
|
||||
SketchCircle::SketchCircle() : m_radius(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
SketchCircle::SketchCircle(const gp_Pnt& center, double radius)
|
||||
: m_center(center), m_radius(radius)
|
||||
{
|
||||
}
|
||||
|
||||
SketchObject::ObjectType SketchCircle::type() const
|
||||
{
|
||||
return ObjectType::Circle;
|
||||
}
|
||||
|
||||
void SketchCircle::read(const QJsonObject& json)
|
||||
{
|
||||
m_center = jsonToPoint(json["center"].toArray());
|
||||
m_radius = json["radius"].toDouble();
|
||||
}
|
||||
|
||||
void SketchCircle::write(QJsonObject& json) const
|
||||
{
|
||||
QJsonArray centerArr;
|
||||
pointToJson(m_center, centerArr);
|
||||
json["center"] = centerArr;
|
||||
json["radius"] = m_radius;
|
||||
json["type"] = "Circle";
|
||||
}
|
||||
|
||||
const gp_Pnt& SketchCircle::center() const
|
||||
{
|
||||
return m_center;
|
||||
}
|
||||
|
||||
double SketchCircle::radius() const
|
||||
{
|
||||
return m_radius;
|
||||
}
|
||||
26
src/SketchCircle.h
Normal file
26
src/SketchCircle.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef SKETCHCIRCLE_H
|
||||
#define SKETCHCIRCLE_H
|
||||
|
||||
#include "SketchObject.h"
|
||||
#include <gp_Pnt.hxx>
|
||||
|
||||
class SketchCircle : public SketchObject
|
||||
{
|
||||
public:
|
||||
SketchCircle();
|
||||
SketchCircle(const gp_Pnt& center, double radius);
|
||||
|
||||
ObjectType type() const override;
|
||||
|
||||
void read(const QJsonObject& json) override;
|
||||
void write(QJsonObject& json) const override;
|
||||
|
||||
const gp_Pnt& center() const;
|
||||
double radius() const;
|
||||
|
||||
private:
|
||||
gp_Pnt m_center;
|
||||
double m_radius;
|
||||
};
|
||||
|
||||
#endif // SKETCHCIRCLE_H
|
||||
@@ -1,8 +1,25 @@
|
||||
#include "SketchFeature.h"
|
||||
#include "SketchObject.h"
|
||||
#include "SketchLine.h"
|
||||
#include "SketchRectangle.h"
|
||||
#include "SketchCircle.h"
|
||||
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <TopoDS_Compound.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <gp_Circ.hxx>
|
||||
#include <gp_Ax2.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <ShapeAnalysis_FreeBounds.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
#include <BRepOffsetAPI_MakeFilling.hxx>
|
||||
#include <GeomAbs_Shape.hxx>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QDebug>
|
||||
|
||||
SketchFeature::SketchFeature(const QString& name)
|
||||
: Feature(name)
|
||||
@@ -34,6 +51,119 @@ const TopoDS_Shape& SketchFeature::shape() const
|
||||
return m_shape;
|
||||
}
|
||||
|
||||
void SketchFeature::buildShape()
|
||||
{
|
||||
m_shape.Nullify();
|
||||
QList<TopoDS_Edge> lineEdges;
|
||||
QList<TopoDS_Face> faces;
|
||||
|
||||
gp_Pln sketchPlane;
|
||||
switch (m_plane) {
|
||||
case SketchPlane::XY: sketchPlane = gp_Pln(gp::Origin(), gp::DY()); break;
|
||||
case SketchPlane::XZ: sketchPlane = gp_Pln(gp::Origin(), gp::DZ()); break;
|
||||
case SketchPlane::YZ: sketchPlane = gp_Pln(gp::Origin(), gp::DX()); break;
|
||||
}
|
||||
|
||||
for (SketchObject* obj : m_objects) {
|
||||
if (auto line = dynamic_cast<SketchLine*>(obj)) {
|
||||
BRepBuilderAPI_MakeEdge makeEdge(line->startPoint(), line->endPoint());
|
||||
if (makeEdge.IsDone()) {
|
||||
lineEdges.append(makeEdge.Edge());
|
||||
}
|
||||
} else if (auto rect = dynamic_cast<SketchRectangle*>(obj)) {
|
||||
const gp_Pnt& c1 = rect->corner1();
|
||||
const gp_Pnt& c2 = rect->corner2();
|
||||
gp_Pnt other_corner1, other_corner2;
|
||||
|
||||
if (m_plane == SketchPlane::XY) {
|
||||
other_corner1.SetCoord(c2.X(), c1.Y(), c1.Z());
|
||||
other_corner2.SetCoord(c1.X(), c1.Y(), c2.Z());
|
||||
} else if (m_plane == SketchPlane::XZ) {
|
||||
other_corner1.SetCoord(c2.X(), c1.Y(), c1.Z());
|
||||
other_corner2.SetCoord(c1.X(), c2.Y(), c1.Z());
|
||||
} else { // YZ
|
||||
other_corner1.SetCoord(c1.X(), c2.Y(), c1.Z());
|
||||
other_corner2.SetCoord(c1.X(), c1.Y(), c2.Z());
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeEdge me1(c1, other_corner1);
|
||||
BRepBuilderAPI_MakeEdge me2(other_corner1, c2);
|
||||
BRepBuilderAPI_MakeEdge me3(c2, other_corner2);
|
||||
BRepBuilderAPI_MakeEdge me4(other_corner2, c1);
|
||||
if (me1.IsDone() && me2.IsDone() && me3.IsDone() && me4.IsDone()) {
|
||||
BRepBuilderAPI_MakeWire wireBuilder(me1.Edge(), me2.Edge(), me3.Edge(), me4.Edge());
|
||||
if (wireBuilder.IsDone()) {
|
||||
BRepBuilderAPI_MakeFace faceBuilder(sketchPlane, wireBuilder.Wire());
|
||||
if (faceBuilder.IsDone()) {
|
||||
faces.append(faceBuilder.Face());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (auto circle = dynamic_cast<SketchCircle*>(obj)) {
|
||||
const gp_Pnt& center = circle->center();
|
||||
double radius = circle->radius();
|
||||
gp_Dir normal;
|
||||
switch (m_plane) {
|
||||
case SketchPlane::XY: normal = gp::DY(); break;
|
||||
case SketchPlane::XZ: normal = gp::DZ(); break;
|
||||
case SketchPlane::YZ: normal = gp::DX(); break;
|
||||
}
|
||||
gp_Ax2 axis(center, normal);
|
||||
gp_Circ circ(axis, radius);
|
||||
BRepBuilderAPI_MakeEdge makeEdge(circ);
|
||||
if (makeEdge.IsDone()) {
|
||||
TopoDS_Edge edge = makeEdge.Edge();
|
||||
BRepBuilderAPI_MakeWire wireBuilder(edge);
|
||||
if(wireBuilder.IsDone()) {
|
||||
BRepBuilderAPI_MakeFace faceBuilder(sketchPlane, wireBuilder.Wire());
|
||||
if (faceBuilder.IsDone()) {
|
||||
faces.append(faceBuilder.Face());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!lineEdges.isEmpty()) {
|
||||
qDebug() << "buildShape: processing" << lineEdges.size() << "line edges";
|
||||
|
||||
BRepOffsetAPI_MakeFilling faceMaker;
|
||||
for (const TopoDS_Edge& edge : lineEdges) {
|
||||
faceMaker.Add(edge, GeomAbs_C0);
|
||||
}
|
||||
faceMaker.Build();
|
||||
|
||||
if (faceMaker.IsDone()) {
|
||||
TopExp_Explorer explorer(faceMaker.Shape(), TopAbs_FACE);
|
||||
int facesAdded = 0;
|
||||
for (; explorer.More(); explorer.Next()) {
|
||||
faces.append(TopoDS::Face(explorer.Current()));
|
||||
facesAdded++;
|
||||
}
|
||||
qDebug() << "buildShape: added" << facesAdded << "face(s) using MakeFilling";
|
||||
} else {
|
||||
qDebug() << "buildShape: MakeFilling failed";
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "buildShape: total faces created:" << faces.size();
|
||||
if (faces.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (faces.size() == 1) {
|
||||
m_shape = faces.first();
|
||||
} else {
|
||||
TopoDS_Compound compound;
|
||||
BRep_Builder builder;
|
||||
builder.MakeCompound(compound);
|
||||
for (const auto& face : faces) {
|
||||
builder.Add(compound, face);
|
||||
}
|
||||
m_shape = compound;
|
||||
}
|
||||
}
|
||||
|
||||
void SketchFeature::addObject(SketchObject* object)
|
||||
{
|
||||
m_objects.append(object);
|
||||
@@ -66,6 +196,10 @@ void SketchFeature::read(const QJsonObject& json)
|
||||
auto line = new SketchLine();
|
||||
line->read(objectJson);
|
||||
m_objects.append(line);
|
||||
} else if (type == "Rectangle") {
|
||||
auto rect = new SketchRectangle();
|
||||
rect->read(objectJson);
|
||||
m_objects.append(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ public:
|
||||
|
||||
const TopoDS_Shape& shape() const;
|
||||
|
||||
void buildShape();
|
||||
|
||||
void addObject(SketchObject* object);
|
||||
const QList<SketchObject*>& objects() const;
|
||||
|
||||
|
||||
@@ -1,10 +1,36 @@
|
||||
#include "SketchGrid.h"
|
||||
#include "ViewportWidget.h"
|
||||
#include "Camera.h"
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QPainter>
|
||||
#include <QVector>
|
||||
|
||||
SketchGrid::SketchGrid()
|
||||
namespace {
|
||||
struct GridParams {
|
||||
float minorIncrement;
|
||||
float majorIncrement;
|
||||
int gridSize;
|
||||
};
|
||||
|
||||
GridParams getGridParams(float distance)
|
||||
{
|
||||
if (distance > 500.0f) {
|
||||
return { 20.0f, 100.0f, 1000 };
|
||||
} else if (distance > 250.0f) {
|
||||
return { 10.0f, 50.0f, 1000 };
|
||||
} else if (distance > 50.0f) {
|
||||
return { 5.0f, 25.0f, 1000 };
|
||||
} else if (distance > 10.0f) {
|
||||
return { 1.0f, 5.0f, 1000 };
|
||||
} else { // zoomed in
|
||||
return { 0.2f, 1.0f, 1000 };
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SketchGrid::SketchGrid(ViewportWidget* viewport) : m_viewport(viewport)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -46,45 +72,51 @@ void SketchGrid::paintGL(SketchPlane plane, QOpenGLShaderProgram* shaderProgram,
|
||||
|
||||
void SketchGrid::drawGridLines(SketchPlane plane, QOpenGLShaderProgram* shaderProgram, int colorLoc)
|
||||
{
|
||||
const int gridSize = 50;
|
||||
const float darkFactor = 0.8f;
|
||||
QVector<GLfloat> lightLines;
|
||||
QVector<GLfloat> darkLines;
|
||||
auto params = getGridParams(-m_viewport->camera()->uiCameraDistance());
|
||||
const float minorIncrement = params.minorIncrement;
|
||||
const int gridSize = params.gridSize;
|
||||
|
||||
for (int i = -gridSize; i <= gridSize; ++i)
|
||||
{
|
||||
if (i == 0) continue;
|
||||
QVector<GLfloat>& current_vector = (i % 5 == 0) ? darkLines : lightLines;
|
||||
QVector<GLfloat> minorLines;
|
||||
QVector<GLfloat> majorLines;
|
||||
|
||||
int numLines = gridSize / minorIncrement;
|
||||
for (int i = -numLines; i <= numLines; ++i) {
|
||||
if (i == 0)
|
||||
continue;
|
||||
|
||||
float pos = i * minorIncrement;
|
||||
QVector<GLfloat>& current_vector = (i % 5 == 0) ? majorLines : minorLines;
|
||||
if (plane == XY) {
|
||||
current_vector << i << 0 << -gridSize << i << 0 << gridSize;
|
||||
current_vector << -gridSize << 0 << i << gridSize << 0 << i;
|
||||
current_vector << pos << 0 << -gridSize << pos << 0 << gridSize;
|
||||
current_vector << -gridSize << 0 << pos << gridSize << 0 << pos;
|
||||
} else if (plane == XZ) {
|
||||
current_vector << i << -gridSize << 0 << i << gridSize << 0;
|
||||
current_vector << -gridSize << i << 0 << gridSize << i << 0;
|
||||
current_vector << pos << -gridSize << 0 << pos << gridSize << 0;
|
||||
current_vector << -gridSize << pos << 0 << gridSize << pos << 0;
|
||||
} else { // YZ
|
||||
current_vector << 0 << i << -gridSize << 0 << i << gridSize;
|
||||
current_vector << 0 << -gridSize << i << 0 << gridSize << i;
|
||||
current_vector << 0 << pos << -gridSize << 0 << pos << gridSize;
|
||||
current_vector << 0 << -gridSize << pos << 0 << gridSize << pos;
|
||||
}
|
||||
}
|
||||
|
||||
m_vbo.bind();
|
||||
|
||||
// Draw lighter lines
|
||||
shaderProgram->setUniformValue(colorLoc, QVector4D(0.5f, 0.5f, 0.5f, 1.0f));
|
||||
// Draw minor lines
|
||||
shaderProgram->setUniformValue(colorLoc, QVector4D(0.4f, 0.4f, 0.4f, 1.0f));
|
||||
glLineWidth(1.0f);
|
||||
m_vbo.allocate(lightLines.constData(), lightLines.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINES, 0, lightLines.size() / 3);
|
||||
m_vbo.allocate(minorLines.constData(), minorLines.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINES, 0, minorLines.size() / 3);
|
||||
|
||||
// Draw darker lines
|
||||
shaderProgram->setUniformValue(colorLoc, QVector4D(0.5f * darkFactor, 0.5f * darkFactor, 0.5f * darkFactor, 1.0f));
|
||||
glLineWidth(1.5f);
|
||||
m_vbo.allocate(darkLines.constData(), darkLines.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINES, 0, darkLines.size() / 3);
|
||||
// Draw major lines
|
||||
shaderProgram->setUniformValue(colorLoc, QVector4D(0.6f, 0.6f, 0.6f, 1.0f));
|
||||
glLineWidth(1.0f);
|
||||
m_vbo.allocate(majorLines.constData(), majorLines.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINES, 0, majorLines.size() / 3);
|
||||
}
|
||||
|
||||
void SketchGrid::drawAxes(SketchPlane plane, QOpenGLShaderProgram* shaderProgram, int colorLoc)
|
||||
{
|
||||
const int axisLength = 50;
|
||||
auto params = getGridParams(-m_viewport->camera()->uiCameraDistance());
|
||||
const int axisLength = params.gridSize;
|
||||
QVector<GLfloat> vertices;
|
||||
|
||||
glLineWidth(2.0f);
|
||||
@@ -123,3 +155,41 @@ void SketchGrid::drawAxes(SketchPlane plane, QOpenGLShaderProgram* shaderProgram
|
||||
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)
|
||||
{
|
||||
painter.setPen(Qt::white);
|
||||
painter.setFont(QFont("Arial", 10));
|
||||
|
||||
auto params = getGridParams(-m_viewport->camera()->uiCameraDistance());
|
||||
const float majorIncrement = params.majorIncrement;
|
||||
const int range = params.gridSize;
|
||||
|
||||
auto drawLabelsForAxis = [&](int axis_idx) {
|
||||
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 screenPos = m_viewport->project(worldCoord, modelView, projection, m_viewport->rect());
|
||||
if (screenPos.z() < 1.0f) { // Not clipped
|
||||
painter.drawText(screenPos.toPoint(), QString::number(val));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (plane == SketchGrid::XY) {
|
||||
drawLabelsForAxis(0); // X
|
||||
drawLabelsForAxis(2); // Y
|
||||
} else if (plane == SketchGrid::XZ) {
|
||||
drawLabelsForAxis(0); // X
|
||||
drawLabelsForAxis(1); // Z
|
||||
} else if (plane == SketchGrid::YZ) {
|
||||
drawLabelsForAxis(1); // Y
|
||||
drawLabelsForAxis(2); // Z
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <QOpenGLBuffer>
|
||||
|
||||
class QOpenGLShaderProgram;
|
||||
class QPainter;
|
||||
class ViewportWidget;
|
||||
|
||||
class SketchGrid : protected QOpenGLFunctions
|
||||
{
|
||||
@@ -17,11 +19,12 @@ public:
|
||||
YZ = 3
|
||||
};
|
||||
|
||||
SketchGrid();
|
||||
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);
|
||||
|
||||
private:
|
||||
void drawGridLines(SketchPlane plane, QOpenGLShaderProgram* shaderProgram, int colorLoc);
|
||||
@@ -29,6 +32,7 @@ private:
|
||||
|
||||
QOpenGLVertexArrayObject m_vao;
|
||||
QOpenGLBuffer m_vbo;
|
||||
ViewportWidget* m_viewport = nullptr;
|
||||
};
|
||||
|
||||
#endif // SKETCHGRID_H
|
||||
|
||||
@@ -7,7 +7,9 @@ class SketchObject
|
||||
{
|
||||
public:
|
||||
enum class ObjectType {
|
||||
Line
|
||||
Line,
|
||||
Rectangle,
|
||||
Circle
|
||||
};
|
||||
|
||||
SketchObject() = default;
|
||||
|
||||
61
src/SketchRectangle.cpp
Normal file
61
src/SketchRectangle.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "SketchRectangle.h"
|
||||
#include <QJsonArray>
|
||||
|
||||
namespace
|
||||
{
|
||||
void pointToJson(const gp_Pnt& p, QJsonArray& arr)
|
||||
{
|
||||
arr.append(p.X());
|
||||
arr.append(p.Y());
|
||||
arr.append(p.Z());
|
||||
}
|
||||
|
||||
gp_Pnt jsonToPoint(const QJsonArray& arr)
|
||||
{
|
||||
return gp_Pnt(arr[0].toDouble(), arr[1].toDouble(), arr[2].toDouble());
|
||||
}
|
||||
}
|
||||
|
||||
SketchRectangle::SketchRectangle()
|
||||
{
|
||||
}
|
||||
|
||||
SketchRectangle::SketchRectangle(const gp_Pnt& corner1, const gp_Pnt& corner2)
|
||||
: m_corner1(corner1), m_corner2(corner2)
|
||||
{
|
||||
}
|
||||
|
||||
SketchObject::ObjectType SketchRectangle::type() const
|
||||
{
|
||||
return ObjectType::Rectangle;
|
||||
}
|
||||
|
||||
void SketchRectangle::read(const QJsonObject& json)
|
||||
{
|
||||
if (json.contains("corner1") && json["corner1"].isArray()) {
|
||||
m_corner1 = jsonToPoint(json["corner1"].toArray());
|
||||
}
|
||||
if (json.contains("corner2") && json["corner2"].isArray()) {
|
||||
m_corner2 = jsonToPoint(json["corner2"].toArray());
|
||||
}
|
||||
}
|
||||
|
||||
void SketchRectangle::write(QJsonObject& json) const
|
||||
{
|
||||
json["type"] = "Rectangle";
|
||||
QJsonArray c1, c2;
|
||||
pointToJson(m_corner1, c1);
|
||||
pointToJson(m_corner2, c2);
|
||||
json["corner1"] = c1;
|
||||
json["corner2"] = c2;
|
||||
}
|
||||
|
||||
const gp_Pnt& SketchRectangle::corner1() const
|
||||
{
|
||||
return m_corner1;
|
||||
}
|
||||
|
||||
const gp_Pnt& SketchRectangle::corner2() const
|
||||
{
|
||||
return m_corner2;
|
||||
}
|
||||
26
src/SketchRectangle.h
Normal file
26
src/SketchRectangle.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef SKETCHRECTANGLE_H
|
||||
#define SKETCHRECTANGLE_H
|
||||
|
||||
#include "SketchObject.h"
|
||||
#include <gp_Pnt.hxx>
|
||||
|
||||
class SketchRectangle : public SketchObject
|
||||
{
|
||||
public:
|
||||
SketchRectangle();
|
||||
SketchRectangle(const gp_Pnt& corner1, const gp_Pnt& corner2);
|
||||
|
||||
ObjectType type() const override;
|
||||
|
||||
void read(const QJsonObject& json) override;
|
||||
void write(QJsonObject& json) const override;
|
||||
|
||||
const gp_Pnt& corner1() const;
|
||||
const gp_Pnt& corner2() const;
|
||||
|
||||
private:
|
||||
gp_Pnt m_corner1;
|
||||
gp_Pnt m_corner2;
|
||||
};
|
||||
|
||||
#endif // SKETCHRECTANGLE_H
|
||||
79
src/SketchTool.cpp
Normal file
79
src/SketchTool.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "SketchTool.h"
|
||||
#include "ViewportWidget.h"
|
||||
#include <QKeyEvent>
|
||||
|
||||
SketchTool::SketchTool(ViewportWidget* viewport)
|
||||
: QObject(viewport), m_viewport(viewport)
|
||||
{
|
||||
}
|
||||
|
||||
void SketchTool::activate()
|
||||
{
|
||||
m_isDefining = false;
|
||||
}
|
||||
|
||||
void SketchTool::deactivate()
|
||||
{
|
||||
m_isDefining = false;
|
||||
for (const QString& propName : m_dimensionPropertyNames.values()) {
|
||||
m_viewport->setProperty(propName.toUtf8().constData(), "");
|
||||
}
|
||||
m_dimensionModes.clear();
|
||||
m_dimensionPropertyNames.clear();
|
||||
}
|
||||
|
||||
void SketchTool::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (m_isDefining) {
|
||||
if (event->key() == Qt::Key_Tab) {
|
||||
if (m_dimensionModes.size() > 1) {
|
||||
QString currentMode = m_viewport->property("dimensionEditMode").toString();
|
||||
int currentIndex = m_dimensionModes.indexOf(currentMode);
|
||||
if (currentIndex != -1) {
|
||||
int nextIndex = (currentIndex + 1) % m_dimensionModes.size();
|
||||
m_viewport->setProperty("dimensionEditMode", m_dimensionModes[nextIndex]);
|
||||
m_viewport->update();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QString editMode = m_viewport->property("dimensionEditMode").toString();
|
||||
if (m_dimensionPropertyNames.contains(editMode)) {
|
||||
QString propertyName = m_dimensionPropertyNames[editMode];
|
||||
QString currentInput = m_viewport->property(propertyName.toUtf8().constData()).toString();
|
||||
|
||||
if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9) {
|
||||
currentInput += event->text();
|
||||
m_viewport->setProperty(propertyName.toUtf8().constData(), currentInput);
|
||||
m_viewport->update();
|
||||
return;
|
||||
} else if (event->key() == Qt::Key_Period) {
|
||||
if (!currentInput.contains('.')) {
|
||||
currentInput += '.';
|
||||
m_viewport->setProperty(propertyName.toUtf8().constData(), currentInput);
|
||||
m_viewport->update();
|
||||
}
|
||||
return;
|
||||
} else if (event->key() == Qt::Key_Backspace) {
|
||||
if (!currentInput.isEmpty()) {
|
||||
currentInput.chop(1);
|
||||
m_viewport->setProperty(propertyName.toUtf8().constData(), currentInput);
|
||||
m_viewport->update();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
|
||||
finalizeCreation();
|
||||
m_viewport->update();
|
||||
return;
|
||||
} else if (event->key() == Qt::Key_Escape) {
|
||||
deactivate();
|
||||
m_viewport->deactivateActiveTool();
|
||||
m_viewport->update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/SketchTool.h
Normal file
40
src/SketchTool.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef SKETCHTOOL_H
|
||||
#define SKETCHTOOL_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QMap>
|
||||
|
||||
class QMouseEvent;
|
||||
class QKeyEvent;
|
||||
class ViewportWidget;
|
||||
class QOpenGLShaderProgram;
|
||||
class QPainter;
|
||||
class QMatrix4x4;
|
||||
|
||||
class SketchTool : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SketchTool(ViewportWidget* viewport);
|
||||
virtual ~SketchTool() = default;
|
||||
|
||||
virtual void mousePressEvent(QMouseEvent *event) = 0;
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) = 0;
|
||||
virtual void keyPressEvent(QKeyEvent *event);
|
||||
|
||||
virtual void paintGL() = 0;
|
||||
virtual void paint2D(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection) = 0;
|
||||
|
||||
virtual void activate();
|
||||
virtual void deactivate();
|
||||
|
||||
protected:
|
||||
virtual void finalizeCreation() = 0;
|
||||
ViewportWidget* m_viewport;
|
||||
bool m_isDefining = false;
|
||||
QStringList m_dimensionModes;
|
||||
QMap<QString, QString> m_dimensionPropertyNames;
|
||||
};
|
||||
|
||||
#endif // SKETCHTOOL_H
|
||||
185
src/Snapping.cpp
Normal file
185
src/Snapping.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
#include "Snapping.h"
|
||||
|
||||
#include "ViewportWidget.h"
|
||||
#include "Camera.h"
|
||||
#include "Document.h"
|
||||
#include "SketchFeature.h"
|
||||
#include "SketchLine.h"
|
||||
#include "SketchRectangle.h"
|
||||
#include "SketchObject.h"
|
||||
#include "ApplicationController.h"
|
||||
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QVector>
|
||||
#include <QtMath>
|
||||
|
||||
Snapping::Snapping(ViewportWidget* viewport) : m_viewport(viewport)
|
||||
{
|
||||
}
|
||||
|
||||
bool Snapping::update(const QPoint& mousePos)
|
||||
{
|
||||
bool oldIsSnappingOrigin = m_isSnappingOrigin;
|
||||
bool oldIsSnappingVertex = m_isSnappingVertex;
|
||||
|
||||
bool shouldSnap = false;
|
||||
if (m_viewport->currentPlane() != ViewportWidget::SketchPlane::NONE && m_viewport->activeTool() != static_cast<int>(ApplicationController::ToolType::None)) {
|
||||
QVector3D worldPos = m_viewport->unproject(mousePos, m_viewport->currentPlane());
|
||||
const float snapRectHalfSize = 0.0075f * -m_viewport->camera()->zoom();
|
||||
|
||||
switch (m_viewport->currentPlane()) {
|
||||
case ViewportWidget::SketchPlane::XY:
|
||||
shouldSnap = qAbs(worldPos.x()) < snapRectHalfSize && qAbs(worldPos.z()) < snapRectHalfSize;
|
||||
break;
|
||||
case ViewportWidget::SketchPlane::XZ:
|
||||
shouldSnap = qAbs(worldPos.x()) < snapRectHalfSize && qAbs(worldPos.y()) < snapRectHalfSize;
|
||||
break;
|
||||
case ViewportWidget::SketchPlane::YZ:
|
||||
shouldSnap = qAbs(worldPos.y()) < snapRectHalfSize && qAbs(worldPos.z()) < snapRectHalfSize;
|
||||
break;
|
||||
case ViewportWidget::SketchPlane::NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_isSnappingOrigin = shouldSnap;
|
||||
if (m_isSnappingOrigin) {
|
||||
m_isSnappingVertex = false;
|
||||
}
|
||||
|
||||
m_isSnappingVertex = false;
|
||||
if (!m_isSnappingOrigin && m_viewport->document() && m_viewport->currentPlane() != ViewportWidget::SketchPlane::NONE && m_viewport->activeTool() != static_cast<int>(ApplicationController::ToolType::None)) {
|
||||
QVector3D worldPos = m_viewport->unproject(mousePos, m_viewport->currentPlane());
|
||||
const float snapRectHalfSize = 0.0075f * -m_viewport->camera()->zoom();
|
||||
|
||||
for (Feature* feature : m_viewport->document()->features()) {
|
||||
if (auto sketch = dynamic_cast<SketchFeature*>(feature)) {
|
||||
for (const auto& obj : sketch->objects()) {
|
||||
if (obj->type() == SketchObject::ObjectType::Line) {
|
||||
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.z() - vertex.Z()) < snapRectHalfSize;
|
||||
break;
|
||||
case ViewportWidget::SketchPlane::XZ:
|
||||
isClose = qAbs(worldPos.x() - vertex.X()) < snapRectHalfSize && qAbs(worldPos.y() - vertex.Y()) < snapRectHalfSize;
|
||||
break;
|
||||
case ViewportWidget::SketchPlane::YZ:
|
||||
isClose = qAbs(worldPos.y() - vertex.Y()) < snapRectHalfSize && qAbs(worldPos.z() - vertex.Z()) < snapRectHalfSize;
|
||||
break;
|
||||
case ViewportWidget::SketchPlane::NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (isClose) {
|
||||
m_isSnappingVertex = true;
|
||||
m_snapVertex = vertex;
|
||||
goto end_snap_check;
|
||||
}
|
||||
}
|
||||
} else if (obj->type() == SketchObject::ObjectType::Rectangle) {
|
||||
auto rect = static_cast<const SketchRectangle*>(obj);
|
||||
const auto& p1 = rect->corner1();
|
||||
const auto& p3 = rect->corner2();
|
||||
gp_Pnt p2, p4;
|
||||
|
||||
if (sketch->plane() == SketchFeature::SketchPlane::XY) {
|
||||
p2.SetCoord(p3.X(), p1.Y(), p1.Z());
|
||||
p4.SetCoord(p1.X(), p1.Y(), p3.Z());
|
||||
} else if (sketch->plane() == SketchFeature::SketchPlane::XZ) {
|
||||
p2.SetCoord(p3.X(), p1.Y(), p1.Z());
|
||||
p4.SetCoord(p1.X(), p3.Y(), p1.Z());
|
||||
} else if (sketch->plane() == SketchFeature::SketchPlane::YZ) {
|
||||
p2.SetCoord(p1.X(), p3.Y(), p1.Z());
|
||||
p4.SetCoord(p1.X(), p1.Y(), p3.Z());
|
||||
}
|
||||
|
||||
const gp_Pnt vertices[] = {p1, p2, p3, p4};
|
||||
for (const auto& vertex : vertices) {
|
||||
bool isClose = false;
|
||||
switch (m_viewport->currentPlane()) {
|
||||
case ViewportWidget::SketchPlane::XY:
|
||||
isClose = qAbs(worldPos.x() - vertex.X()) < snapRectHalfSize && qAbs(worldPos.z() - vertex.Z()) < snapRectHalfSize;
|
||||
break;
|
||||
case ViewportWidget::SketchPlane::XZ:
|
||||
isClose = qAbs(worldPos.x() - vertex.X()) < snapRectHalfSize && qAbs(worldPos.y() - vertex.Y()) < snapRectHalfSize;
|
||||
break;
|
||||
case ViewportWidget::SketchPlane::YZ:
|
||||
isClose = qAbs(worldPos.y() - vertex.Y()) < snapRectHalfSize && qAbs(worldPos.z() - vertex.Z()) < snapRectHalfSize;
|
||||
break;
|
||||
case ViewportWidget::SketchPlane::NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (isClose) {
|
||||
m_isSnappingVertex = true;
|
||||
m_snapVertex = vertex;
|
||||
goto end_snap_check;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end_snap_check:;
|
||||
}
|
||||
|
||||
return (oldIsSnappingOrigin != m_isSnappingOrigin) || (oldIsSnappingVertex != m_isSnappingVertex);
|
||||
}
|
||||
|
||||
void Snapping::paintGL() const
|
||||
{
|
||||
if (!m_isSnappingOrigin && !m_isSnappingVertex) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<GLfloat> vertices;
|
||||
if (m_isSnappingOrigin) {
|
||||
const float rectSize = 0.0075f * -m_viewport->camera()->zoom();
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
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::XZ) {
|
||||
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::YZ) {
|
||||
vertices << 0 << -rectSize << -rectSize << 0 << rectSize << -rectSize;
|
||||
vertices << 0 << rectSize << -rectSize << 0 << rectSize << rectSize;
|
||||
vertices << 0 << rectSize << rectSize << 0 << -rectSize << rectSize;
|
||||
vertices << 0 << -rectSize << rectSize << 0 << -rectSize << -rectSize;
|
||||
}
|
||||
} else if (m_isSnappingVertex) {
|
||||
const float rectSize = 0.0075f * -m_viewport->camera()->zoom();
|
||||
const auto& v = m_snapVertex;
|
||||
if (m_viewport->currentPlane() == ViewportWidget::SketchPlane::XY) {
|
||||
vertices << v.X() - rectSize << v.Y() << 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::XZ) {
|
||||
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::YZ) {
|
||||
vertices << v.X() << v.Y() - rectSize << v.Z() - rectSize << v.X() << v.Y() + rectSize << v.Z() - rectSize;
|
||||
vertices << v.X() << v.Y() + rectSize << v.Z() - rectSize << v.X() << v.Y() + rectSize << v.Z() + rectSize;
|
||||
vertices << v.X() << v.Y() + rectSize << v.Z() + rectSize << v.X() << v.Y() - rectSize << v.Z() + rectSize;
|
||||
vertices << v.X() << v.Y() - rectSize << v.Z() + rectSize << v.X() << v.Y() - rectSize << v.Z() - rectSize;
|
||||
}
|
||||
}
|
||||
m_viewport->shaderProgram()->setUniformValue(m_viewport->colorLoc(), QVector4D(1.0f, 1.0f, 0.0f, 0.5f));
|
||||
m_viewport->vbo().bind();
|
||||
m_viewport->vbo().allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDrawArrays(GL_LINES, 0, vertices.size() / 3);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
28
src/Snapping.h
Normal file
28
src/Snapping.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef SNAPPING_H
|
||||
#define SNAPPING_H
|
||||
|
||||
#include <gp_Pnt.hxx>
|
||||
#include <QPoint>
|
||||
|
||||
class ViewportWidget;
|
||||
|
||||
class Snapping
|
||||
{
|
||||
public:
|
||||
explicit Snapping(ViewportWidget* viewport);
|
||||
|
||||
bool update(const QPoint& mousePos);
|
||||
void paintGL() const;
|
||||
|
||||
bool isSnappingOrigin() const { return m_isSnappingOrigin; }
|
||||
bool isSnappingVertex() const { return m_isSnappingVertex; }
|
||||
const gp_Pnt& snapVertex() const { return m_snapVertex; }
|
||||
|
||||
private:
|
||||
ViewportWidget* m_viewport = nullptr;
|
||||
bool m_isSnappingOrigin = false;
|
||||
bool m_isSnappingVertex = false;
|
||||
gp_Pnt m_snapVertex;
|
||||
};
|
||||
|
||||
#endif // SNAPPING_H
|
||||
@@ -1,12 +1,30 @@
|
||||
#include "ViewCube.h"
|
||||
#include <QGuiApplication>
|
||||
#include <QPainter>
|
||||
#include <QFont>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QScreen>
|
||||
#include <QSvgRenderer>
|
||||
#include <QVector>
|
||||
|
||||
namespace
|
||||
{
|
||||
QRect getHomeButtonRect(int widgetWidth) {
|
||||
const int viewCubeSize = 150; // logical pixels
|
||||
const int buttonSize = 24;
|
||||
const int buttonOffsetX = 16;
|
||||
const int buttonOffsetY = 16;
|
||||
|
||||
const int viewCubeX = widgetWidth - viewCubeSize;
|
||||
|
||||
return QRect(viewCubeX + buttonOffsetX, buttonOffsetY, buttonSize, buttonSize);
|
||||
}
|
||||
}
|
||||
|
||||
ViewCube::ViewCube()
|
||||
{
|
||||
m_homeButtonRenderer = new QSvgRenderer(QString(":/icons/home.svg"));
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
m_faceTextures[i] = nullptr;
|
||||
}
|
||||
@@ -18,6 +36,7 @@ ViewCube::~ViewCube()
|
||||
delete m_faceTextures[i];
|
||||
}
|
||||
delete m_textureShaderProgram;
|
||||
delete m_homeButtonRenderer;
|
||||
m_cubeVbo.destroy();
|
||||
m_cubeVao.destroy();
|
||||
m_axesVbo.destroy();
|
||||
@@ -32,17 +51,30 @@ void ViewCube::initializeGL()
|
||||
setupBuffers();
|
||||
}
|
||||
|
||||
void ViewCube::paintGL(QOpenGLShaderProgram* simpleShader, int simpleShaderColorLoc, const QMatrix4x4& viewMatrix, int width, int height)
|
||||
void ViewCube::paintGL(QOpenGLShaderProgram* simpleShader, int simpleShaderColorLoc, const QMatrix4x4& viewMatrix, int width, int height, const QPoint& mousePos)
|
||||
{
|
||||
int viewCubeSize = 150;
|
||||
int viewCubeSize = 150 * QGuiApplication::primaryScreen()->devicePixelRatio();
|
||||
glViewport(width - viewCubeSize, height - viewCubeSize, viewCubeSize, viewCubeSize);
|
||||
|
||||
QRect viewCubeRect(width - viewCubeSize, 0, viewCubeSize, viewCubeSize);
|
||||
QPoint physicalMousePos = mousePos * QGuiApplication::primaryScreen()->devicePixelRatio();
|
||||
|
||||
m_isHovered = viewCubeRect.contains(physicalMousePos);
|
||||
|
||||
float opacity = m_isHovered ? 1.0f : 0.5f;
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
QMatrix4x4 viewCubeProjection;
|
||||
viewCubeProjection.ortho(-2, 2, -2, 2, -10, 10);
|
||||
|
||||
drawViewCube(viewCubeProjection, viewMatrix);
|
||||
drawViewCube(viewCubeProjection, viewMatrix, opacity);
|
||||
drawAxes(simpleShader, simpleShaderColorLoc, viewCubeProjection, viewMatrix);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void ViewCube::createFaceTextures()
|
||||
@@ -144,7 +176,7 @@ void ViewCube::setupBuffers()
|
||||
m_axesVbo.release();
|
||||
}
|
||||
|
||||
void ViewCube::drawViewCube(const QMatrix4x4& projection, const QMatrix4x4& view)
|
||||
void ViewCube::drawViewCube(const QMatrix4x4& projection, const QMatrix4x4& view, float opacity)
|
||||
{
|
||||
if (!m_textureShaderProgram || !m_textureShaderProgram->isLinked()) return;
|
||||
|
||||
@@ -153,6 +185,7 @@ void ViewCube::drawViewCube(const QMatrix4x4& projection, const QMatrix4x4& view
|
||||
m_textureShaderProgram->setUniformValue("projectionMatrix", projection);
|
||||
m_textureShaderProgram->setUniformValue("modelViewMatrix", view);
|
||||
m_textureShaderProgram->setUniformValue("texture_diffuse1", 0);
|
||||
m_textureShaderProgram->setUniformValue("opacity", opacity);
|
||||
|
||||
QOpenGLVertexArrayObject::Binder vaoBinder(&m_cubeVao);
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
@@ -193,3 +226,27 @@ void ViewCube::drawAxes(QOpenGLShaderProgram* simpleShader, int colorLoc, const
|
||||
|
||||
glLineWidth(1.0f);
|
||||
}
|
||||
|
||||
void ViewCube::paint2D(QPainter& painter, int widgetWidth, int widgetHeight)
|
||||
{
|
||||
if (!m_isHovered) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_homeButtonRect = getHomeButtonRect(widgetWidth);
|
||||
|
||||
if (m_homeButtonRenderer && m_homeButtonRenderer->isValid()) {
|
||||
m_homeButtonRenderer->render(&painter, m_homeButtonRect);
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewCube::handleMousePress(const QPoint& pos, int widgetWidth, int widgetHeight)
|
||||
{
|
||||
if (!m_isHovered) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QRect homeButtonRect = getHomeButtonRect(widgetWidth);
|
||||
|
||||
return homeButtonRect.contains(pos);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
#include <QMatrix4x4>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QPoint>
|
||||
|
||||
class QOpenGLShaderProgram;
|
||||
class QOpenGLTexture;
|
||||
class QPainter;
|
||||
class QSvgRenderer;
|
||||
|
||||
class ViewCube : protected QOpenGLFunctions
|
||||
{
|
||||
@@ -16,15 +19,21 @@ public:
|
||||
~ViewCube();
|
||||
|
||||
void initializeGL();
|
||||
void paintGL(QOpenGLShaderProgram* simpleShader, int simpleShaderColorLoc, const QMatrix4x4& viewMatrix, int width, int height);
|
||||
void paintGL(QOpenGLShaderProgram* simpleShader, int simpleShaderColorLoc, const QMatrix4x4& viewMatrix, int width, int height, const QPoint& mousePos);
|
||||
void paint2D(QPainter& painter, int widgetWidth, int widgetHeight);
|
||||
bool handleMousePress(const QPoint& pos, int widgetWidth, int widgetHeight);
|
||||
|
||||
private:
|
||||
void createFaceTextures();
|
||||
void initShaders();
|
||||
void setupBuffers();
|
||||
void drawViewCube(const QMatrix4x4& projection, const QMatrix4x4& view);
|
||||
void drawViewCube(const QMatrix4x4& projection, const QMatrix4x4& view, float opacity);
|
||||
void drawAxes(QOpenGLShaderProgram* simpleShader, int colorLoc, const QMatrix4x4& projection, const QMatrix4x4& view);
|
||||
|
||||
bool m_isHovered = false;
|
||||
QRect m_homeButtonRect;
|
||||
QSvgRenderer* m_homeButtonRenderer = nullptr;
|
||||
|
||||
QOpenGLTexture* m_faceTextures[6];
|
||||
|
||||
QOpenGLShaderProgram* m_textureShaderProgram = nullptr;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ViewportWidget.h"
|
||||
#include "Snapping.h"
|
||||
#include "Camera.h"
|
||||
#include "ViewCube.h"
|
||||
#include "SketchGrid.h"
|
||||
@@ -6,8 +7,13 @@
|
||||
#include "FeatureBrowser.h"
|
||||
#include "SketchFeature.h"
|
||||
#include "SketchLine.h"
|
||||
#include "SketchRectangle.h"
|
||||
#include "SketchCircle.h"
|
||||
#include "SketchObject.h"
|
||||
#include "ApplicationController.h"
|
||||
#include "RectangleTool.h"
|
||||
#include "LineTool.h"
|
||||
#include "CircleTool.h"
|
||||
#include <QMouseEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QWheelEvent>
|
||||
@@ -18,13 +24,20 @@
|
||||
#include <QSvgRenderer>
|
||||
#include <QWheelEvent>
|
||||
#include <QApplication>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QParallelAnimationGroup>
|
||||
#include <cmath>
|
||||
#include <QtMath>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
#include <map>
|
||||
|
||||
#include <BRepMesh_IncrementalMesh.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <Poly_Triangulation.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <gp_Dir.hxx>
|
||||
#include <gp_Trsf.hxx>
|
||||
|
||||
struct PntComparator {
|
||||
bool operator()(const gp_Pnt& a, const gp_Pnt& b) const {
|
||||
@@ -42,8 +55,9 @@ ViewportWidget::ViewportWidget(QWidget *parent)
|
||||
{
|
||||
m_camera = new Camera(this);
|
||||
connect(m_camera, &Camera::cameraChanged, this, QOverload<>::of(&QWidget::update));
|
||||
connect(m_camera, &Camera::restoreStateAnimationFinished, this, &ViewportWidget::onRestoreStateAnimationFinished);
|
||||
m_viewCube = new ViewCube();
|
||||
m_sketchGrid = new SketchGrid();
|
||||
m_sketchGrid = new SketchGrid(this);
|
||||
m_featureBrowser = new FeatureBrowser();
|
||||
setMouseTracking(true);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
@@ -55,12 +69,19 @@ ViewportWidget::ViewportWidget(QWidget *parent)
|
||||
m_toolIcons.insert(static_cast<int>(ApplicationController::ToolType::Circle), new QSvgRenderer(QString(":/icons/circle.svg"), this));
|
||||
|
||||
m_cursorRenderer = new QSvgRenderer(QString(":/icons/cursor.svg"), this);
|
||||
|
||||
m_sketchTools.insert(static_cast<int>(ApplicationController::ToolType::Line), new LineTool(this));
|
||||
m_sketchTools.insert(static_cast<int>(ApplicationController::ToolType::Rectangle), new RectangleTool(this));
|
||||
m_sketchTools.insert(static_cast<int>(ApplicationController::ToolType::Circle), new CircleTool(this));
|
||||
|
||||
m_snapping = new Snapping(this);
|
||||
}
|
||||
|
||||
ViewportWidget::~ViewportWidget()
|
||||
{
|
||||
makeCurrent();
|
||||
delete m_shaderProgram;
|
||||
delete m_litShaderProgram;
|
||||
delete m_viewCube;
|
||||
delete m_sketchGrid;
|
||||
m_vbo.destroy();
|
||||
@@ -68,6 +89,7 @@ ViewportWidget::~ViewportWidget()
|
||||
doneCurrent();
|
||||
|
||||
delete m_featureBrowser;
|
||||
delete m_snapping;
|
||||
}
|
||||
|
||||
void ViewportWidget::setDocument(Document* document)
|
||||
@@ -102,8 +124,10 @@ void ViewportWidget::initializeGL()
|
||||
|
||||
void ViewportWidget::paintGL()
|
||||
{
|
||||
const qreal retinaScale = devicePixelRatio();
|
||||
|
||||
// Main scene rendering
|
||||
glViewport(0, 0, width(), height());
|
||||
glViewport(0, 0, width() * retinaScale, height() * retinaScale);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
if (!m_shaderProgram || !m_shaderProgram->isLinked()) {
|
||||
@@ -142,117 +166,33 @@ void ViewportWidget::paintGL()
|
||||
}
|
||||
}
|
||||
|
||||
QVector<GLfloat> vertices;
|
||||
if (m_isSnappingOrigin) {
|
||||
const float rectSize = 0.0075f * -m_camera->zoom();
|
||||
if (m_currentPlane == SketchPlane::XY) {
|
||||
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_currentPlane == SketchPlane::XZ) {
|
||||
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_currentPlane == 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;
|
||||
}
|
||||
m_shaderProgram->setUniformValue(m_colorLoc, QVector4D(1.0f, 1.0f, 0.0f, 0.5f));
|
||||
m_vbo.bind();
|
||||
m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDrawArrays(GL_LINES, 0, vertices.size() / 3);
|
||||
glDisable(GL_BLEND);
|
||||
} else if (m_isSnappingVertex) {
|
||||
const float rectSize = 0.0075f * -m_camera->zoom();
|
||||
const auto& v = m_snapVertex;
|
||||
if (m_currentPlane == SketchPlane::XY) {
|
||||
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_currentPlane == SketchPlane::XZ) {
|
||||
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_currentPlane == SketchPlane::YZ) {
|
||||
vertices << v.X() << v.Y() - rectSize << v.Z() - rectSize << v.X() << v.Y() + rectSize << v.Z() - rectSize;
|
||||
vertices << v.X() << v.Y() + rectSize << v.Z() - rectSize << v.X() << v.Y() + rectSize << v.Z() + rectSize;
|
||||
vertices << v.X() << v.Y() + rectSize << v.Z() + rectSize << v.X() << v.Y() - rectSize << v.Z() + rectSize;
|
||||
vertices << v.X() << v.Y() - rectSize << v.Z() + rectSize << v.X() << v.Y() - rectSize << v.Z() - rectSize;
|
||||
}
|
||||
m_shaderProgram->setUniformValue(m_colorLoc, QVector4D(1.0f, 1.0f, 0.0f, 0.5f));
|
||||
m_vbo.bind();
|
||||
m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDrawArrays(GL_LINES, 0, vertices.size() / 3);
|
||||
glDisable(GL_BLEND);
|
||||
m_snapping->paintGL();
|
||||
|
||||
if (m_camera->isRotating()) {
|
||||
const float radius = 0.004f * -m_camera->uiCameraDistance();
|
||||
const int numSegments = 16;
|
||||
QMatrix4x4 invModelView = m_camera->modelViewMatrix().inverted();
|
||||
QVector3D rightVec = invModelView.column(0).toVector3D();
|
||||
QVector3D upVec = invModelView.column(1).toVector3D();
|
||||
|
||||
QVector<GLfloat> circleFillVertices;
|
||||
QVector3D center = m_camera->rotationPivot();
|
||||
|
||||
circleFillVertices << center.x() << center.y() << center.z(); // Center vertex for fan
|
||||
for (int i = 0; i <= numSegments; ++i) { // <= to close the circle
|
||||
float angle = (2.0f * M_PI * float(i)) / float(numSegments);
|
||||
QVector3D p = center + radius * (cos(angle) * rightVec + sin(angle) * upVec);
|
||||
circleFillVertices << p.x() << p.y() << p.z();
|
||||
}
|
||||
|
||||
if (m_isDefiningLine && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
||||
vertices.clear();
|
||||
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||
if (m_isSnappingOrigin) {
|
||||
worldPos.setX(0); worldPos.setY(0); worldPos.setZ(0);
|
||||
} else if (m_isSnappingVertex) {
|
||||
worldPos.setX(m_snapVertex.X()); worldPos.setY(m_snapVertex.Y()); worldPos.setZ(m_snapVertex.Z());
|
||||
} else if (m_isSnappingHorizontal) {
|
||||
if (m_currentPlane == SketchPlane::XY) worldPos.setZ(m_firstLinePoint.Z());
|
||||
else if (m_currentPlane == SketchPlane::XZ) worldPos.setY(m_firstLinePoint.Y());
|
||||
else if (m_currentPlane == SketchPlane::YZ) worldPos.setZ(m_firstLinePoint.Z());
|
||||
} else if (m_isSnappingVertical) {
|
||||
if (m_currentPlane == SketchPlane::XY) worldPos.setX(m_firstLinePoint.X());
|
||||
else if (m_currentPlane == SketchPlane::XZ) worldPos.setX(m_firstLinePoint.X());
|
||||
else if (m_currentPlane == SketchPlane::YZ) worldPos.setY(m_firstLinePoint.Y());
|
||||
}
|
||||
vertices << m_firstLinePoint.X() << m_firstLinePoint.Y() << m_firstLinePoint.Z();
|
||||
vertices << worldPos.x() << worldPos.y() << worldPos.z();
|
||||
|
||||
m_shaderProgram->setUniformValue(m_colorLoc, QVector4D(1.0f, 1.0f, 0.0f, 1.0f));
|
||||
m_shaderProgram->setUniformValue(m_colorLoc, QVector4D(0.0f, 0.0f, 1.0f, 1.0f)); // Blue
|
||||
m_vbo.bind();
|
||||
m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINES, 0, 2);
|
||||
m_vbo.allocate(circleFillVertices.constData(), circleFillVertices.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, circleFillVertices.size() / 3);
|
||||
}
|
||||
|
||||
if (m_isSnappingHorizontal || m_isSnappingVertical) {
|
||||
vertices.clear();
|
||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
QVector3D midPoint = (startPos + worldPos) / 2.0;
|
||||
const float indicatorSize = 0.02f * -m_camera->zoom();
|
||||
const float indicatorOffset = 0.02f * -m_camera->zoom();
|
||||
if (m_isSnappingHorizontal) {
|
||||
if (m_currentPlane == SketchPlane::XY) {
|
||||
vertices << midPoint.x() - indicatorSize << midPoint.y() << midPoint.z() + indicatorOffset;
|
||||
vertices << midPoint.x() + indicatorSize << midPoint.y() << midPoint.z() + indicatorOffset;
|
||||
} else if (m_currentPlane == SketchPlane::XZ) {
|
||||
vertices << midPoint.x() - indicatorSize << midPoint.y() + indicatorOffset << midPoint.z();
|
||||
vertices << midPoint.x() + indicatorSize << midPoint.y() + indicatorOffset << midPoint.z();
|
||||
} else if (m_currentPlane == SketchPlane::YZ) {
|
||||
vertices << midPoint.x() << midPoint.y() - indicatorSize << midPoint.z() + indicatorOffset;
|
||||
vertices << midPoint.x() << midPoint.y() + indicatorSize << midPoint.z() + indicatorOffset;
|
||||
}
|
||||
} else { // m_isSnappingVertical
|
||||
if (m_currentPlane == SketchPlane::XY) {
|
||||
vertices << midPoint.x() + indicatorOffset << midPoint.y() << midPoint.z() - indicatorSize;
|
||||
vertices << midPoint.x() + indicatorOffset << midPoint.y() << midPoint.z() + indicatorSize;
|
||||
} else if (m_currentPlane == SketchPlane::XZ) {
|
||||
vertices << midPoint.x() + indicatorOffset << midPoint.y() - indicatorSize << midPoint.z();
|
||||
vertices << midPoint.x() + indicatorOffset << midPoint.y() + indicatorSize << midPoint.z();
|
||||
} else if (m_currentPlane == SketchPlane::YZ) {
|
||||
vertices << midPoint.x() << midPoint.y() + indicatorOffset << midPoint.z() - indicatorSize;
|
||||
vertices << midPoint.x() << midPoint.y() + indicatorOffset << midPoint.z() + indicatorSize;
|
||||
}
|
||||
}
|
||||
m_vbo.bind();
|
||||
m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_LINES, 0, 2);
|
||||
}
|
||||
if (m_activeSketchTool) {
|
||||
m_activeSketchTool->paintGL();
|
||||
}
|
||||
|
||||
m_shaderProgram->release();
|
||||
@@ -261,17 +201,26 @@ void ViewportWidget::paintGL()
|
||||
QMatrix4x4 viewCubeModel;
|
||||
viewCubeModel.rotate(m_camera->xRotation() / 16.0f, 1, 0, 0);
|
||||
viewCubeModel.rotate(m_camera->yRotation() / 16.0f, 0, 1, 0);
|
||||
m_viewCube->paintGL(m_shaderProgram, m_colorLoc, viewCubeModel, width(), height());
|
||||
m_viewCube->paintGL(m_shaderProgram, m_colorLoc, viewCubeModel, width() * retinaScale, height() * retinaScale, m_currentMousePos);
|
||||
|
||||
glViewport(0, 0, width(), height());
|
||||
glViewport(0, 0, width() * retinaScale, height() * retinaScale);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
QPainter painter(this);
|
||||
if (m_currentPlane != SketchPlane::NONE) {
|
||||
drawAxisLabels(painter, model, projection);
|
||||
m_sketchGrid->paintAxisLabels(painter, static_cast<SketchGrid::SketchPlane>(m_currentPlane), model, projection);
|
||||
}
|
||||
m_featureBrowser->paint(painter, width(), height());
|
||||
m_viewCube->paint2D(painter, width(), height());
|
||||
|
||||
|
||||
if (m_activeSketchTool) {
|
||||
m_activeSketchTool->paint2D(painter, model, projection);
|
||||
}
|
||||
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(width() - 350, height() - 10, QString("Camera Distance: %1").arg(-m_camera->uiCameraDistance()));
|
||||
|
||||
painter.end();
|
||||
}
|
||||
@@ -279,12 +228,25 @@ void ViewportWidget::paintGL()
|
||||
void ViewportWidget::resizeGL(int w, int h)
|
||||
{
|
||||
projection.setToIdentity();
|
||||
projection.perspective(45.0f, w / float(h), 0.01f, 100.0f);
|
||||
projection.perspective(45.0f, w / float(h), 0.01f, 10000.0f);
|
||||
}
|
||||
|
||||
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));
|
||||
update();
|
||||
}
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (m_viewCube->handleMousePress(event->pos(), width(), height())) {
|
||||
m_camera->animateToHomeView();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_isSelectingPlane) {
|
||||
if (m_highlightedPlane != SketchPlane::NONE) {
|
||||
emit planeSelected(m_highlightedPlane);
|
||||
@@ -295,37 +257,12 @@ void ViewportWidget::mousePressEvent(QMouseEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_currentPlane != SketchPlane::NONE && m_activeTool == static_cast<int>(ApplicationController::ToolType::Line)) {
|
||||
gp_Pnt p;
|
||||
if (m_isSnappingOrigin) {
|
||||
p.SetCoord(0, 0, 0);
|
||||
} else if (m_isSnappingVertex) {
|
||||
p = m_snapVertex;
|
||||
} else {
|
||||
QVector3D worldPos = unproject(event->pos(), m_currentPlane);
|
||||
if (m_isSnappingHorizontal) {
|
||||
if (m_currentPlane == SketchPlane::XY) worldPos.setZ(m_firstLinePoint.Z());
|
||||
else if (m_currentPlane == SketchPlane::XZ) worldPos.setY(m_firstLinePoint.Y());
|
||||
else if (m_currentPlane == SketchPlane::YZ) worldPos.setZ(m_firstLinePoint.Z());
|
||||
} else if (m_isSnappingVertical) {
|
||||
if (m_currentPlane == SketchPlane::XY) worldPos.setX(m_firstLinePoint.X());
|
||||
else if (m_currentPlane == SketchPlane::XZ) worldPos.setX(m_firstLinePoint.X());
|
||||
else if (m_currentPlane == SketchPlane::YZ) worldPos.setY(m_firstLinePoint.Y());
|
||||
}
|
||||
p.SetCoord(worldPos.x(), worldPos.y(), worldPos.z());
|
||||
if (m_activeSketchTool) {
|
||||
m_activeSketchTool->mousePressEvent(event);
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_isDefiningLine) {
|
||||
m_firstLinePoint = p;
|
||||
m_isDefiningLine = true;
|
||||
} else {
|
||||
emit lineAdded(m_firstLinePoint, p);
|
||||
m_firstLinePoint = p;
|
||||
}
|
||||
update();
|
||||
}
|
||||
} else {
|
||||
lastPos = event->pos();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,208 +278,106 @@ void ViewportWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldSnap = false;
|
||||
if (m_currentPlane != SketchPlane::NONE && m_activeTool != static_cast<int>(ApplicationController::ToolType::None)) {
|
||||
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||
const float snapRectHalfSize = 0.0075f * -m_camera->zoom();
|
||||
|
||||
switch (m_currentPlane) {
|
||||
case SketchPlane::XY:
|
||||
shouldSnap = qAbs(worldPos.x()) < snapRectHalfSize && qAbs(worldPos.z()) < snapRectHalfSize;
|
||||
break;
|
||||
case SketchPlane::XZ:
|
||||
shouldSnap = qAbs(worldPos.x()) < snapRectHalfSize && qAbs(worldPos.y()) < snapRectHalfSize;
|
||||
break;
|
||||
case SketchPlane::YZ:
|
||||
shouldSnap = qAbs(worldPos.y()) < snapRectHalfSize && qAbs(worldPos.z()) < snapRectHalfSize;
|
||||
break;
|
||||
case SketchPlane::NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSnap != m_isSnappingOrigin) {
|
||||
m_isSnappingOrigin = shouldSnap;
|
||||
if (m_isSnappingOrigin) {
|
||||
m_isSnappingVertex = false;
|
||||
}
|
||||
if (m_snapping->update(m_currentMousePos)) {
|
||||
update();
|
||||
}
|
||||
|
||||
bool oldIsSnappingVertex = m_isSnappingVertex;
|
||||
m_isSnappingVertex = false;
|
||||
if (!m_isSnappingOrigin && m_document && m_currentPlane != SketchPlane::NONE && m_activeTool != static_cast<int>(ApplicationController::ToolType::None)) {
|
||||
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||
const float snapRectHalfSize = 0.0075f * -m_camera->zoom();
|
||||
|
||||
for (Feature* feature : m_document->features()) {
|
||||
if (auto sketch = dynamic_cast<SketchFeature*>(feature)) {
|
||||
for (const auto& obj : sketch->objects()) {
|
||||
if (obj->type() == SketchObject::ObjectType::Line) {
|
||||
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_currentPlane) {
|
||||
case SketchPlane::XY:
|
||||
isClose = qAbs(worldPos.x() - vertex.X()) < snapRectHalfSize && qAbs(worldPos.z() - vertex.Z()) < snapRectHalfSize;
|
||||
break;
|
||||
case SketchPlane::XZ:
|
||||
isClose = qAbs(worldPos.x() - vertex.X()) < snapRectHalfSize && qAbs(worldPos.y() - vertex.Y()) < snapRectHalfSize;
|
||||
break;
|
||||
case SketchPlane::YZ:
|
||||
isClose = qAbs(worldPos.y() - vertex.Y()) < snapRectHalfSize && qAbs(worldPos.z() - vertex.Z()) < snapRectHalfSize;
|
||||
break;
|
||||
case SketchPlane::NONE:
|
||||
break;
|
||||
if (m_activeSketchTool) {
|
||||
m_activeSketchTool->mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
if (isClose) {
|
||||
m_isSnappingVertex = true;
|
||||
m_snapVertex = vertex;
|
||||
goto end_snap_check;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end_snap_check:;
|
||||
}
|
||||
|
||||
if (oldIsSnappingVertex != m_isSnappingVertex) {
|
||||
update();
|
||||
}
|
||||
|
||||
bool oldIsSnappingHorizontal = m_isSnappingHorizontal;
|
||||
bool oldIsSnappingVertical = m_isSnappingVertical;
|
||||
m_isSnappingHorizontal = false;
|
||||
m_isSnappingVertical = false;
|
||||
|
||||
if (m_isDefiningLine && !m_isSnappingOrigin && !m_isSnappingVertex) {
|
||||
QVector3D worldPos = unproject(m_currentMousePos, m_currentPlane);
|
||||
QVector3D startPos(m_firstLinePoint.X(), m_firstLinePoint.Y(), m_firstLinePoint.Z());
|
||||
QVector3D delta = worldPos - startPos;
|
||||
|
||||
if (delta.length() > 1e-6) {
|
||||
const double snapAngleThreshold = qDegreesToRadians(2.0);
|
||||
double angle = 0;
|
||||
|
||||
if (m_currentPlane == SketchPlane::XY) {
|
||||
angle = atan2(delta.z(), delta.x());
|
||||
} else if (m_currentPlane == SketchPlane::XZ) {
|
||||
angle = atan2(delta.y(), delta.x());
|
||||
} else if (m_currentPlane == SketchPlane::YZ) {
|
||||
angle = atan2(delta.z(), delta.y());
|
||||
}
|
||||
|
||||
if (qAbs(sin(angle)) < sin(snapAngleThreshold)) {
|
||||
m_isSnappingHorizontal = true;
|
||||
} else if (qAbs(cos(angle)) < sin(snapAngleThreshold)) {
|
||||
m_isSnappingVertical = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oldIsSnappingHorizontal != m_isSnappingHorizontal || oldIsSnappingVertical != m_isSnappingVertical) {
|
||||
update();
|
||||
}
|
||||
|
||||
if (event->buttons() & Qt::MiddleButton) {
|
||||
m_camera->processMouseMovement(event, lastPos);
|
||||
}
|
||||
lastPos = event->pos();
|
||||
m_camera->mouseMoveEvent(event, height());
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewportWidget::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
m_camera->processWheel(event);
|
||||
QVector3D worldPos = unproject(event->position().toPoint(), m_currentPlane);
|
||||
m_camera->wheelEvent(event, worldPos);
|
||||
}
|
||||
|
||||
void ViewportWidget::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_Escape) {
|
||||
if (m_isDefiningLine) {
|
||||
m_isDefiningLine = false;
|
||||
emit toolDeactivated();
|
||||
update();
|
||||
if (m_activeSketchTool) {
|
||||
m_activeSketchTool->keyPressEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->key() == Qt::Key_Escape) {
|
||||
if (m_isSelectingPlane) {
|
||||
m_isSelectingPlane = false;
|
||||
m_highlightedPlane = SketchPlane::NONE;
|
||||
m_currentPlane = SketchPlane::XY;
|
||||
update();
|
||||
return;
|
||||
}
|
||||
else if (m_activeTool) {
|
||||
emit toolDeactivated();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
QOpenGLWidget::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void ViewportWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::MiddleButton) {
|
||||
m_camera->stopRotation();
|
||||
update();
|
||||
}
|
||||
QOpenGLWidget::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void ViewportWidget::addLine(const gp_Pnt& start, const gp_Pnt& end)
|
||||
{
|
||||
emit lineAdded(start, end);
|
||||
}
|
||||
|
||||
void ViewportWidget::deactivateActiveTool()
|
||||
{
|
||||
emit toolDeactivated();
|
||||
}
|
||||
|
||||
bool ViewportWidget::isSnappingOrigin() const
|
||||
{
|
||||
return m_snapping->isSnappingOrigin();
|
||||
}
|
||||
|
||||
bool ViewportWidget::isSnappingVertex() const
|
||||
{
|
||||
return m_snapping->isSnappingVertex();
|
||||
}
|
||||
|
||||
const gp_Pnt& ViewportWidget::snapVertex() const
|
||||
{
|
||||
return m_snapping->snapVertex();
|
||||
}
|
||||
|
||||
void ViewportWidget::setSnappingHorizontal(bool snapping)
|
||||
{
|
||||
m_isSnappingHorizontal = snapping;
|
||||
}
|
||||
|
||||
void ViewportWidget::setSnappingVertical(bool snapping)
|
||||
{
|
||||
m_isSnappingVertical = snapping;
|
||||
}
|
||||
|
||||
bool ViewportWidget::focusNextPrevChild(bool next)
|
||||
{
|
||||
if (m_activeTool != static_cast<int>(ApplicationController::ToolType::None)) {
|
||||
return false;
|
||||
}
|
||||
return QOpenGLWidget::focusNextPrevChild(next);
|
||||
}
|
||||
|
||||
void ViewportWidget::onSketchModeStarted(SketchPlane plane)
|
||||
{
|
||||
m_currentPlane = plane;
|
||||
|
||||
m_camera->saveState();
|
||||
|
||||
float targetXRot = m_camera->xRotation();
|
||||
float targetYRot = m_camera->yRotation();
|
||||
switch (plane) {
|
||||
case SketchPlane::XY: // Top view
|
||||
targetXRot = 90 * 16;
|
||||
targetYRot = 0;
|
||||
break;
|
||||
case SketchPlane::XZ: // Front view
|
||||
targetXRot = 0;
|
||||
targetYRot = 0;
|
||||
break;
|
||||
case SketchPlane::YZ: // Right view
|
||||
targetXRot = 0;
|
||||
targetYRot = -90 * 16;
|
||||
break;
|
||||
case SketchPlane::NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
auto* animGroup = new QParallelAnimationGroup(this);
|
||||
|
||||
auto* xRotAnim = new QPropertyAnimation(m_camera, "xRotation");
|
||||
xRotAnim->setDuration(300);
|
||||
xRotAnim->setStartValue(m_camera->xRotation());
|
||||
xRotAnim->setEndValue(targetXRot);
|
||||
xRotAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(xRotAnim);
|
||||
|
||||
auto* yRotAnim = new QPropertyAnimation(m_camera, "yRotation");
|
||||
yRotAnim->setDuration(300);
|
||||
yRotAnim->setStartValue(m_camera->yRotation());
|
||||
yRotAnim->setEndValue(targetYRot);
|
||||
yRotAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(yRotAnim);
|
||||
|
||||
auto* panXAnim = new QPropertyAnimation(m_camera, "panX");
|
||||
panXAnim->setDuration(300);
|
||||
panXAnim->setStartValue(m_camera->panX());
|
||||
panXAnim->setEndValue(0.0f);
|
||||
panXAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(panXAnim);
|
||||
|
||||
auto* panYAnim = new QPropertyAnimation(m_camera, "panY");
|
||||
panYAnim->setDuration(300);
|
||||
panYAnim->setStartValue(m_camera->panY());
|
||||
panYAnim->setEndValue(0.0f);
|
||||
panYAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(panYAnim);
|
||||
|
||||
auto* zoomAnim = new QPropertyAnimation(m_camera, "zoom");
|
||||
zoomAnim->setDuration(300);
|
||||
zoomAnim->setStartValue(m_camera->zoom());
|
||||
zoomAnim->setEndValue(-20.0f);
|
||||
zoomAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(zoomAnim);
|
||||
|
||||
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
m_camera->animateToPlaneView(static_cast<int>(plane));
|
||||
}
|
||||
|
||||
void ViewportWidget::onPlaneSelectionModeStarted()
|
||||
@@ -555,50 +390,14 @@ void ViewportWidget::onPlaneSelectionModeStarted()
|
||||
|
||||
void ViewportWidget::onSketchModeEnded()
|
||||
{
|
||||
auto* animGroup = new QParallelAnimationGroup(this);
|
||||
m_camera->animateRestoreState();
|
||||
}
|
||||
|
||||
auto* xRotAnim = new QPropertyAnimation(m_camera, "xRotation");
|
||||
xRotAnim->setDuration(300);
|
||||
xRotAnim->setStartValue(m_camera->xRotation());
|
||||
xRotAnim->setEndValue(m_camera->savedXRot());
|
||||
xRotAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(xRotAnim);
|
||||
|
||||
auto* yRotAnim = new QPropertyAnimation(m_camera, "yRotation");
|
||||
yRotAnim->setDuration(300);
|
||||
yRotAnim->setStartValue(m_camera->yRotation());
|
||||
yRotAnim->setEndValue(m_camera->savedYRot());
|
||||
yRotAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(yRotAnim);
|
||||
|
||||
auto* panXAnim = new QPropertyAnimation(m_camera, "panX");
|
||||
panXAnim->setDuration(300);
|
||||
panXAnim->setStartValue(m_camera->panX());
|
||||
panXAnim->setEndValue(m_camera->savedPanX());
|
||||
panXAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(panXAnim);
|
||||
|
||||
auto* panYAnim = new QPropertyAnimation(m_camera, "panY");
|
||||
panYAnim->setDuration(300);
|
||||
panYAnim->setStartValue(m_camera->panY());
|
||||
panYAnim->setEndValue(m_camera->savedPanY());
|
||||
panYAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(panYAnim);
|
||||
|
||||
auto* zoomAnim = new QPropertyAnimation(m_camera, "zoom");
|
||||
zoomAnim->setDuration(300);
|
||||
zoomAnim->setStartValue(m_camera->zoom());
|
||||
zoomAnim->setEndValue(m_camera->savedZoom());
|
||||
zoomAnim->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
animGroup->addAnimation(zoomAnim);
|
||||
|
||||
connect(animGroup, &QParallelAnimationGroup::finished, this, [this]() {
|
||||
void ViewportWidget::onRestoreStateAnimationFinished()
|
||||
{
|
||||
// Return to showing the base XY grid when not in a sketch
|
||||
m_currentPlane = SketchPlane::XY;
|
||||
update();
|
||||
});
|
||||
|
||||
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
}
|
||||
|
||||
QVector3D ViewportWidget::project(const QVector3D& worldCoord, const QMatrix4x4& modelView, const QMatrix4x4& projection, const QRect& viewport)
|
||||
@@ -616,43 +415,19 @@ QVector3D ViewportWidget::project(const QVector3D& worldCoord, const QMatrix4x4&
|
||||
return QVector3D(winX, winY, (ndc.z() + 1.0) / 2.0);
|
||||
}
|
||||
|
||||
void ViewportWidget::drawAxisLabels(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection)
|
||||
{
|
||||
painter.setPen(Qt::white);
|
||||
painter.setFont(QFont("Arial", 10));
|
||||
|
||||
const int range = 50;
|
||||
const int step = 5;
|
||||
|
||||
auto drawLabelsForAxis = [&](int axis_idx) {
|
||||
for (int i = -range; i <= range; i += step) {
|
||||
if (i == 0) continue;
|
||||
QVector3D worldCoord;
|
||||
worldCoord[axis_idx] = i;
|
||||
|
||||
QVector3D screenPos = project(worldCoord, modelView, projection, rect());
|
||||
if (screenPos.z() < 1.0f) { // Not clipped
|
||||
painter.drawText(screenPos.toPoint(), QString::number(i));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (m_currentPlane == SketchPlane::XY) {
|
||||
drawLabelsForAxis(0); // X
|
||||
drawLabelsForAxis(2); // Y
|
||||
} else if (m_currentPlane == SketchPlane::XZ) {
|
||||
drawLabelsForAxis(0); // X
|
||||
drawLabelsForAxis(1); // Z
|
||||
} else if (m_currentPlane == SketchPlane::YZ) {
|
||||
drawLabelsForAxis(1); // Y
|
||||
drawLabelsForAxis(2); // Z
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportWidget::onActiveToolChanged(int tool)
|
||||
{
|
||||
if (m_activeSketchTool) {
|
||||
m_activeSketchTool->deactivate();
|
||||
m_activeSketchTool = nullptr;
|
||||
}
|
||||
|
||||
m_activeTool = tool;
|
||||
m_isDefiningLine = false;
|
||||
|
||||
if (m_sketchTools.contains(tool)) {
|
||||
m_activeSketchTool = m_sketchTools.value(tool);
|
||||
m_activeSketchTool->activate();
|
||||
}
|
||||
|
||||
if (tool == static_cast<int>(ApplicationController::ToolType::None)) {
|
||||
unsetCursor();
|
||||
@@ -744,10 +519,9 @@ void ViewportWidget::drawSketch(const SketchFeature* sketch)
|
||||
{
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glLineWidth(2.0f);
|
||||
glPointSize(5.0f);
|
||||
|
||||
QVector<GLfloat> lineVertices;
|
||||
QMap<gp_Pnt, int, PntComparator> vertexCounts;
|
||||
std::map<gp_Pnt, int, PntComparator> vertexCounts;
|
||||
|
||||
for (const auto& obj : sketch->objects()) {
|
||||
if (obj->type() == SketchObject::ObjectType::Line) {
|
||||
@@ -759,14 +533,69 @@ void ViewportWidget::drawSketch(const SketchFeature* sketch)
|
||||
|
||||
vertexCounts[start]++;
|
||||
vertexCounts[end]++;
|
||||
}
|
||||
} else if (obj->type() == SketchObject::ObjectType::Circle) {
|
||||
auto circle = static_cast<const SketchCircle*>(obj);
|
||||
const auto& center = circle->center();
|
||||
double radius = circle->radius();
|
||||
QVector3D centerPos(center.X(), center.Y(), center.Z());
|
||||
|
||||
const int numSegments = 64;
|
||||
QVector3D u_axis, v_axis;
|
||||
switch (sketch->plane()) {
|
||||
case SketchFeature::SketchPlane::XY:
|
||||
u_axis = QVector3D(1, 0, 0);
|
||||
v_axis = QVector3D(0, 0, 1);
|
||||
break;
|
||||
case SketchFeature::SketchPlane::XZ:
|
||||
u_axis = QVector3D(1, 0, 0);
|
||||
v_axis = QVector3D(0, 1, 0);
|
||||
break;
|
||||
case SketchFeature::SketchPlane::YZ:
|
||||
u_axis = QVector3D(0, 1, 0);
|
||||
v_axis = QVector3D(0, 0, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
QVector<GLfloat> pointVertices;
|
||||
for (auto it = vertexCounts.constBegin(); it != vertexCounts.constEnd(); ++it) {
|
||||
if (it.value() == 1) {
|
||||
const gp_Pnt& p = it.key();
|
||||
pointVertices << p.X() << p.Y() << p.Z();
|
||||
for (int i = 0; i < numSegments; ++i) {
|
||||
double angle1 = 2.0 * M_PI * double(i) / double(numSegments);
|
||||
QVector3D p1 = centerPos + radius * (cos(angle1) * u_axis + sin(angle1) * v_axis);
|
||||
|
||||
double angle2 = 2.0 * M_PI * double(i + 1) / double(numSegments);
|
||||
QVector3D p2 = centerPos + radius * (cos(angle2) * u_axis + sin(angle2) * v_axis);
|
||||
|
||||
lineVertices << p1.x() << p1.y() << p1.z();
|
||||
lineVertices << p2.x() << p2.y() << p2.z();
|
||||
}
|
||||
} else if (obj->type() == SketchObject::ObjectType::Rectangle) {
|
||||
auto rect = static_cast<const SketchRectangle*>(obj);
|
||||
const auto& p1 = rect->corner1();
|
||||
const auto& p3 = rect->corner2();
|
||||
|
||||
gp_Pnt p2, p4;
|
||||
if (sketch->plane() == SketchFeature::SketchPlane::XY) {
|
||||
p2.SetCoord(p3.X(), p1.Y(), p1.Z());
|
||||
p4.SetCoord(p1.X(), p1.Y(), p3.Z());
|
||||
} else if (sketch->plane() == SketchFeature::SketchPlane::XZ) {
|
||||
p2.SetCoord(p3.X(), p1.Y(), p1.Z());
|
||||
p4.SetCoord(p1.X(), p3.Y(), p1.Z());
|
||||
} else if (sketch->plane() == SketchFeature::SketchPlane::YZ) {
|
||||
p2.SetCoord(p1.X(), p3.Y(), p1.Z());
|
||||
p4.SetCoord(p1.X(), p1.Y(), p3.Z());
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
vertexCounts[p1] += 2;
|
||||
vertexCounts[p2] += 2;
|
||||
vertexCounts[p3] += 2;
|
||||
vertexCounts[p4] += 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -775,16 +604,137 @@ void ViewportWidget::drawSketch(const SketchFeature* sketch)
|
||||
|
||||
if (!lineVertices.isEmpty()) {
|
||||
m_vbo.allocate(lineVertices.constData(), lineVertices.size() * sizeof(GLfloat));
|
||||
m_shaderProgram->enableAttributeArray(0);
|
||||
m_shaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 3, 0);
|
||||
m_shaderProgram->disableAttributeArray(1);
|
||||
glDrawArrays(GL_LINES, 0, lineVertices.size() / 3);
|
||||
}
|
||||
if (!pointVertices.isEmpty()) {
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
m_vbo.allocate(pointVertices.constData(), pointVertices.size() * sizeof(GLfloat));
|
||||
glDrawArrays(GL_POINTS, 0, pointVertices.size() / 3);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_POINT_SMOOTH);
|
||||
|
||||
QVector<gp_Pnt> unattachedVertices;
|
||||
for (const auto& pair : vertexCounts) {
|
||||
if (pair.second == 1) {
|
||||
unattachedVertices.append(pair.first);
|
||||
}
|
||||
}
|
||||
|
||||
if (!unattachedVertices.isEmpty()) {
|
||||
const float radius = 0.004f * -m_camera->uiCameraDistance();
|
||||
const int numSegments = 16;
|
||||
QMatrix4x4 invModelView = m_camera->modelViewMatrix().inverted();
|
||||
QVector3D rightVec = invModelView.column(0).toVector3D();
|
||||
QVector3D upVec = invModelView.column(1).toVector3D();
|
||||
|
||||
QVector<GLfloat> circleFillVertices;
|
||||
QVector<GLfloat> circleOutlineVertices;
|
||||
|
||||
for (const auto& centerPnt : unattachedVertices) {
|
||||
QVector3D center(centerPnt.X(), centerPnt.Y(), centerPnt.Z());
|
||||
|
||||
circleFillVertices << center.x() << center.y() << center.z(); // Center vertex for fan
|
||||
|
||||
QVector3D firstCircumferencePoint;
|
||||
for (int i = 0; i < numSegments; ++i) {
|
||||
float angle = (2.0f * M_PI * float(i)) / float(numSegments);
|
||||
QVector3D p = center + radius * (cos(angle) * rightVec + sin(angle) * upVec);
|
||||
if (i == 0) {
|
||||
firstCircumferencePoint = p;
|
||||
}
|
||||
circleFillVertices << p.x() << p.y() << p.z();
|
||||
circleOutlineVertices << p.x() << p.y() << p.z();
|
||||
}
|
||||
circleFillVertices << firstCircumferencePoint.x() << firstCircumferencePoint.y() << firstCircumferencePoint.z();
|
||||
}
|
||||
|
||||
// Draw fills
|
||||
m_shaderProgram->setUniformValue(m_colorLoc, QVector4D(0.2f, 0.3f, 0.3f, 1.0f));
|
||||
m_vbo.allocate(circleFillVertices.constData(), circleFillVertices.size() * sizeof(GLfloat));
|
||||
const int vertsPerFan = numSegments + 2;
|
||||
for (size_t i = 0; i < unattachedVertices.size(); ++i) {
|
||||
glDrawArrays(GL_TRIANGLE_FAN, i * vertsPerFan, vertsPerFan);
|
||||
}
|
||||
|
||||
// Draw outlines
|
||||
m_shaderProgram->setUniformValue(m_colorLoc, QVector4D(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
m_vbo.allocate(circleOutlineVertices.constData(), circleOutlineVertices.size() * sizeof(GLfloat));
|
||||
const int vertsPerLoop = numSegments;
|
||||
for (size_t i = 0; i < unattachedVertices.size(); ++i) {
|
||||
glDrawArrays(GL_LINE_LOOP, i * vertsPerLoop, vertsPerLoop);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw faces
|
||||
if (!sketch->shape().IsNull()) {
|
||||
glDisable(GL_CULL_FACE);
|
||||
BRepMesh_IncrementalMesh(sketch->shape(), 0.1, Standard_False, 0.5, Standard_True); // Linear deflection, compute normals
|
||||
|
||||
QVector<GLfloat> faceData;
|
||||
TopExp_Explorer explorer(sketch->shape(), TopAbs_FACE);
|
||||
for (; explorer.More(); explorer.Next()) {
|
||||
TopoDS_Face face = TopoDS::Face(explorer.Current());
|
||||
TopLoc_Location location;
|
||||
Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation(face, location);
|
||||
|
||||
if (!triangulation.IsNull()) {
|
||||
gp_Trsf locTrsf = location.Transformation();
|
||||
|
||||
if (triangulation->HasNormals()) {
|
||||
for (int i = 1; i <= triangulation->NbTriangles(); ++i) {
|
||||
const Poly_Triangle& triangle = triangulation->Triangle(i);
|
||||
for (int j = 1; j <= 3; ++j) {
|
||||
int nodeIdx = triangle.Value(j);
|
||||
gp_Pnt p = triangulation->Node(nodeIdx).Transformed(location);
|
||||
gp_Dir n = triangulation->Normal(nodeIdx);
|
||||
n.Transform(locTrsf);
|
||||
|
||||
if (face.Orientation() == TopAbs_REVERSED) {
|
||||
n.Reverse();
|
||||
}
|
||||
faceData << p.X() << p.Y() << p.Z();
|
||||
faceData << n.X() << n.Y() << n.Z();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 1; i <= triangulation->NbTriangles(); ++i) {
|
||||
const Poly_Triangle& triangle = triangulation->Triangle(i);
|
||||
gp_Pnt p1 = triangulation->Node(triangle.Value(1)).Transformed(location);
|
||||
gp_Pnt p2 = triangulation->Node(triangle.Value(2)).Transformed(location);
|
||||
gp_Pnt p3 = triangulation->Node(triangle.Value(3)).Transformed(location);
|
||||
|
||||
QVector3D v1(p1.X(), p1.Y(), p1.Z());
|
||||
QVector3D v2(p2.X(), p2.Y(), p2.Z());
|
||||
QVector3D v3(p3.X(), p3.Y(), p3.Z());
|
||||
QVector3D faceNormal = QVector3D::crossProduct(v2 - v1, v3 - v1).normalized();
|
||||
if (face.Orientation() == TopAbs_REVERSED) {
|
||||
faceNormal = -faceNormal;
|
||||
}
|
||||
faceData << p1.X() << p1.Y() << p1.Z() << faceNormal.x() << faceNormal.y() << faceNormal.z();
|
||||
faceData << p2.X() << p2.Y() << p2.Z() << faceNormal.x() << faceNormal.y() << faceNormal.z();
|
||||
faceData << p3.X() << p3.Y() << p3.Z() << faceNormal.x() << faceNormal.y() << faceNormal.z();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!faceData.isEmpty()) {
|
||||
m_litShaderProgram->bind();
|
||||
m_litShaderProgram->setUniformValue(m_litProjMatrixLoc, projection);
|
||||
m_litShaderProgram->setUniformValue(m_litMvMatrixLoc, m_camera->modelViewMatrix());
|
||||
m_litShaderProgram->setUniformValue(m_litNormalMatrixLoc, m_camera->modelViewMatrix().normalMatrix());
|
||||
|
||||
m_vbo.bind();
|
||||
m_vbo.allocate(faceData.constData(), faceData.size() * sizeof(GLfloat));
|
||||
|
||||
m_litShaderProgram->enableAttributeArray(0);
|
||||
m_litShaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 3, 6 * sizeof(GLfloat));
|
||||
m_litShaderProgram->enableAttributeArray(1);
|
||||
m_litShaderProgram->setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(GLfloat), 3, 6 * sizeof(GLfloat));
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, faceData.size() / 6);
|
||||
m_litShaderProgram->disableAttributeArray(1);
|
||||
m_shaderProgram->bind(); // rebind simple shader for subsequent draws
|
||||
m_shaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 3, 0);
|
||||
}
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
@@ -792,8 +742,8 @@ void ViewportWidget::drawSketch(const SketchFeature* sketch)
|
||||
|
||||
void ViewportWidget::initShaders()
|
||||
{
|
||||
// Simple shader for lines and grids
|
||||
m_shaderProgram = new QOpenGLShaderProgram(this);
|
||||
|
||||
if (!m_shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/simple.vert")) {
|
||||
qCritical() << "Vertex shader compilation failed:" << m_shaderProgram->log();
|
||||
return;
|
||||
@@ -806,10 +756,27 @@ void ViewportWidget::initShaders()
|
||||
qCritical() << "Shader program linking failed:" << m_shaderProgram->log();
|
||||
return;
|
||||
}
|
||||
|
||||
m_projMatrixLoc = m_shaderProgram->uniformLocation("projectionMatrix");
|
||||
m_mvMatrixLoc = m_shaderProgram->uniformLocation("modelViewMatrix");
|
||||
m_colorLoc = m_shaderProgram->uniformLocation("objectColor");
|
||||
|
||||
// Lit shader for faces
|
||||
m_litShaderProgram = new QOpenGLShaderProgram(this);
|
||||
if (!m_litShaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/lit.vert")) {
|
||||
qCritical() << "Lit vertex shader compilation failed:" << m_litShaderProgram->log();
|
||||
return;
|
||||
}
|
||||
if (!m_litShaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/lit.frag")) {
|
||||
qCritical() << "Lit fragment shader compilation failed:" << m_litShaderProgram->log();
|
||||
return;
|
||||
}
|
||||
if (!m_litShaderProgram->link()) {
|
||||
qCritical() << "Lit shader program linking failed:" << m_litShaderProgram->log();
|
||||
return;
|
||||
}
|
||||
m_litProjMatrixLoc = m_litShaderProgram->uniformLocation("projectionMatrix");
|
||||
m_litMvMatrixLoc = m_litShaderProgram->uniformLocation("modelViewMatrix");
|
||||
m_litNormalMatrixLoc = m_litShaderProgram->uniformLocation("normalMatrix");
|
||||
}
|
||||
|
||||
void ViewportWidget::drawSelectionPlanes()
|
||||
@@ -824,6 +791,9 @@ void ViewportWidget::drawSelectionPlanes()
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
m_vbo.bind();
|
||||
m_shaderProgram->enableAttributeArray(0);
|
||||
m_shaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 3, 0);
|
||||
m_shaderProgram->disableAttributeArray(1);
|
||||
QVector<GLfloat> vertices;
|
||||
|
||||
auto drawPlane = [&](const QVector<GLfloat>& quadVerts, bool highlighted) {
|
||||
|
||||
@@ -20,6 +20,8 @@ class Document;
|
||||
class FeatureBrowser;
|
||||
class SketchFeature;
|
||||
class Camera;
|
||||
class SketchTool;
|
||||
class Snapping;
|
||||
|
||||
class ViewportWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
{
|
||||
@@ -38,6 +40,27 @@ public:
|
||||
|
||||
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);
|
||||
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; }
|
||||
const QPoint& currentMousePos() const { return m_currentMousePos; }
|
||||
bool isSnappingOrigin() const;
|
||||
bool isSnappingVertex() const;
|
||||
const gp_Pnt& snapVertex() const;
|
||||
int activeTool() const { return m_activeTool; }
|
||||
bool isSnappingHorizontal() const { return m_isSnappingHorizontal; }
|
||||
bool isSnappingVertical() const { return m_isSnappingVertical; }
|
||||
void setSnappingHorizontal(bool snapping);
|
||||
void setSnappingVertical(bool snapping);
|
||||
|
||||
void addLine(const gp_Pnt& start, const gp_Pnt& end);
|
||||
void deactivateActiveTool();
|
||||
|
||||
public slots:
|
||||
void onSketchModeStarted(SketchPlane plane);
|
||||
void onSketchModeEnded();
|
||||
@@ -46,30 +69,35 @@ public slots:
|
||||
|
||||
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 toolDeactivated();
|
||||
|
||||
private slots:
|
||||
void onRestoreStateAnimationFinished();
|
||||
|
||||
protected:
|
||||
void initializeGL() override;
|
||||
void paintGL() override;
|
||||
void resizeGL(int w, int h) override;
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
bool focusNextPrevChild(bool next) override;
|
||||
|
||||
private:
|
||||
void initShaders();
|
||||
QVector3D project(const QVector3D& worldCoord, const QMatrix4x4& modelView, const QMatrix4x4& projection, const QRect& viewport);
|
||||
QVector3D unproject(const QPoint& screenPos, SketchPlane plane);
|
||||
void drawAxisLabels(QPainter& painter, const QMatrix4x4& modelView, const QMatrix4x4& projection);
|
||||
void drawSketch(const SketchFeature* sketch);
|
||||
void drawSelectionPlanes();
|
||||
ViewportWidget::SketchPlane checkPlaneSelection(const QPoint& screenPos);
|
||||
|
||||
QMatrix4x4 projection;
|
||||
QOpenGLShaderProgram* m_shaderProgram = nullptr;
|
||||
QOpenGLShaderProgram* m_litShaderProgram = nullptr;
|
||||
QOpenGLVertexArrayObject m_vao;
|
||||
QOpenGLBuffer m_vbo;
|
||||
|
||||
@@ -77,6 +105,11 @@ private:
|
||||
int m_projMatrixLoc = -1;
|
||||
int m_mvMatrixLoc = -1;
|
||||
int m_colorLoc = -1;
|
||||
|
||||
// Lit shader uniform locations
|
||||
int m_litProjMatrixLoc = -1;
|
||||
int m_litMvMatrixLoc = -1;
|
||||
int m_litNormalMatrixLoc = -1;
|
||||
Camera* m_camera = nullptr;
|
||||
ViewCube* m_viewCube;
|
||||
SketchGrid* m_sketchGrid = nullptr;
|
||||
@@ -88,19 +121,15 @@ private:
|
||||
SketchPlane m_highlightedPlane = SketchPlane::NONE;
|
||||
|
||||
int m_activeTool = 0;
|
||||
bool m_isDefiningLine = false;
|
||||
gp_Pnt m_firstLinePoint;
|
||||
SketchTool* m_activeSketchTool = nullptr;
|
||||
QMap<int, SketchTool*> m_sketchTools;
|
||||
QPoint m_currentMousePos;
|
||||
bool m_isSnappingOrigin = false;
|
||||
bool m_isSnappingVertex = false;
|
||||
gp_Pnt m_snapVertex;
|
||||
Snapping* m_snapping = nullptr;
|
||||
bool m_isSnappingHorizontal = false;
|
||||
bool m_isSnappingVertical = false;
|
||||
|
||||
QMap<int, QSvgRenderer*> m_toolIcons;
|
||||
QSvgRenderer* m_cursorRenderer = nullptr;
|
||||
|
||||
QPoint lastPos;
|
||||
};
|
||||
|
||||
#endif // VIEWPORTWIDGET_H
|
||||
|
||||
33
src/shaders/lit.frag
Normal file
33
src/shaders/lit.frag
Normal file
@@ -0,0 +1,33 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
in vec3 FragPos;
|
||||
in vec3 Normal;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 viewPos = vec3(0.0, 0.0, 0.0); // View position is origin in view space
|
||||
vec3 lightPos = vec3(0.0, 10.0, 5.0); // Light position in view space
|
||||
vec3 lightColor = vec3(1.0, 1.0, 1.0);
|
||||
vec3 objectColor = vec3(0.5, 0.5, 1.0);
|
||||
|
||||
// Ambient
|
||||
float ambientStrength = 0.3;
|
||||
vec3 ambient = ambientStrength * lightColor;
|
||||
|
||||
// Diffuse
|
||||
vec3 norm = normalize(Normal);
|
||||
vec3 lightDir = normalize(lightPos - FragPos);
|
||||
float diff = max(dot(norm, lightDir), 0.0);
|
||||
vec3 diffuse = diff * lightColor;
|
||||
|
||||
// Specular
|
||||
float specularStrength = 0.5;
|
||||
vec3 viewDir = normalize(viewPos - FragPos);
|
||||
vec3 reflectDir = reflect(-lightDir, norm);
|
||||
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
|
||||
vec3 specular = specularStrength * spec * lightColor;
|
||||
|
||||
vec3 result = (ambient + diffuse + specular) * objectColor;
|
||||
FragColor = vec4(result, 1.0);
|
||||
}
|
||||
17
src/shaders/lit.vert
Normal file
17
src/shaders/lit.vert
Normal file
@@ -0,0 +1,17 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec3 aNormal;
|
||||
|
||||
out vec3 FragPos;
|
||||
out vec3 Normal;
|
||||
|
||||
uniform mat4 modelViewMatrix;
|
||||
uniform mat4 projectionMatrix;
|
||||
uniform mat3 normalMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragPos = vec3(modelViewMatrix * vec4(aPos, 1.0));
|
||||
Normal = normalMatrix * aNormal;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(aPos, 1.0);
|
||||
}
|
||||
@@ -5,8 +5,10 @@ out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D texture_diffuse1;
|
||||
uniform float opacity;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = texture(texture_diffuse1, TexCoord);
|
||||
vec4 texColor = texture(texture_diffuse1, TexCoord);
|
||||
FragColor = vec4(texColor.rgb, texColor.a * opacity);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user