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