1 /* This file is part of Step.
2    Copyright (C) 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com>
3 
4    Step is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    Step is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with Step; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18 
19 #include "polygongraphics.h"
20 
21 #include <stepcore/rigidbody.h>
22 
23 #include <stepcore/constants.h>
24 #include <stepcore/types.h>
25 #include "worldmodel.h"
26 #include "worldscene.h"
27 #include "worldfactory.h"
28 
29 #include <QEvent>
30 #include <QGraphicsSceneMouseEvent>
31 #include <QItemSelectionModel>
32 #include <QKeyEvent>
33 #include <QPainter>
34 
35 #include <KLocalizedString>
36 
RigidBodyGraphicsItem(StepCore::Item * item,WorldModel * worldModel)37 RigidBodyGraphicsItem::RigidBodyGraphicsItem(StepCore::Item* item, WorldModel* worldModel)
38     : StepGraphicsItem(item, worldModel)
39 {
40     Q_ASSERT(dynamic_cast<StepCore::RigidBody*>(_item) != NULL);
41     setFlag(QGraphicsItem::ItemIsSelectable);
42     setFlag(QGraphicsItem::ItemIsMovable);
43     setAcceptHoverEvents(true);
44     _velocityHandler = new ArrowHandlerGraphicsItem(item, worldModel, this,
45                    _item->metaObject()->property(QStringLiteral("velocity")));
46     _velocityHandler->setVisible(false);
47 
48     _angularVelocityHandler = new CircularArrowHandlerGraphicsItem(item, worldModel, this,
49                    ANGULAR_VELOCITY_RADIUS, _item->metaObject()->property(QStringLiteral("angularVelocity")));
50     _angleHandler = new CircularArrowHandlerGraphicsItem(item, worldModel, this,
51                    ANGLE_HANDLER_RADIUS, _item->metaObject()->property(QStringLiteral("angle")));
52     _angularVelocityHandler->setVisible(false);
53     _angleHandler->setVisible(false);
54     setOnHoverHandlerEnabled(true);
55     //scene()->addItem(_velocityHandler);
56 }
57 
rigidBody() const58 inline StepCore::RigidBody* RigidBodyGraphicsItem::rigidBody() const
59 {
60     return static_cast<StepCore::RigidBody*>(_item);
61 }
62 
shape() const63 QPainterPath RigidBodyGraphicsItem::shape() const
64 {
65     return _painterPath;
66 }
67 
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)68 void RigidBodyGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/,
69 				  QWidget* /*widget*/)
70 {
71     //int renderHints = painter->renderHints();
72     painter->setRenderHint(QPainter::Antialiasing, true);
73 
74     QColor color = QColor::fromRgba(rigidBody()->color());
75     if (isItemHighlighted()) {
76 	color = highlightColor(color);
77     }
78     painter->setPen(Qt::NoPen);
79     painter->setBrush(QBrush(color));
80 
81     painter->drawPath(_painterPath);
82 
83     if (!_markPath.isEmpty()) {
84 	QPen pen(color);
85 	pen.setWidth(0);
86 	painter->setPen(pen);
87 	painter->setBrush(QColor(Qt::white));
88         painter->drawPath(_markPath);
89     }
90 
91     if(_isSelected) {
92         double s = currentViewScale();
93         QRectF rect = _painterPath.boundingRect();
94         rect.adjust(-SELECTION_MARGIN/s, -SELECTION_MARGIN/s, SELECTION_MARGIN/s, SELECTION_MARGIN/s);
95         painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine));
96         painter->setBrush(QBrush());
97         painter->drawRect(rect);
98     }
99 
100     if(_isSelected || _isMouseOverItem) {
101         //painter->setRenderHint(QPainter::Antialiasing, renderHints & QPainter::Antialiasing);
102         painter->setPen(QPen(Qt::blue, 0));
103         drawArrow(painter, rigidBody()->velocity());
104         drawCircularArrow(painter, rigidBody()->angularVelocity(), ANGULAR_VELOCITY_RADIUS);
105         painter->setPen(QPen(Qt::red, 0));
106         drawArrow(painter, rigidBody()->acceleration());
107         drawCircularArrow(painter, rigidBody()->angularAcceleration(), ANGULAR_ACCELERATION_RADIUS);
108     }
109 }
110 
viewScaleChanged()111 void RigidBodyGraphicsItem::viewScaleChanged()
112 {
113     /// XXX: optimize it !
114     prepareGeometryChange();
115 
116     const StepCore::Vector2d& v = rigidBody()->velocity();
117     const StepCore::Vector2d  a = rigidBody()->acceleration();
118     double s = currentViewScale();
119 
120     double avr = (ANGULAR_VELOCITY_RADIUS+CIRCULAR_ARROW_STROKE)/s;
121     double aar = (ANGULAR_ACCELERATION_RADIUS+CIRCULAR_ARROW_STROKE)/s;
122     _boundingRect = _painterPath.boundingRect()
123                     | QRectF(0, 0, v[0], v[1]).normalized()
124                     | QRectF(0, 0, a[0], a[1]).normalized()
125                     | QRectF(-avr, -avr, 2*avr, 2*avr)
126                     | QRectF(-aar, -aar, 2*aar, 2*aar);
127     double adjust = (ARROW_STROKE+SELECTION_MARGIN)/s;
128     _boundingRect.adjust(-adjust,-adjust, adjust, adjust);
129 }
130 
worldDataChanged(bool dynamicOnly)131 void RigidBodyGraphicsItem::worldDataChanged(bool dynamicOnly)
132 {
133     Q_UNUSED(dynamicOnly)
134     // XXX: TODO do not redraw everything each time
135     setPos(vectorToPoint(rigidBody()->position()));
136     viewScaleChanged();
137     update();
138 }
139 
stateChanged()140 void RigidBodyGraphicsItem::stateChanged()
141 {
142     if(_isSelected) {
143         _velocityHandler->setVisible(true);
144         _angularVelocityHandler->setVisible(true);
145         _angleHandler->setVisible(true);
146     } else {
147         _velocityHandler->setVisible(false);
148         _angularVelocityHandler->setVisible(false);
149         _angleHandler->setVisible(false);
150     }
151 
152     viewScaleChanged();
153     update();
154 }
155 
156 /////////////////////////////////////////////////////////////////////////////////////////
157 
start()158 void DiskCreator::start()
159 {
160     showMessage(MessageFrame::Information,
161             i18n("Press left mouse button to position a center of a %1", classNameTr()));
162 }
163 
sceneEvent(QEvent * event)164 bool DiskCreator::sceneEvent(QEvent* event)
165 {
166     QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event);
167 
168     if(event->type() == QEvent::GraphicsSceneMousePress && mouseEvent->button() == Qt::LeftButton) {
169         QPointF pos = mouseEvent->scenePos();
170         QVariant vpos = QVariant::fromValue(StepGraphicsItem::pointToVector(pos));
171 
172         _worldModel->simulationPause();
173         _worldModel->beginMacro(i18n("Create %1", _worldModel->newItemName(_className)));
174         _item = _worldModel->createItem(_className); Q_ASSERT(_item != NULL);
175         _worldModel->setProperty(_item, QStringLiteral("position"), vpos);
176         _worldModel->setProperty(_item, QStringLiteral("radius"), QVariant::fromValue(0.0));
177         _worldModel->addItem(_item);
178         _worldModel->selectionModel()->setCurrentIndex(_worldModel->objectIndex(_item),
179                                                     QItemSelectionModel::ClearAndSelect);
180 
181         showMessage(MessageFrame::Information,
182             i18n("Move mouse and release left mouse button to define a radius of the %1", classNameTr()));
183 
184         return true;
185     } else if(event->type() == QEvent::GraphicsSceneMouseMove &&
186                     mouseEvent->buttons() & Qt::LeftButton) {
187 
188         _worldModel->simulationPause();
189         StepCore::Vector2d pos = StepGraphicsItem::pointToVector(mouseEvent->scenePos());
190         double radius = (pos - static_cast<StepCore::Disk*>(_item)->position()).norm();
191         _worldModel->setProperty(_item, QStringLiteral("radius"), QVariant::fromValue(radius));
192         return true;
193 
194     } else if(event->type() == QEvent::GraphicsSceneMouseRelease &&
195                     mouseEvent->button() == Qt::LeftButton) {
196 
197         _worldModel->simulationPause();
198         StepCore::Vector2d pos = StepGraphicsItem::pointToVector(mouseEvent->scenePos());
199         StepCore::Disk* disk = static_cast<StepCore::Disk*>(_item);
200         double radius = (pos - disk->position()).norm();
201         if(radius == 0) radius = 0.5;
202         double inertia = disk->mass() * radius*radius/2.0;
203         _worldModel->setProperty(_item, QStringLiteral("radius"), QVariant::fromValue(radius));
204         _worldModel->setProperty(_item, QStringLiteral("inertia"), QVariant::fromValue(inertia));
205         _worldModel->endMacro();
206 
207         showMessage(MessageFrame::Information,
208             i18n("%1 named '%2' created", classNameTr(), _item->name()),
209             MessageFrame::CloseButton | MessageFrame::CloseTimer);
210 
211         setFinished();
212         return true;
213     }
214 
215     return false;
216 }
217 
disk() const218 inline StepCore::Disk* DiskVertexHandlerGraphicsItem::disk() const
219 {
220     return static_cast<StepCore::Disk*>(_item);
221 }
222 
value()223 StepCore::Vector2d DiskVertexHandlerGraphicsItem::value()
224 {
225     return scorners[_vertexNum]*disk()->radius();
226 }
227 
setValue(const StepCore::Vector2d & value)228 void DiskVertexHandlerGraphicsItem::setValue(const StepCore::Vector2d& value)
229 {
230     _worldModel->setProperty(_item, QStringLiteral("radius"), value.norm());
231 }
232 
DiskGraphicsItem(StepCore::Item * item,WorldModel * worldModel)233 DiskGraphicsItem::DiskGraphicsItem(StepCore::Item* item, WorldModel* worldModel)
234     : RigidBodyGraphicsItem(item, worldModel)
235 {
236     Q_ASSERT(dynamic_cast<StepCore::Disk*>(_item) != NULL);
237 }
238 
disk() const239 inline StepCore::Disk* DiskGraphicsItem::disk() const
240 {
241     return static_cast<StepCore::Disk*>(_item);
242 }
243 
viewScaleChanged()244 void DiskGraphicsItem::viewScaleChanged()
245 {
246     _painterPath = QPainterPath();
247     _painterPath.setFillRule(Qt::WindingFill);
248 
249     _markPath = QPainterPath();
250     _painterPath.setFillRule(Qt::WindingFill);
251 
252     double s = currentViewScale();
253     double radius = disk()->radius();
254     if (radius > 1/s) {
255         _painterPath.addEllipse(-radius, -radius, 2*radius, 2*radius);
256 
257         _markPath.moveTo(0, 0);
258         _markPath.arcTo(QRectF(-radius, -radius, 2 * radius, 2 * radius),
259                         -15.0, 30.0);
260 	_markPath.closeSubpath();
261     } else {
262         _painterPath.addEllipse(-1/s, -1/s, 2/s, 2/s);
263 	// Don't need a marker when the disk is too small to see in detail.
264     }
265 
266     _markPath = QMatrix().rotate(disk()->angle() * 180 / StepCore::Constants::Pi).map(_markPath);
267     RigidBodyGraphicsItem::viewScaleChanged();
268 }
269 
createOnHoverHandler(const QPointF & pos)270 OnHoverHandlerGraphicsItem* DiskGraphicsItem::createOnHoverHandler(const QPointF& pos)
271 {
272     StepCore::Vector2d l = (pointToVector(pos) - disk()->position())/disk()->radius();
273     double s = currentViewScale();
274     int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE
275                                         /s/s/disk()->radius()/disk()->radius();
276     for(unsigned int i=0; i<4; ++i) {
277         double dist2 = (l - DiskVertexHandlerGraphicsItem::scorners[i]).squaredNorm();
278         if(dist2 < minDist2) { num = i; minDist2 = dist2; }
279     }
280 
281     if(_onHoverHandler && _onHoverHandler->vertexNum() == num)
282         return _onHoverHandler;
283 
284     if(num >= 0)
285         return new DiskVertexHandlerGraphicsItem(_item, _worldModel, this, num);
286 
287     return 0;
288 }
289 
290 /////////////////////////////////////////////////////////////////////////////////////////
291 
start()292 void BoxCreator::start()
293 {
294     showMessage(MessageFrame::Information,
295             i18n("Press left mouse button to position\ntop left corner of a %1", classNameTr()));
296 }
297 
sceneEvent(QEvent * event)298 bool BoxCreator::sceneEvent(QEvent* event)
299 {
300     QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event);
301 
302     if(event->type() == QEvent::GraphicsSceneMousePress && mouseEvent->button() == Qt::LeftButton) {
303         QPointF pos = mouseEvent->scenePos();
304         QVariant vpos = QVariant::fromValue(StepGraphicsItem::pointToVector(pos));
305 
306         _worldModel->simulationPause();
307         _worldModel->beginMacro(i18n("Create %1", _worldModel->newItemName(_className)));
308         _item = _worldModel->createItem(_className); Q_ASSERT(_item != NULL);
309         _worldModel->setProperty(_item, QStringLiteral("position"), vpos);
310         _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue(StepCore::Vector2d::Zero().eval()));
311         _worldModel->addItem(_item);
312         _worldModel->selectionModel()->setCurrentIndex(_worldModel->objectIndex(_item),
313                                                     QItemSelectionModel::ClearAndSelect);
314         _topLeft = StepGraphicsItem::pointToVector(pos);
315 
316         showMessage(MessageFrame::Information,
317             i18n("Move mouse and release left mouse button to position\nbottom right corner of the %1", classNameTr()));
318 
319         return true;
320     } else if(event->type() == QEvent::GraphicsSceneMouseMove &&
321                     mouseEvent->buttons() & Qt::LeftButton) {
322 
323         _worldModel->simulationPause();
324         StepCore::Vector2d pos = StepGraphicsItem::pointToVector(mouseEvent->scenePos());
325         StepCore::Vector2d position = (_topLeft + pos) / 2.0;
326         StepCore::Vector2d size = _topLeft - pos;
327         _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(position));
328         _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue(size));
329         return true;
330 
331     } else if(event->type() == QEvent::GraphicsSceneMouseRelease &&
332                     mouseEvent->button() == Qt::LeftButton) {
333 
334         _worldModel->simulationPause();
335         StepCore::Vector2d pos = StepGraphicsItem::pointToVector(mouseEvent->scenePos());
336         StepCore::Box* box = static_cast<StepCore::Box*>(_item);
337         StepCore::Vector2d position = (_topLeft + pos) / 2.0;
338         StepCore::Vector2d size = _topLeft - pos;
339         if(size[0] == 0 && size[1] == 0) { size[0] = size[1] = 1; }
340         double inertia = box->mass() * (size[0]*size[0] + size[1]*size[1]) / 12.0;
341         _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(position));
342         _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue(size));
343         _worldModel->setProperty(_item, QStringLiteral("inertia"), QVariant::fromValue(inertia));
344         _worldModel->endMacro();
345 
346         showMessage(MessageFrame::Information,
347             i18n("%1 named '%2' created", classNameTr(), _item->name()),
348             MessageFrame::CloseButton | MessageFrame::CloseTimer);
349 
350         setFinished();
351         return true;
352     }
353 
354     return false;
355 }
356 
357 /////////////////////////////////////////////////////////////////////////////////////////
358 
fixCenterOfMass()359 void PolygonCreator::fixCenterOfMass()
360 {
361     StepCore::Vector2dList v = static_cast<StepCore::Polygon*>(_item)->vertexes();
362     StepCore::Vector2d position = static_cast<StepCore::Polygon*>(_item)->position();
363 
364     StepCore::Vector2d center(0, 0);
365     double area_i, area = 0;
366     unsigned int i;
367 
368     if(v.size() == 1) center = v[0];
369     else {
370         if(v.size() > 2) {
371             for(i=0; i+1<v.size(); ++i) {
372                 area_i = (v[i][0]*v[i+1][1] - v[i][1]*v[i+1][0]) / 2;
373                 center += (v[i] + v[i+1]) * (area_i/3);
374                 area += area_i;
375             }
376             area_i = (v[i][0]*v[0][1] - v[i][1]*v[0][0]) / 2;
377             center += (v[i] + v[0]) * (area_i/3);
378             area += area_i;
379         }
380 
381         if(area == 0) { // all vertexes on one line
382             center.setZero();
383             for(i=0; i+1<v.size(); ++i) {
384                 area_i = (v[i+1] - v[i]).norm();
385                 center += (v[i] + v[i+1]) * (area_i/2);
386                 area += area_i;
387             }
388         }
389 
390         if(area == 0) center = v[0]; // all vertexes are at one point
391         else center /= area;
392     }
393 
394     for(i=0; i<v.size(); ++i) v[i] -= center;
395     _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue((position + center).eval()));
396     _worldModel->setProperty(_item, QStringLiteral("vertexes"), QVariant::fromValue(v));
397 }
398 
fixInertia()399 void PolygonCreator::fixInertia()
400 {
401     // XXX: unite it with fixCenterOfMass
402     const StepCore::Vector2dList& v = static_cast<StepCore::Polygon*>(_item)->vertexes();
403     double mass = static_cast<StepCore::Polygon*>(_item)->mass();
404     double area_i, area = 0;
405     double inertia = 0;
406     unsigned int i;
407 
408     if(v.size() > 2) {
409         if(v.size() > 2) {
410             for(i=0; i+1<v.size(); ++i) {
411                 area_i = (v[i][0]*v[i+1][1] - v[i][1]*v[i+1][0]) / 2;
412                 inertia += (v[i].squaredNorm() + v[i+1].dot(v[i]) + v[i+1].squaredNorm())*(area_i/6);
413                 area += area_i;
414             }
415             area_i = (v[i][0]*v[0][1] - v[i][1]*v[0][0]) / 2;
416             inertia += (v[i].squaredNorm() + v[0].dot(v[i]) + v[0].squaredNorm())*(area_i/6);
417             area += area_i;
418         }
419     }
420 
421     if(area == 0) { // all vertexes on one line
422         inertia = 0;
423         for(i=0; i+1<v.size(); ++i) {
424             area_i = (v[i+1] - v[i]).norm();
425             inertia += area_i*area_i*area_i / 12 + area_i * (v[i]+v[i+1]).squaredNorm() / 4;
426             area += area_i;
427         }
428 
429         if(area == 0) inertia = 0; // all vertexes are at one point
430         else inertia /= area;
431     }
432 
433     inertia = fabs(inertia * mass); // 1 = 1m XXX XXX XXX
434     _worldModel->setProperty(_item, QStringLiteral("inertia"), QVariant::fromValue(inertia));
435 }
436 
start()437 void PolygonCreator::start()
438 {
439     showMessage(MessageFrame::Information,
440             i18n("Click on the scene to create a first vertex of %1", classNameTr()));
441 }
442 
sceneEvent(QEvent * event)443 bool PolygonCreator::sceneEvent(QEvent* event)
444 {
445     QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event);
446 
447     if(!_item && event->type() == QEvent::GraphicsSceneMousePress && mouseEvent->button() == Qt::LeftButton) {
448         QPointF pos = mouseEvent->scenePos();
449         QVariant vpos = QVariant::fromValue(StepGraphicsItem::pointToVector(pos));
450 
451         _worldModel->simulationPause();
452         _worldModel->beginMacro(i18n("Create %1", _worldModel->newItemName(_className)));
453         _item = _worldModel->createItem(_className); Q_ASSERT(_item != NULL);
454         _worldModel->setProperty(_item, QStringLiteral("position"), vpos);
455         _worldModel->setProperty(_item, QStringLiteral("vertexes"), QStringLiteral("(0,0)"));
456         _worldModel->addItem(_item);
457         _worldModel->selectionModel()->setCurrentIndex(_worldModel->objectIndex(_item),
458                                                     QItemSelectionModel::ClearAndSelect);
459 
460         return true;
461 
462     } else if(_item && event->type() == QEvent::GraphicsSceneMousePress) {
463         return true;
464 
465     } else if(_item && (event->type() == QEvent::GraphicsSceneMouseMove ||
466                         (event->type() == QEvent::GraphicsSceneMouseRelease &&
467                          mouseEvent->button() == Qt::LeftButton))) {
468 
469         QPointF pos = mouseEvent->scenePos();
470         StepCore::Vector2d v = StepGraphicsItem::pointToVector(pos);
471 
472         _worldModel->simulationPause();
473         // XXX: don't use strings !
474         QString vertexes = _item->metaObject()->property(QStringLiteral("vertexes"))->readString(_item).section(',', 0, -3);
475         if(vertexes.isEmpty()) {
476             _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(v));
477             vertexes = QStringLiteral("(0,0)"); v.setZero();
478         } else {
479             v -= static_cast<StepCore::Polygon*>(_item)->position();
480             vertexes += QStringLiteral(",(%1,%2)").arg(v[0]).arg(v[1]);
481             _worldModel->setProperty(_item, QStringLiteral("vertexes"), vertexes);
482         }
483 
484         if(event->type() == QEvent::GraphicsSceneMouseRelease) {
485             vertexes += QStringLiteral(",(%1,%2)").arg(v[0]).arg(v[1]);
486             _worldModel->setProperty(_item, QStringLiteral("vertexes"), vertexes);
487             showMessage(MessageFrame::Information,
488                 i18n("Click on the scene to add new vertex or press Enter to finish"));
489         }
490 
491         //fixCenterOfMass();
492         //fixInertia();
493         return true;
494 
495     } else if(_item && event->type() == QEvent::KeyPress &&
496                 static_cast<QKeyEvent*>(event)->key() == Qt::Key_Return) {
497         fixCenterOfMass();
498         fixInertia();
499         _worldModel->endMacro();
500 
501         showMessage(MessageFrame::Information,
502             i18n("%1 named '%2' created", classNameTr(), _item->name()),
503             MessageFrame::CloseButton | MessageFrame::CloseTimer);
504 
505         setFinished();
506         return true;
507     }
508     return false;
509 }
510 
511 /////////////////////////////////////////////////////////////////////////////////////////
512 
BasePolygonGraphicsItem(StepCore::Item * item,WorldModel * worldModel)513 BasePolygonGraphicsItem::BasePolygonGraphicsItem(StepCore::Item* item, WorldModel* worldModel)
514     : RigidBodyGraphicsItem(item, worldModel)
515 {
516     Q_ASSERT(dynamic_cast<StepCore::BasePolygon*>(_item) != NULL);
517 }
518 
basePolygon() const519 inline StepCore::BasePolygon* BasePolygonGraphicsItem::basePolygon() const
520 {
521     return static_cast<StepCore::BasePolygon*>(_item);
522 }
523 
viewScaleChanged()524 void BasePolygonGraphicsItem::viewScaleChanged()
525 {
526     _painterPath = QPainterPath();
527     _painterPath.setFillRule(Qt::WindingFill);
528 
529     if(basePolygon()->vertexes().size() > 0) {
530         _painterPath.moveTo(vectorToPoint( basePolygon()->vertexes()[0] ));
531         for(unsigned int i=1; i<basePolygon()->vertexes().size(); ++i) {
532             _painterPath.lineTo(vectorToPoint( basePolygon()->vertexes()[i] ));
533         }
534         _painterPath.closeSubpath();
535         _painterPath = QMatrix().rotate(basePolygon()->angle() * 180 / StepCore::Constants::Pi).map(_painterPath);
536     } else {
537         double s = currentViewScale();
538         _painterPath.addEllipse(-1/s, -1/s, 2/s, 2/s);
539     }
540 
541     RigidBodyGraphicsItem::viewScaleChanged();
542 }
543 
544 /////////////////////////////////////////////////////////////////////////////////////////
545 
box() const546 inline StepCore::Box* BoxVertexHandlerGraphicsItem::box() const
547 {
548     return static_cast<StepCore::Box*>(_item);
549 }
550 
value()551 StepCore::Vector2d BoxVertexHandlerGraphicsItem::value() {
552     return box()->vectorLocalToWorld((box()->size().array()*(corners[_vertexNum]).array()).matrix());
553     //return box()->vectorLocalToWorld(box()->vertexes()[_vertexNum]);
554 }
555 
setValue(const StepCore::Vector2d & value)556 void BoxVertexHandlerGraphicsItem::setValue(const StepCore::Vector2d& value)
557 {
558     StepCore::Vector2d oCorner = box()->position() -
559                         (box()->size().array()*(corners[_vertexNum].array())).matrix();
560 
561     StepCore::Vector2d delta = (box()->position() + value - oCorner)/2.0;
562     StepCore::Vector2d newPos = oCorner + delta;
563     StepCore::Vector2d newSize = (newPos - oCorner)*2.0;
564 
565     double d = -0.1/currentViewScale();
566     StepCore::Vector2d sign = (delta.array()*(corners[_vertexNum]).array()).matrix();
567     if(sign[0] < d || sign[1] < d) {
568         if(sign[0] < d) {
569             newPos[0] = oCorner[0]; newSize[0] = 0;
570             _vertexNum ^= 1;
571         }
572         if(sign[1] < d) {
573             newPos[1] = oCorner[1]; newSize[1] = 0;
574             _vertexNum ^= 2;
575         }
576         _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(newPos));
577         _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue(newSize));
578         setValue(value);
579         return;
580     }
581 
582     _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(newPos));
583     _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue(newSize));
584 #if 0
585     StepCore::Vector2d delta = box()->vectorWorldToLocal(value) - box()->vertexes()[_vertexNum];
586     StepCore::Vector2d newPos = box()->position() + box()->vectorLocalToWorld(delta/2.0);
587 
588     switch(_vertexNum) {
589         case 3: delta[0] = -delta[0]; break;
590         case 0: delta[0] = -delta[0]; /* no break */
591         case 1: delta[1] = -delta[1]; break;
592         default: break;
593     }
594 
595     _worldModel->setProperty(_item, "position", QVariant::fromValue(newPos));
596     _worldModel->setProperty(_item, "size", QVariant::fromValue(box()->size() + delta));
597 #endif
598 }
599 
createOnHoverHandler(const QPointF & pos)600 OnHoverHandlerGraphicsItem* BoxGraphicsItem::createOnHoverHandler(const QPointF& pos)
601 {
602     double s = currentViewScale();
603     StepCore::Vector2d l = pointToVector(pos) - rigidBody()->position();
604     StepCore::Vector2d size = static_cast<StepCore::Box*>(_item)->size();
605 
606     int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE/s/s;
607     for(unsigned int i=0; i<4; ++i) {
608         double dist2 = (l - (size.array()*(OnHoverHandlerGraphicsItem::corners[i]).array()).matrix()).squaredNorm();
609         if(dist2 < minDist2) { num = i; minDist2 = dist2; }
610     }
611 
612 #if 0
613     StepCore::Vector2d l = basePolygon()->pointWorldToLocal(pointToVector(pos));
614     double s = currentViewScale();
615     int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE/s/s;
616     for(unsigned int i=0; i<basePolygon()->vertexes().size(); ++i) {
617         double dist2 = (basePolygon()->vertexes()[i] - l).squaredNorm();
618         if(dist2 < minDist2) { num = i; minDist2 = dist2; }
619     }
620 #endif
621 
622     if(_onHoverHandler && _onHoverHandler->vertexNum() == num)
623         return _onHoverHandler;
624 
625     if(num >= 0)
626         return new BoxVertexHandlerGraphicsItem(_item, _worldModel, this, num);
627 
628     return 0;
629 }
630 
631 /////////////////////////////////////////////////////////////////////////////////////////
632 
polygon() const633 inline StepCore::Polygon* PolygonVertexHandlerGraphicsItem::polygon() const
634 {
635     return static_cast<StepCore::Polygon*>(_item);
636 }
637 
value()638 StepCore::Vector2d PolygonVertexHandlerGraphicsItem::value() {
639     return polygon()->vectorLocalToWorld(polygon()->vertexes()[_vertexNum]);
640 }
641 
setValue(const StepCore::Vector2d & value)642 void PolygonVertexHandlerGraphicsItem::setValue(const StepCore::Vector2d& value)
643 {
644     PolygonGraphicsItem::changePolygonVertex(_worldModel, _item,
645                 _vertexNum, polygon()->vectorWorldToLocal(value));
646 }
647 
createOnHoverHandler(const QPointF & pos)648 OnHoverHandlerGraphicsItem* PolygonGraphicsItem::createOnHoverHandler(const QPointF& pos)
649 {
650     StepCore::Vector2d l = polygon()->pointWorldToLocal(pointToVector(pos));
651     double s = currentViewScale();
652     int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE/s/s;
653     for(unsigned int i=0; i<polygon()->vertexes().size(); ++i) {
654         double dist2 = (polygon()->vertexes()[i] - l).squaredNorm();
655         if(dist2 < minDist2) { num = i; minDist2 = dist2; }
656     }
657 
658     if(_onHoverHandler && _onHoverHandler->vertexNum() == num)
659         return _onHoverHandler;
660 
661     if(num >= 0)
662         return new PolygonVertexHandlerGraphicsItem(_item, _worldModel, this, num);
663 
664     return 0;
665 }
666 
polygon() const667 inline StepCore::Polygon* PolygonGraphicsItem::polygon() const
668 {
669     return static_cast<StepCore::Polygon*>(_item);
670 }
671 
changePolygonVertex(WorldModel * worldModel,StepCore::Item * item,int vertexNum,const StepCore::Vector2d & value)672 void PolygonGraphicsItem::changePolygonVertex(WorldModel* worldModel,
673             StepCore::Item* item, int vertexNum, const StepCore::Vector2d& value)
674 {
675     StepCore::Vector2dList vertexes = static_cast<StepCore::Polygon*>(item)->vertexes();
676     Q_ASSERT(vertexNum < (int) vertexes.size());
677     vertexes[vertexNum] = value;
678     worldModel->setProperty(item, QStringLiteral("vertexes"), QVariant::fromValue(vertexes));
679 }
680 
681