Commit b9bf97b9 authored by WINTZ Julien's avatar WINTZ Julien

Adding overlay rope PoC together with an example application.

To be integrated on top on the view layout manager, identifying
underlying target to make the view connections for synchronization.

So far the example provides a collider to test the physics engine but
it has to be changed to control the end of the rope.

Colliders will must likely not be used in the case of
dtkVisualizationViewer.

So far many attributes can be tweaked, however default values have
been empirically chosen to decrease CPU usage and increase visual
feedback.

These are:

- Rope vertices count
- Rope vertices interaval (or distance in pixels)
- Integration factor (for computing gravity and velocity)

The rope's implementation topology is piecewise linear, which handy
when it comes to adjust joints attributes (velocity, mass ...).

No parametric representation is whatsoever needed, besides, the number
of vertices can be dramatically improved without performance
loss. Beware to decrease vertices interval in this case or the rope
length will grow significantly.
parent 2a618e20
......@@ -96,6 +96,7 @@ include_directories(${CMAKE_SOURCE_DIR}/src/dtkVisualizationWidgets)
add_subdirectory(src)
add_subdirectory(app)
add_subdirectory(exp)
## ###################################################################
## Exporting
......
......@@ -11,19 +11,19 @@
<key>CFBundleIconFile</key>
<string>@PROJECT_NAME@</string>
<key>CFBundleIdentifier</key>
<string>fr.inria.sw2d</string>
<string>fr.inria.@PROJECT_NAME@</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>@sw2d_VERSION@</string>
<string>@dtkVisualization_VERSION@</string>
<key>CFBundleName</key>
<string>@PROJECT_NAME@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>@sw2d_VERSION@</string>
<string>@dtkVisualization_VERSION@</string>
<key>CFBundleVersion</key>
<string>@sw2d_VERSION@</string>
<string>@dtkVisualization_VERSION@</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>NSHumanReadableCopyright</key>
......
## Version: $Id$
##
######################################################################
##
### Commentary:
##
######################################################################
##
### Change Log:
##
######################################################################
##
### Code:
add_subdirectory(dtkVisualizationOverlayRope)
######################################################################
### CMakeLists.txt ends here
## Version: $Id$
##
######################################################################
##
### Commentary:
##
######################################################################
##
### Change Log:
##
######################################################################
##
### Code:
project(dtkVisualizationOverlayRope)
## #################################################################
## Sources
## #################################################################
set(${PROJECT_NAME}_SOURCES main.cpp)
## #################################################################
## Build rules
## #################################################################
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${${PROJECT_NAME}_SOURCES})
qt5_use_modules(${PROJECT_NAME} Core)
qt5_use_modules(${PROJECT_NAME} Gui)
qt5_use_modules(${PROJECT_NAME} Widgets)
target_link_libraries(${PROJECT_NAME} dtkVisualizationWidgets)
## ###################################################################
## Bundle setup
## ###################################################################
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.plist.in ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.plist)
if(APPLE)
set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.plist)
endif(APPLE)
######################################################################
### CMakeLists.txt ends here
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>@PROJECT_NAME@</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string>@PROJECT_NAME@</string>
<key>CFBundleIdentifier</key>
<string>fr.inria.@PROJECT_NAME@</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>@dtkVisualization_VERSION@</string>
<key>CFBundleName</key>
<string>@PROJECT_NAME@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>@dtkVisualization_VERSION@</string>
<key>CFBundleVersion</key>
<string>@dtkVisualization_VERSION@</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Inria</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>
// Version: $Id$
//
//
// Commentary:
//
//
// Change Log:
//
//
// Code:
#include <dtkVisualizationWidgets/dtkVisualizationOverlayRope>
#include <QtWidgets>
int main(int argc, char **argv)
{
QApplication application(argc, argv);
application.setApplicationName("dtkVisualizationOverlayRope");
application.setOrganizationName("inria");
application.setOrganizationDomain("fr");
dtkVisualizationOverlayRope *window = new dtkVisualizationOverlayRope;
window->setWindowTitle("dtkVisualizationOverlayRope");
window->resize(800, 600);
window->show();
window->raise();
return application.exec();
}
//
// main.cpp ends here
......@@ -37,6 +37,8 @@ set(${PROJECT_NAME}_HEADERS
dtkVisualizationHUDItem.h
dtkVisualizationOverlay
dtkVisualizationOverlay.h
dtkVisualizationOverlayRope
dtkVisualizationOverlayRope.h
dtkVisualizationSettings
dtkVisualizationSettings.h
dtkVisualizationWidget
......@@ -52,6 +54,7 @@ set(${PROJECT_NAME}_SOURCES
dtkVisualizationHUD.cpp
dtkVisualizationHUDItem.cpp
dtkVisualizationOverlay.cpp
dtkVisualizationOverlayRope.cpp
dtkVisualizationSettings.cpp
dtkVisualizationWidget.cpp)
......
#include "dtkVisualizationOverlayRope.h"
\ No newline at end of file
// Version: $Id$
//
//
// Commentary:
//
//
// Change Log:
//
//
// Code:
#include "dtkVisualizationOverlayRope.h"
#include <QtGui>
#include <QtMath>
// ///////////////////////////////////////////////////////////////////
// dtkVisualizationRopeOverlayVertex
// ///////////////////////////////////////////////////////////////////
class dtkVisualizationRopeOverlayVertex
{
public:
QVector2D velocity;
QVector2D position;
QVector2D oldPosition;
public:
qreal invMass;
};
// ///////////////////////////////////////////////////////////////////
// dtkVisualizationOverlayRopePrivate
// ///////////////////////////////////////////////////////////////////
class dtkVisualizationOverlayRopePrivate
{
public:
void solve(void);
void integrate(qreal dt);
void velocityFixup(qreal inv_dt);
void collide(void);
void solveRope(qreal dt);
void buildRope(void);
void renderRope(QPainter *painter);
void renderCollider(QPainter *painter);
public:
QVector<dtkVisualizationRopeOverlayVertex> vertices;
public:
qreal collision_radius = 40.0;
public:
quint32 vertices_distance = 10;
quint32 vertices_count = 30;
public:
qreal integration_factor = (1.0 / 7.5);
public:
bool collider = false;
public:
qreal xf;
qreal yf;
};
// ///////////////////////////////////////////////////////////////////
// dtkVisualizationOverlayRopePrivate
// ///////////////////////////////////////////////////////////////////
void dtkVisualizationOverlayRopePrivate::solve(void)
{
if(!vertices.count())
return;
for (int i = 0; i < vertices.count() - 1; i++)
{
dtkVisualizationRopeOverlayVertex& a = vertices[i];
dtkVisualizationRopeOverlayVertex& b = vertices[i + 1];
QVector2D v = b.position - a.position;
qreal length = v.length();
if (length != qreal(0.0))
{
qreal error = vertices_distance / length - qreal(1.0);
QVector2D correction = v * error;
if (i != 0)
{
qreal invMass = a.invMass + b.invMass;
a.position -= correction * (a.invMass / invMass);
b.position += correction * (b.invMass / invMass);
}
else
{
b.position += correction;
}
}
}
}
void dtkVisualizationOverlayRopePrivate::integrate(qreal dt)
{
for (int i = 1; i < vertices.size(); ++i)
{
dtkVisualizationRopeOverlayVertex& a = vertices[i];
a.velocity += QVector2D(0.0, 9.8) * dt;
a.position += a.velocity * dt;
}
}
void dtkVisualizationOverlayRopePrivate::velocityFixup(qreal inv_dt)
{
for (int i = 1; i < vertices.size(); ++i)
{
dtkVisualizationRopeOverlayVertex& a = vertices[i];
a.velocity = (a.position - a.oldPosition) * inv_dt;
a.oldPosition = a.position;
}
}
void dtkVisualizationOverlayRopePrivate::collide(void)
{
if (collider)
{
QVector2D mouse(xf, yf);
for (int i = 1; i < vertices.size(); ++i)
{
dtkVisualizationRopeOverlayVertex& a = vertices[i];
qreal d = QVector2D::dotProduct(a.position - mouse, a.position - mouse);
if (d < collision_radius * collision_radius)
{
QVector2D dir = a.position - mouse;
dir.normalize();
dir *= collision_radius - qSqrt(d);
a.position += dir;
}
}
}
}
void dtkVisualizationOverlayRopePrivate::solveRope(qreal dt)
{
this->integrate(dt);
this->collide();
this->solve();
this->velocityFixup(qreal(1.0) / dt);
}
void dtkVisualizationOverlayRopePrivate::buildRope(void)
{
vertices.clear();
qreal x = 400.0;
qreal y = 50.0;
for (quint32 i = 0; i < vertices_count; ++i)
{
dtkVisualizationRopeOverlayVertex v;
v.position.setX(x + qreal(i) * vertices_distance);
v.position.setY(y);
v.oldPosition = v.position;
v.velocity.setX(0.0);
v.velocity.setY(0.0);
v.invMass = qreal(1.0) / qreal(vertices_count * 3 - i * 2);
vertices.push_back(v);
}
}
void dtkVisualizationOverlayRopePrivate::renderRope(QPainter *painter)
{
if(!vertices.count())
return;
painter->setPen(QPen(Qt::gray, 2));
int i;
for (i = 0; i < vertices.size() - 1; ++i)
{
dtkVisualizationRopeOverlayVertex& a = vertices[i];
dtkVisualizationRopeOverlayVertex& b = vertices[i + 1];
painter->drawLine(a.position.toPoint(), b.position.toPoint());
}
painter->setPen(QPen(Qt::gray, 4));
painter->setBrush(Qt::white);
painter->drawRoundedRect(vertices[0].position.x() - 10, vertices[0].position.y() - 10, 20, 20, 10, 10);
i--;
painter->setPen(QPen(Qt::gray, 4));
painter->setBrush(Qt::white);
painter->drawRoundedRect(vertices[i].position.x() - 10, vertices[i].position.y() - 10, 20, 20, 10, 10);
}
void dtkVisualizationOverlayRopePrivate::renderCollider(QPainter *painter)
{
QPointF start;
if (collider)
{
QPainterPath path;
const quint32 kSegments = 60;
QVector2D pos(xf, yf);
qreal theta = 0;
qreal inc = M_PI * qreal(2.0) / (qreal)kSegments;
for(quint32 i = 0; i < kSegments; ++i)
{
theta += inc;
QVector2D p(std::cos(theta), std::sin(theta));
p *= collision_radius;
p += pos;
if (start.isNull()) {
start = p.toPointF();
path.moveTo(start);
} else {
path.lineTo(p.toPointF());
}
}
path.lineTo(start);
painter->setPen(QPen(Qt::darkRed, 2));
painter->drawPath(path);
}
}
// ///////////////////////////////////////////////////////////////////
// dtkVisualizationOverlayRope
// ///////////////////////////////////////////////////////////////////
dtkVisualizationOverlayRope::dtkVisualizationOverlayRope(QWidget *parent) : QFrame(parent)
{
d = new dtkVisualizationOverlayRopePrivate;
QTimer *timer = new QTimer(this);
timer->setInterval(1000/60);
timer->start();
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
this->setAttribute(Qt::WA_DeleteOnClose);
}
dtkVisualizationOverlayRope::~dtkVisualizationOverlayRope(void)
{
delete d;
}
void dtkVisualizationOverlayRope::setVerticesCount(quint32 count)
{
d->vertices_count = count;
}
void dtkVisualizationOverlayRope::setVerticesInterval(quint32 distance)
{
d->vertices_distance = distance;
}
void dtkVisualizationOverlayRope::setIntegrationFactor(qreal factor)
{
d->integration_factor = factor;
}
void dtkVisualizationOverlayRope::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
d->solveRope(d->integration_factor);
d->renderRope(&painter);
d->renderCollider(&painter);
QRect bottom_rect = QRect(event->rect().bottomLeft().x(), event->rect().bottomLeft().y() - 50, event->rect().width(), 51);
painter.setPen(QColor("#cc2d2e2e"));
painter.setBrush(QColor("#cc2d2e2e"));
painter.drawRect(bottom_rect);
painter.setPen(QColor("#bebfbf"));
painter.drawText(bottom_rect, "Hit Qt::Key_R to (re)build the rope, Qt::LeftButton to collide", QTextOption(Qt::AlignCenter));
QFrame::paintEvent(event);
}
void dtkVisualizationOverlayRope::keyPressEvent(QKeyEvent *event)
{
switch(event->key())
{
case Qt::Key_R:
event->accept();
d->buildRope();
break;
default:
event->ignore();
}
QFrame::keyPressEvent(event);
}
void dtkVisualizationOverlayRope::mousePressEvent(QMouseEvent *event)
{
d->xf = event->pos().x();
d->yf = event->pos().y();
switch (event->button())
{
case Qt::LeftButton:
event->accept();
d->collider = true;
break;
default:
event->ignore();
break;
}
QFrame::mousePressEvent(event);
}
void dtkVisualizationOverlayRope::mouseMoveEvent(QMouseEvent *event)
{
event->accept();
d->xf = event->pos().x();
d->yf = event->pos().y();
QFrame::mouseMoveEvent(event);
}
void dtkVisualizationOverlayRope::mouseReleaseEvent(QMouseEvent *event)
{
d->xf = event->pos().x();
d->yf = event->pos().y();
switch(event->button())
{
case Qt::LeftButton:
event->accept();
d->collider = false;
break;
default:
event->ignore();
break;
}
QFrame::mouseReleaseEvent(event);
}
//
// dtkVisualizationOverlayRope.cpp ends here
// Version: $Id$
//
//
// Commentary:
//
//
// Change Log:
//
//
// Code:
#pragma once
#include <dtkVisualizationWidgetsExport.h>
#include <QtCore>
#include <QtWidgets>
class DTKVISUALIZATIONWIDGETS_EXPORT dtkVisualizationOverlayRope : public QFrame
{
Q_OBJECT
public:
dtkVisualizationOverlayRope(QWidget *parent = Q_NULLPTR);
~dtkVisualizationOverlayRope(void);
public:
void setVerticesCount(quint32 count);
void setVerticesInterval(quint32 distance);
public:
void setIntegrationFactor(qreal factor);
protected:
void paintEvent(QPaintEvent *event);
protected:
void keyPressEvent(QKeyEvent *event);
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
class dtkVisualizationOverlayRopePrivate *d;
};
//
// dtkVisualizationOverlayRope.h ends here
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment