File indexing completed on 2024-04-28 07:39:38
0001 /*. 0002 SPDX-FileCopyrightText: 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "polygongraphics.h" 0008 0009 #include <stepcore/rigidbody.h> 0010 0011 #include <stepcore/constants.h> 0012 #include <stepcore/types.h> 0013 #include "worldmodel.h" 0014 #include "worldscene.h" 0015 #include "worldfactory.h" 0016 0017 #include <QEvent> 0018 #include <QGraphicsSceneMouseEvent> 0019 #include <QItemSelectionModel> 0020 #include <QKeyEvent> 0021 #include <QPainter> 0022 0023 #include <KLocalizedString> 0024 0025 RigidBodyGraphicsItem::RigidBodyGraphicsItem(StepCore::Item* item, WorldModel* worldModel) 0026 : StepGraphicsItem(item, worldModel) 0027 { 0028 Q_ASSERT(dynamic_cast<StepCore::RigidBody*>(_item) != nullptr); 0029 setFlag(QGraphicsItem::ItemIsSelectable); 0030 setFlag(QGraphicsItem::ItemIsMovable); 0031 setAcceptHoverEvents(true); 0032 _velocityHandler = new ArrowHandlerGraphicsItem(item, worldModel, this, 0033 _item->metaObject()->property(QStringLiteral("velocity"))); 0034 _velocityHandler->setVisible(false); 0035 0036 _angularVelocityHandler = new CircularArrowHandlerGraphicsItem(item, worldModel, this, 0037 ANGULAR_VELOCITY_RADIUS, _item->metaObject()->property(QStringLiteral("angularVelocity"))); 0038 _angleHandler = new CircularArrowHandlerGraphicsItem(item, worldModel, this, 0039 ANGLE_HANDLER_RADIUS, _item->metaObject()->property(QStringLiteral("angle"))); 0040 _angularVelocityHandler->setVisible(false); 0041 _angleHandler->setVisible(false); 0042 setOnHoverHandlerEnabled(true); 0043 //scene()->addItem(_velocityHandler); 0044 } 0045 0046 inline StepCore::RigidBody* RigidBodyGraphicsItem::rigidBody() const 0047 { 0048 return static_cast<StepCore::RigidBody*>(_item); 0049 } 0050 0051 QPainterPath RigidBodyGraphicsItem::shape() const 0052 { 0053 return _painterPath; 0054 } 0055 0056 void RigidBodyGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, 0057 QWidget* /*widget*/) 0058 { 0059 //int renderHints = painter->renderHints(); 0060 painter->setRenderHint(QPainter::Antialiasing, true); 0061 0062 QColor color = QColor::fromRgba(rigidBody()->color()); 0063 if (isItemHighlighted()) { 0064 color = highlightColor(color); 0065 } 0066 painter->setPen(Qt::NoPen); 0067 painter->setBrush(QBrush(color)); 0068 0069 painter->drawPath(_painterPath); 0070 0071 if (!_markPath.isEmpty()) { 0072 QPen pen(color); 0073 pen.setWidth(0); 0074 painter->setPen(pen); 0075 painter->setBrush(QColor(Qt::white)); 0076 painter->drawPath(_markPath); 0077 } 0078 0079 if(_isSelected) { 0080 double s = currentViewScale(); 0081 QRectF rect = _painterPath.boundingRect(); 0082 rect.adjust(-SELECTION_MARGIN/s, -SELECTION_MARGIN/s, SELECTION_MARGIN/s, SELECTION_MARGIN/s); 0083 painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine)); 0084 painter->setBrush(QBrush()); 0085 painter->drawRect(rect); 0086 } 0087 0088 if(_isSelected || _isMouseOverItem) { 0089 //painter->setRenderHint(QPainter::Antialiasing, renderHints & QPainter::Antialiasing); 0090 painter->setPen(QPen(Qt::blue, 0)); 0091 drawArrow(painter, rigidBody()->velocity()); 0092 drawCircularArrow(painter, rigidBody()->angularVelocity(), ANGULAR_VELOCITY_RADIUS); 0093 painter->setPen(QPen(Qt::red, 0)); 0094 drawArrow(painter, rigidBody()->acceleration()); 0095 drawCircularArrow(painter, rigidBody()->angularAcceleration(), ANGULAR_ACCELERATION_RADIUS); 0096 } 0097 } 0098 0099 void RigidBodyGraphicsItem::viewScaleChanged() 0100 { 0101 /// XXX: optimize it ! 0102 prepareGeometryChange(); 0103 0104 const StepCore::Vector2d& v = rigidBody()->velocity(); 0105 const StepCore::Vector2d a = rigidBody()->acceleration(); 0106 double s = currentViewScale(); 0107 0108 double avr = (ANGULAR_VELOCITY_RADIUS+CIRCULAR_ARROW_STROKE)/s; 0109 double aar = (ANGULAR_ACCELERATION_RADIUS+CIRCULAR_ARROW_STROKE)/s; 0110 _boundingRect = _painterPath.boundingRect() 0111 | QRectF(0, 0, v[0], v[1]).normalized() 0112 | QRectF(0, 0, a[0], a[1]).normalized() 0113 | QRectF(-avr, -avr, 2*avr, 2*avr) 0114 | QRectF(-aar, -aar, 2*aar, 2*aar); 0115 double adjust = (ARROW_STROKE+SELECTION_MARGIN)/s; 0116 _boundingRect.adjust(-adjust,-adjust, adjust, adjust); 0117 } 0118 0119 void RigidBodyGraphicsItem::worldDataChanged(bool dynamicOnly) 0120 { 0121 Q_UNUSED(dynamicOnly) 0122 // XXX: TODO do not redraw everything each time 0123 setPos(vectorToPoint(rigidBody()->position())); 0124 viewScaleChanged(); 0125 update(); 0126 } 0127 0128 void RigidBodyGraphicsItem::stateChanged() 0129 { 0130 if(_isSelected) { 0131 _velocityHandler->setVisible(true); 0132 _angularVelocityHandler->setVisible(true); 0133 _angleHandler->setVisible(true); 0134 } else { 0135 _velocityHandler->setVisible(false); 0136 _angularVelocityHandler->setVisible(false); 0137 _angleHandler->setVisible(false); 0138 } 0139 0140 viewScaleChanged(); 0141 update(); 0142 } 0143 0144 ///////////////////////////////////////////////////////////////////////////////////////// 0145 0146 void DiskCreator::start() 0147 { 0148 showMessage(MessageFrame::Information, 0149 i18n("Press left mouse button to position a center of a %1", classNameTr())); 0150 } 0151 0152 bool DiskCreator::sceneEvent(QEvent* event) 0153 { 0154 QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event); 0155 0156 if(event->type() == QEvent::GraphicsSceneMousePress && mouseEvent->button() == Qt::LeftButton) { 0157 QPointF pos = mouseEvent->scenePos(); 0158 QVariant vpos = QVariant::fromValue(StepGraphicsItem::pointToVector(pos)); 0159 0160 _worldModel->simulationPause(); 0161 _worldModel->beginMacro(i18n("Create %1", _worldModel->newItemName(_className))); 0162 _item = _worldModel->createItem(_className); Q_ASSERT(_item != nullptr); 0163 _worldModel->setProperty(_item, QStringLiteral("position"), vpos); 0164 _worldModel->setProperty(_item, QStringLiteral("radius"), QVariant::fromValue(0.0)); 0165 _worldModel->addItem(_item); 0166 _worldModel->selectionModel()->setCurrentIndex(_worldModel->objectIndex(_item), 0167 QItemSelectionModel::ClearAndSelect); 0168 0169 showMessage(MessageFrame::Information, 0170 i18n("Move mouse and release left mouse button to define a radius of the %1", classNameTr())); 0171 0172 return true; 0173 } else if(event->type() == QEvent::GraphicsSceneMouseMove && 0174 mouseEvent->buttons() & Qt::LeftButton) { 0175 0176 _worldModel->simulationPause(); 0177 StepCore::Vector2d pos = StepGraphicsItem::pointToVector(mouseEvent->scenePos()); 0178 double radius = (pos - static_cast<StepCore::Disk*>(_item)->position()).norm(); 0179 _worldModel->setProperty(_item, QStringLiteral("radius"), QVariant::fromValue(radius)); 0180 return true; 0181 0182 } else if(event->type() == QEvent::GraphicsSceneMouseRelease && 0183 mouseEvent->button() == Qt::LeftButton) { 0184 0185 _worldModel->simulationPause(); 0186 StepCore::Vector2d pos = StepGraphicsItem::pointToVector(mouseEvent->scenePos()); 0187 StepCore::Disk* disk = static_cast<StepCore::Disk*>(_item); 0188 double radius = (pos - disk->position()).norm(); 0189 if(radius == 0) radius = 0.5; 0190 double inertia = disk->mass() * radius*radius/2.0; 0191 _worldModel->setProperty(_item, QStringLiteral("radius"), QVariant::fromValue(radius)); 0192 _worldModel->setProperty(_item, QStringLiteral("inertia"), QVariant::fromValue(inertia)); 0193 _worldModel->endMacro(); 0194 0195 showMessage(MessageFrame::Information, 0196 i18n("%1 named '%2' created", classNameTr(), _item->name()), 0197 MessageFrame::CloseButton | MessageFrame::CloseTimer); 0198 0199 setFinished(); 0200 return true; 0201 } 0202 0203 return false; 0204 } 0205 0206 inline StepCore::Disk* DiskVertexHandlerGraphicsItem::disk() const 0207 { 0208 return static_cast<StepCore::Disk*>(_item); 0209 } 0210 0211 StepCore::Vector2d DiskVertexHandlerGraphicsItem::value() 0212 { 0213 return scorners[_vertexNum]*disk()->radius(); 0214 } 0215 0216 void DiskVertexHandlerGraphicsItem::setValue(const StepCore::Vector2d& value) 0217 { 0218 _worldModel->setProperty(_item, QStringLiteral("radius"), value.norm()); 0219 } 0220 0221 DiskGraphicsItem::DiskGraphicsItem(StepCore::Item* item, WorldModel* worldModel) 0222 : RigidBodyGraphicsItem(item, worldModel) 0223 { 0224 Q_ASSERT(dynamic_cast<StepCore::Disk*>(_item) != nullptr); 0225 } 0226 0227 inline StepCore::Disk* DiskGraphicsItem::disk() const 0228 { 0229 return static_cast<StepCore::Disk*>(_item); 0230 } 0231 0232 void DiskGraphicsItem::viewScaleChanged() 0233 { 0234 _painterPath = QPainterPath(); 0235 _painterPath.setFillRule(Qt::WindingFill); 0236 0237 _markPath = QPainterPath(); 0238 _painterPath.setFillRule(Qt::WindingFill); 0239 0240 double s = currentViewScale(); 0241 double radius = disk()->radius(); 0242 if (radius > 1/s) { 0243 _painterPath.addEllipse(-radius, -radius, 2*radius, 2*radius); 0244 0245 _markPath.moveTo(0, 0); 0246 _markPath.arcTo(QRectF(-radius, -radius, 2 * radius, 2 * radius), 0247 -15.0, 30.0); 0248 _markPath.closeSubpath(); 0249 } else { 0250 _painterPath.addEllipse(-1/s, -1/s, 2/s, 2/s); 0251 // Don't need a marker when the disk is too small to see in detail. 0252 } 0253 0254 _markPath = QTransform().rotate(disk()->angle() * 180 / StepCore::Constants::Pi).map(_markPath); 0255 RigidBodyGraphicsItem::viewScaleChanged(); 0256 } 0257 0258 OnHoverHandlerGraphicsItem* DiskGraphicsItem::createOnHoverHandler(const QPointF& pos) 0259 { 0260 StepCore::Vector2d l = (pointToVector(pos) - disk()->position())/disk()->radius(); 0261 double s = currentViewScale(); 0262 int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE 0263 /s/s/disk()->radius()/disk()->radius(); 0264 for(unsigned int i=0; i<4; ++i) { 0265 double dist2 = (l - DiskVertexHandlerGraphicsItem::scorners[i]).squaredNorm(); 0266 if(dist2 < minDist2) { num = i; minDist2 = dist2; } 0267 } 0268 0269 if(_onHoverHandler && _onHoverHandler->vertexNum() == num) 0270 return _onHoverHandler; 0271 0272 if(num >= 0) 0273 return new DiskVertexHandlerGraphicsItem(_item, _worldModel, this, num); 0274 0275 return nullptr; 0276 } 0277 0278 ///////////////////////////////////////////////////////////////////////////////////////// 0279 0280 void BoxCreator::start() 0281 { 0282 showMessage(MessageFrame::Information, 0283 i18n("Press left mouse button to position\ntop left corner of a %1", classNameTr())); 0284 } 0285 0286 bool BoxCreator::sceneEvent(QEvent* event) 0287 { 0288 QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event); 0289 0290 if(event->type() == QEvent::GraphicsSceneMousePress && mouseEvent->button() == Qt::LeftButton) { 0291 QPointF pos = mouseEvent->scenePos(); 0292 QVariant vpos = QVariant::fromValue(StepGraphicsItem::pointToVector(pos)); 0293 0294 _worldModel->simulationPause(); 0295 _worldModel->beginMacro(i18n("Create %1", _worldModel->newItemName(_className))); 0296 _item = _worldModel->createItem(_className); Q_ASSERT(_item != nullptr); 0297 _worldModel->setProperty(_item, QStringLiteral("position"), vpos); 0298 _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue(StepCore::Vector2d::Zero().eval())); 0299 _worldModel->addItem(_item); 0300 _worldModel->selectionModel()->setCurrentIndex(_worldModel->objectIndex(_item), 0301 QItemSelectionModel::ClearAndSelect); 0302 _topLeft = StepGraphicsItem::pointToVector(pos); 0303 0304 showMessage(MessageFrame::Information, 0305 i18n("Move mouse and release left mouse button to position\nbottom right corner of the %1", classNameTr())); 0306 0307 return true; 0308 } else if(event->type() == QEvent::GraphicsSceneMouseMove && 0309 mouseEvent->buttons() & Qt::LeftButton) { 0310 0311 _worldModel->simulationPause(); 0312 StepCore::Vector2d pos = StepGraphicsItem::pointToVector(mouseEvent->scenePos()); 0313 StepCore::Vector2d position = (_topLeft + pos) / 2.0; 0314 StepCore::Vector2d size = _topLeft - pos; 0315 _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(position)); 0316 _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue(size)); 0317 return true; 0318 0319 } else if(event->type() == QEvent::GraphicsSceneMouseRelease && 0320 mouseEvent->button() == Qt::LeftButton) { 0321 0322 _worldModel->simulationPause(); 0323 StepCore::Vector2d pos = StepGraphicsItem::pointToVector(mouseEvent->scenePos()); 0324 StepCore::Box* box = static_cast<StepCore::Box*>(_item); 0325 StepCore::Vector2d position = (_topLeft + pos) / 2.0; 0326 StepCore::Vector2d size = _topLeft - pos; 0327 if(size[0] == 0 && size[1] == 0) { size[0] = size[1] = 1; } 0328 double inertia = box->mass() * (size[0]*size[0] + size[1]*size[1]) / 12.0; 0329 _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(position)); 0330 _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue(size)); 0331 _worldModel->setProperty(_item, QStringLiteral("inertia"), QVariant::fromValue(inertia)); 0332 _worldModel->endMacro(); 0333 0334 showMessage(MessageFrame::Information, 0335 i18n("%1 named '%2' created", classNameTr(), _item->name()), 0336 MessageFrame::CloseButton | MessageFrame::CloseTimer); 0337 0338 setFinished(); 0339 return true; 0340 } 0341 0342 return false; 0343 } 0344 0345 ///////////////////////////////////////////////////////////////////////////////////////// 0346 0347 void PolygonCreator::fixCenterOfMass() 0348 { 0349 StepCore::Vector2dList v = static_cast<StepCore::Polygon*>(_item)->vertexes(); 0350 StepCore::Vector2d position = static_cast<StepCore::Polygon*>(_item)->position(); 0351 0352 StepCore::Vector2d center(0, 0); 0353 double area_i, area = 0; 0354 unsigned int i; 0355 0356 if(v.size() == 1) center = v[0]; 0357 else { 0358 if(v.size() > 2) { 0359 for(i=0; i+1<v.size(); ++i) { 0360 area_i = (v[i][0]*v[i+1][1] - v[i][1]*v[i+1][0]) / 2; 0361 center += (v[i] + v[i+1]) * (area_i/3); 0362 area += area_i; 0363 } 0364 area_i = (v[i][0]*v[0][1] - v[i][1]*v[0][0]) / 2; 0365 center += (v[i] + v[0]) * (area_i/3); 0366 area += area_i; 0367 } 0368 0369 if(area == 0) { // all vertexes on one line 0370 center.setZero(); 0371 for(i=0; i+1<v.size(); ++i) { 0372 area_i = (v[i+1] - v[i]).norm(); 0373 center += (v[i] + v[i+1]) * (area_i/2); 0374 area += area_i; 0375 } 0376 } 0377 0378 if(area == 0) center = v[0]; // all vertexes are at one point 0379 else center /= area; 0380 } 0381 0382 for(i=0; i<v.size(); ++i) v[i] -= center; 0383 _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue((position + center).eval())); 0384 _worldModel->setProperty(_item, QStringLiteral("vertexes"), QVariant::fromValue(v)); 0385 } 0386 0387 void PolygonCreator::fixInertia() 0388 { 0389 // XXX: unite it with fixCenterOfMass 0390 const StepCore::Vector2dList& v = static_cast<StepCore::Polygon*>(_item)->vertexes(); 0391 double mass = static_cast<StepCore::Polygon*>(_item)->mass(); 0392 double area_i, area = 0; 0393 double inertia = 0; 0394 unsigned int i; 0395 0396 if(v.size() > 2) { 0397 if(v.size() > 2) { 0398 for(i=0; i+1<v.size(); ++i) { 0399 area_i = (v[i][0]*v[i+1][1] - v[i][1]*v[i+1][0]) / 2; 0400 inertia += (v[i].squaredNorm() + v[i+1].dot(v[i]) + v[i+1].squaredNorm())*(area_i/6); 0401 area += area_i; 0402 } 0403 area_i = (v[i][0]*v[0][1] - v[i][1]*v[0][0]) / 2; 0404 inertia += (v[i].squaredNorm() + v[0].dot(v[i]) + v[0].squaredNorm())*(area_i/6); 0405 area += area_i; 0406 } 0407 } 0408 0409 if(area == 0) { // all vertexes on one line 0410 inertia = 0; 0411 for(i=0; i+1<v.size(); ++i) { 0412 area_i = (v[i+1] - v[i]).norm(); 0413 inertia += area_i*area_i*area_i / 12 + area_i * (v[i]+v[i+1]).squaredNorm() / 4; 0414 area += area_i; 0415 } 0416 0417 if(area == 0) inertia = 0; // all vertexes are at one point 0418 else inertia /= area; 0419 } 0420 0421 inertia = fabs(inertia * mass); // 1 = 1m XXX XXX XXX 0422 _worldModel->setProperty(_item, QStringLiteral("inertia"), QVariant::fromValue(inertia)); 0423 } 0424 0425 void PolygonCreator::start() 0426 { 0427 showMessage(MessageFrame::Information, 0428 i18n("Click on the scene to create a first vertex of %1", classNameTr())); 0429 } 0430 0431 bool PolygonCreator::sceneEvent(QEvent* event) 0432 { 0433 QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event); 0434 0435 if(!_item && event->type() == QEvent::GraphicsSceneMousePress && mouseEvent->button() == Qt::LeftButton) { 0436 QPointF pos = mouseEvent->scenePos(); 0437 QVariant vpos = QVariant::fromValue(StepGraphicsItem::pointToVector(pos)); 0438 0439 _worldModel->simulationPause(); 0440 _worldModel->beginMacro(i18n("Create %1", _worldModel->newItemName(_className))); 0441 _item = _worldModel->createItem(_className); Q_ASSERT(_item != nullptr); 0442 _worldModel->setProperty(_item, QStringLiteral("position"), vpos); 0443 _worldModel->setProperty(_item, QStringLiteral("vertexes"), QStringLiteral("(0,0)")); 0444 _worldModel->addItem(_item); 0445 _worldModel->selectionModel()->setCurrentIndex(_worldModel->objectIndex(_item), 0446 QItemSelectionModel::ClearAndSelect); 0447 0448 return true; 0449 0450 } else if(_item && event->type() == QEvent::GraphicsSceneMousePress) { 0451 return true; 0452 0453 } else if(_item && (event->type() == QEvent::GraphicsSceneMouseMove || 0454 (event->type() == QEvent::GraphicsSceneMouseRelease && 0455 mouseEvent->button() == Qt::LeftButton))) { 0456 0457 QPointF pos = mouseEvent->scenePos(); 0458 StepCore::Vector2d v = StepGraphicsItem::pointToVector(pos); 0459 0460 _worldModel->simulationPause(); 0461 // XXX: don't use strings ! 0462 QString vertexes = _item->metaObject()->property(QStringLiteral("vertexes"))->readString(_item).section(',', 0, -3); 0463 if(vertexes.isEmpty()) { 0464 _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(v)); 0465 vertexes = QStringLiteral("(0,0)"); v.setZero(); 0466 } else { 0467 v -= static_cast<StepCore::Polygon*>(_item)->position(); 0468 vertexes += QStringLiteral(",(%1,%2)").arg(v[0]).arg(v[1]); 0469 _worldModel->setProperty(_item, QStringLiteral("vertexes"), vertexes); 0470 } 0471 0472 if(event->type() == QEvent::GraphicsSceneMouseRelease) { 0473 vertexes += QStringLiteral(",(%1,%2)").arg(v[0]).arg(v[1]); 0474 _worldModel->setProperty(_item, QStringLiteral("vertexes"), vertexes); 0475 showMessage(MessageFrame::Information, 0476 i18n("Click on the scene to add new vertex or press Enter to finish")); 0477 } 0478 0479 //fixCenterOfMass(); 0480 //fixInertia(); 0481 return true; 0482 0483 } else if(_item && event->type() == QEvent::KeyPress && 0484 static_cast<QKeyEvent*>(event)->key() == Qt::Key_Return) { 0485 fixCenterOfMass(); 0486 fixInertia(); 0487 _worldModel->endMacro(); 0488 0489 showMessage(MessageFrame::Information, 0490 i18n("%1 named '%2' created", classNameTr(), _item->name()), 0491 MessageFrame::CloseButton | MessageFrame::CloseTimer); 0492 0493 setFinished(); 0494 return true; 0495 } 0496 return false; 0497 } 0498 0499 ///////////////////////////////////////////////////////////////////////////////////////// 0500 0501 BasePolygonGraphicsItem::BasePolygonGraphicsItem(StepCore::Item* item, WorldModel* worldModel) 0502 : RigidBodyGraphicsItem(item, worldModel) 0503 { 0504 Q_ASSERT(dynamic_cast<StepCore::BasePolygon*>(_item) != nullptr); 0505 } 0506 0507 inline StepCore::BasePolygon* BasePolygonGraphicsItem::basePolygon() const 0508 { 0509 return static_cast<StepCore::BasePolygon*>(_item); 0510 } 0511 0512 void BasePolygonGraphicsItem::viewScaleChanged() 0513 { 0514 _painterPath = QPainterPath(); 0515 _painterPath.setFillRule(Qt::WindingFill); 0516 0517 if(basePolygon()->vertexes().size() > 0) { 0518 _painterPath.moveTo(vectorToPoint( basePolygon()->vertexes()[0] )); 0519 for(unsigned int i=1; i<basePolygon()->vertexes().size(); ++i) { 0520 _painterPath.lineTo(vectorToPoint( basePolygon()->vertexes()[i] )); 0521 } 0522 _painterPath.closeSubpath(); 0523 _painterPath = QTransform().rotate(basePolygon()->angle() * 180 / StepCore::Constants::Pi).map(_painterPath); 0524 } else { 0525 double s = currentViewScale(); 0526 _painterPath.addEllipse(-1/s, -1/s, 2/s, 2/s); 0527 } 0528 0529 RigidBodyGraphicsItem::viewScaleChanged(); 0530 } 0531 0532 ///////////////////////////////////////////////////////////////////////////////////////// 0533 0534 inline StepCore::Box* BoxVertexHandlerGraphicsItem::box() const 0535 { 0536 return static_cast<StepCore::Box*>(_item); 0537 } 0538 0539 StepCore::Vector2d BoxVertexHandlerGraphicsItem::value() { 0540 return box()->vectorLocalToWorld((box()->size().array()*(corners[_vertexNum]).array()).matrix()); 0541 //return box()->vectorLocalToWorld(box()->vertexes()[_vertexNum]); 0542 } 0543 0544 void BoxVertexHandlerGraphicsItem::setValue(const StepCore::Vector2d& value) 0545 { 0546 StepCore::Vector2d oCorner = box()->position() - 0547 (box()->size().array()*(corners[_vertexNum].array())).matrix(); 0548 0549 StepCore::Vector2d delta = (box()->position() + value - oCorner)/2.0; 0550 StepCore::Vector2d newPos = oCorner + delta; 0551 StepCore::Vector2d newSize = (newPos - oCorner)*2.0; 0552 0553 double d = -0.1/currentViewScale(); 0554 StepCore::Vector2d sign = (delta.array()*(corners[_vertexNum]).array()).matrix(); 0555 if(sign[0] < d || sign[1] < d) { 0556 if(sign[0] < d) { 0557 newPos[0] = oCorner[0]; newSize[0] = 0; 0558 _vertexNum ^= 1; 0559 } 0560 if(sign[1] < d) { 0561 newPos[1] = oCorner[1]; newSize[1] = 0; 0562 _vertexNum ^= 2; 0563 } 0564 _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(newPos)); 0565 _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue(newSize)); 0566 setValue(value); 0567 return; 0568 } 0569 0570 _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(newPos)); 0571 _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue(newSize)); 0572 #if 0 0573 StepCore::Vector2d delta = box()->vectorWorldToLocal(value) - box()->vertexes()[_vertexNum]; 0574 StepCore::Vector2d newPos = box()->position() + box()->vectorLocalToWorld(delta/2.0); 0575 0576 switch(_vertexNum) { 0577 case 3: delta[0] = -delta[0]; break; 0578 case 0: delta[0] = -delta[0]; /* no break */ 0579 case 1: delta[1] = -delta[1]; break; 0580 default: break; 0581 } 0582 0583 _worldModel->setProperty(_item, "position", QVariant::fromValue(newPos)); 0584 _worldModel->setProperty(_item, "size", QVariant::fromValue(box()->size() + delta)); 0585 #endif 0586 } 0587 0588 OnHoverHandlerGraphicsItem* BoxGraphicsItem::createOnHoverHandler(const QPointF& pos) 0589 { 0590 double s = currentViewScale(); 0591 StepCore::Vector2d l = pointToVector(pos) - rigidBody()->position(); 0592 StepCore::Vector2d size = static_cast<StepCore::Box*>(_item)->size(); 0593 0594 int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE/s/s; 0595 for(unsigned int i=0; i<4; ++i) { 0596 double dist2 = (l - (size.array()*(OnHoverHandlerGraphicsItem::corners[i]).array()).matrix()).squaredNorm(); 0597 if(dist2 < minDist2) { num = i; minDist2 = dist2; } 0598 } 0599 0600 #if 0 0601 StepCore::Vector2d l = basePolygon()->pointWorldToLocal(pointToVector(pos)); 0602 double s = currentViewScale(); 0603 int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE/s/s; 0604 for(unsigned int i=0; i<basePolygon()->vertexes().size(); ++i) { 0605 double dist2 = (basePolygon()->vertexes()[i] - l).squaredNorm(); 0606 if(dist2 < minDist2) { num = i; minDist2 = dist2; } 0607 } 0608 #endif 0609 0610 if(_onHoverHandler && _onHoverHandler->vertexNum() == num) 0611 return _onHoverHandler; 0612 0613 if(num >= 0) 0614 return new BoxVertexHandlerGraphicsItem(_item, _worldModel, this, num); 0615 0616 return nullptr; 0617 } 0618 0619 ///////////////////////////////////////////////////////////////////////////////////////// 0620 0621 inline StepCore::Polygon* PolygonVertexHandlerGraphicsItem::polygon() const 0622 { 0623 return static_cast<StepCore::Polygon*>(_item); 0624 } 0625 0626 StepCore::Vector2d PolygonVertexHandlerGraphicsItem::value() { 0627 return polygon()->vectorLocalToWorld(polygon()->vertexes()[_vertexNum]); 0628 } 0629 0630 void PolygonVertexHandlerGraphicsItem::setValue(const StepCore::Vector2d& value) 0631 { 0632 PolygonGraphicsItem::changePolygonVertex(_worldModel, _item, 0633 _vertexNum, polygon()->vectorWorldToLocal(value)); 0634 } 0635 0636 OnHoverHandlerGraphicsItem* PolygonGraphicsItem::createOnHoverHandler(const QPointF& pos) 0637 { 0638 StepCore::Vector2d l = polygon()->pointWorldToLocal(pointToVector(pos)); 0639 double s = currentViewScale(); 0640 int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE/s/s; 0641 for(unsigned int i=0; i<polygon()->vertexes().size(); ++i) { 0642 double dist2 = (polygon()->vertexes()[i] - l).squaredNorm(); 0643 if(dist2 < minDist2) { num = i; minDist2 = dist2; } 0644 } 0645 0646 if(_onHoverHandler && _onHoverHandler->vertexNum() == num) 0647 return _onHoverHandler; 0648 0649 if(num >= 0) 0650 return new PolygonVertexHandlerGraphicsItem(_item, _worldModel, this, num); 0651 0652 return nullptr; 0653 } 0654 0655 inline StepCore::Polygon* PolygonGraphicsItem::polygon() const 0656 { 0657 return static_cast<StepCore::Polygon*>(_item); 0658 } 0659 0660 void PolygonGraphicsItem::changePolygonVertex(WorldModel* worldModel, 0661 StepCore::Item* item, int vertexNum, const StepCore::Vector2d& value) 0662 { 0663 StepCore::Vector2dList vertexes = static_cast<StepCore::Polygon*>(item)->vertexes(); 0664 Q_ASSERT(vertexNum < (int) vertexes.size()); 0665 vertexes[vertexNum] = value; 0666 worldModel->setProperty(item, QStringLiteral("vertexes"), QVariant::fromValue(vertexes)); 0667 } 0668 0669 #include "moc_polygongraphics.cpp"