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"