File indexing completed on 2024-04-28 07:39:36

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 "jointgraphics.h"
0008 
0009 #include "worldmodel.h"
0010 
0011 #include <stepcore/particle.h>
0012 #include <stepcore/rigidbody.h>
0013 
0014 #include <QGraphicsSceneMouseEvent>
0015 #include <KLocalizedString>
0016 
0017 AnchorGraphicsItem::AnchorGraphicsItem(StepCore::Item* item, WorldModel* worldModel)
0018     : StepGraphicsItem(item, worldModel)
0019 {
0020     Q_ASSERT(dynamic_cast<StepCore::Anchor*>(_item) != nullptr);
0021     setFlag(QGraphicsItem::ItemIsSelectable);
0022     setFlag(QGraphicsItem::ItemIsMovable);
0023     setZValue(JOINT_ZVALUE);
0024     setExclusiveMoving(true);
0025 }
0026 
0027 inline StepCore::Anchor* AnchorGraphicsItem::anchor() const
0028 {
0029     return static_cast<StepCore::Anchor*>(_item);
0030 }
0031 
0032 QPainterPath AnchorGraphicsItem::shape() const
0033 {
0034     QPainterPath path;
0035     double radius = (HANDLER_SIZE+1)/currentViewScale();
0036     path.addEllipse(QRectF(-radius,-radius,radius*2,radius*2));
0037     return path;
0038 }
0039 
0040 void AnchorGraphicsItem::mouseSetPos(const QPointF& pos, const QPointF&, MovingState movingState)
0041 {
0042     static_cast<WorldScene*>(scene())->snapItem(pos,
0043                 WorldScene::SnapRigidBody | WorldScene::SnapParticle | WorldScene::SnapOnCenter |
0044                 WorldScene::SnapSetPosition | WorldScene::SnapSetAngle, nullptr, movingState, _item);
0045 }
0046 
0047 void AnchorGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
0048 {
0049     double s = currentViewScale();
0050     double radius = HANDLER_SIZE/s;
0051 
0052     painter->setRenderHint(QPainter::Antialiasing, true);
0053     painter->setPen(QPen(QColor::fromRgba(anchor()->color()), 0, Qt::SolidLine));
0054     painter->drawEllipse(QRectF(-radius,-radius,radius*2,radius*2));
0055     painter->drawLine(QLineF(-radius, -radius, radius, radius));
0056     painter->drawLine(QLineF(-radius, radius, radius, -radius));
0057 
0058     if(_isSelected) {
0059         painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine));
0060         painter->setBrush(Qt::NoBrush);
0061         //painter->setBrush(QBrush(QColor(0, 0x99, 0xff)));
0062         radius = (HANDLER_SIZE+SELECTION_MARGIN)/s;
0063         painter->drawEllipse(QRectF(-radius, -radius, radius*2, radius*2));
0064     }
0065 }
0066 
0067 void AnchorGraphicsItem::viewScaleChanged()
0068 {
0069     prepareGeometryChange();
0070 
0071     double s = currentViewScale();
0072     _boundingRect |= QRectF((-HANDLER_SIZE-SELECTION_MARGIN)/s,  (-HANDLER_SIZE-SELECTION_MARGIN)/s,
0073                             (HANDLER_SIZE+SELECTION_MARGIN)*2/s,( HANDLER_SIZE+SELECTION_MARGIN)*2/s);
0074 }
0075 
0076 void AnchorGraphicsItem::worldDataChanged(bool dynamicOnly)
0077 {
0078     if(!dynamicOnly) update();
0079     setPos(vectorToPoint(anchor()->position()));       
0080 }
0081 
0082 //////////////////////////////////////////////////////////////////////////
0083 
0084 PinGraphicsItem::PinGraphicsItem(StepCore::Item* item, WorldModel* worldModel)
0085     : StepGraphicsItem(item, worldModel)
0086 {
0087     Q_ASSERT(dynamic_cast<StepCore::Pin*>(_item) != nullptr);
0088     setFlag(QGraphicsItem::ItemIsSelectable);
0089     setFlag(QGraphicsItem::ItemIsMovable);
0090     setZValue(JOINT_ZVALUE);
0091     setExclusiveMoving(true);
0092 }
0093 
0094 inline StepCore::Pin* PinGraphicsItem::pin() const
0095 {
0096     return static_cast<StepCore::Pin*>(_item);
0097 }
0098 
0099 QPainterPath PinGraphicsItem::shape() const
0100 {
0101     QPainterPath path;
0102     double radius = (HANDLER_SIZE+1)/currentViewScale();
0103     path.addEllipse(QRectF(-radius,-radius,radius*2,radius*2));
0104     return path;
0105 }
0106 
0107 void PinGraphicsItem::mouseSetPos(const QPointF& pos, const QPointF&, MovingState movingState)
0108 {
0109     static_cast<WorldScene*>(scene())->snapItem(pos,
0110                 WorldScene::SnapRigidBody | WorldScene::SnapSetPosition |
0111                 WorldScene::SnapSetLocalPosition, nullptr, movingState, _item);
0112 }
0113 
0114 void PinGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
0115 {
0116     double s = currentViewScale();
0117     double radius = HANDLER_SIZE/s;
0118 
0119     painter->setRenderHint(QPainter::Antialiasing, true);
0120     painter->setPen(QPen(QColor::fromRgba(pin()->color()), 0, Qt::SolidLine));
0121     painter->drawEllipse(QRectF(-radius,-radius,radius*2,radius*2));
0122     painter->drawPoint(0,0);//Rect(QRectF(-0.5/s,-0.5/s, 1/s, 1/s));
0123 
0124     if(_isSelected) {
0125         painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine));
0126         painter->setBrush(Qt::NoBrush);
0127         //painter->setBrush(QBrush(QColor(0, 0x99, 0xff)));
0128         radius = (HANDLER_SIZE+SELECTION_MARGIN)/s;
0129         painter->drawEllipse(QRectF(-radius, -radius, radius*2, radius*2));
0130     }
0131 }
0132 
0133 void PinGraphicsItem::viewScaleChanged()
0134 {
0135     prepareGeometryChange();
0136 
0137     double s = currentViewScale();
0138     _boundingRect |= QRectF((-HANDLER_SIZE-SELECTION_MARGIN)/s,  (-HANDLER_SIZE-SELECTION_MARGIN)/s,
0139                             (HANDLER_SIZE+SELECTION_MARGIN)*2/s,( HANDLER_SIZE+SELECTION_MARGIN)*2/s);
0140 }
0141 
0142 void PinGraphicsItem::worldDataChanged(bool dynamicOnly)
0143 {
0144     if(!dynamicOnly) update();
0145     setPos(vectorToPoint(pin()->position()));       
0146 }
0147 
0148 //////////////////////////////////////////////////////////////////////////
0149 
0150 StickHandlerGraphicsItem::StickHandlerGraphicsItem(StepCore::Item* item, WorldModel* worldModel, 
0151                                 QGraphicsItem* parent, int num)
0152     : StepGraphicsItem(item, worldModel, parent), _num(num)
0153 {
0154     Q_ASSERT(_num == 1 || _num == 2);
0155     setFlag(QGraphicsItem::ItemIsMovable);
0156     setZValue(HANDLER_ZVALUE);
0157     setExclusiveMoving(true);
0158     setExclusiveMovingMessage(i18n("Move end of %1", _item->name()));
0159     setPos(0, 0);
0160 }
0161 
0162 void StickHandlerGraphicsItem::viewScaleChanged()
0163 {
0164     prepareGeometryChange();
0165     double w = HANDLER_SIZE/currentViewScale()/2;
0166     _boundingRect = QRectF(-w, -w, w*2, w*2);
0167 }
0168 
0169 void StickHandlerGraphicsItem::worldDataChanged(bool)
0170 {
0171     if(_num == 2)
0172         setPos(vectorToPoint(static_cast<StepCore::Stick*>(_item)->position2()-
0173                              static_cast<StepCore::Stick*>(_item)->position1()));
0174 }
0175 
0176 void StickHandlerGraphicsItem::mouseSetPos(const QPointF& pos, const QPointF&, MovingState movingState)
0177 {
0178     static_cast<WorldScene*>(scene())->snapItem(parentItem()->mapToParent(pos),
0179                     WorldScene::SnapParticle | WorldScene::SnapRigidBody |
0180                     WorldScene::SnapSetLocalPosition, nullptr, movingState, _item, _num);
0181 
0182     StepCore::Stick* stick = static_cast<StepCore::Stick*>(_item);
0183     _worldModel->setProperty(_item, QStringLiteral("restLength"), 
0184                         (stick->position2() - stick->position1()).norm());
0185 
0186 }
0187 
0188 StickGraphicsItem::StickGraphicsItem(StepCore::Item* item, WorldModel* worldModel)
0189     : StepGraphicsItem(item, worldModel)
0190 {
0191     Q_ASSERT(dynamic_cast<StepCore::Stick*>(_item) != nullptr);
0192     setFlag(QGraphicsItem::ItemIsSelectable);
0193     setFlag(QGraphicsItem::ItemIsMovable);
0194     setZValue(JOINT_ZVALUE);
0195     _handler1 = new StickHandlerGraphicsItem(item, worldModel, this, 1);
0196     _handler2 = new StickHandlerGraphicsItem(item, worldModel, this, 2);
0197     _handler1->setVisible(false);
0198     _handler2->setVisible(false);
0199 }
0200 
0201 QPainterPath StickGraphicsItem::shape() const
0202 {
0203     return _painterPath;
0204     /*
0205     QPainterPath path;
0206 
0207     double u = 1/currentViewScale();
0208     path.addRect(QRectF(-u, -_radius-u, _rnorm+u, _radius*2+u));
0209 
0210     _worldModel->simulationPause();
0211     StepCore::Vector2d r = stick()->position2() - stick()->position1();
0212     return QMatrix().rotate(atan2(r[1], r[0])*180/3.14).map(path);
0213     */
0214 }
0215 
0216 void StickGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
0217 {
0218     StepCore::Vector2d r = stick()->position2() - stick()->position1();
0219 
0220     painter->setPen(QPen(QColor::fromRgba(stick()->color()), 0));
0221 
0222     StepCore::Vector2d p1c, p2c;
0223     if(stick()->rigidBody1()) p1c = stick()->rigidBody1()->position();
0224     else if(stick()->particle1()) p1c = stick()->particle1()->position();
0225     else p1c = stick()->position1();
0226 
0227     if(stick()->rigidBody2()) p2c = stick()->rigidBody2()->position();
0228     else if(stick()->particle2()) p2c = stick()->particle2()->position();
0229     else p2c = stick()->position2();
0230 
0231     painter->drawLine(QPointF(0, 0), vectorToPoint(p1c - stick()->position1()));
0232     painter->drawLine(vectorToPoint(r), vectorToPoint(p2c - stick()->position1()));
0233 
0234     painter->rotate(atan2(r[1], r[0])*180/3.14);
0235     painter->setPen(Qt::NoPen);
0236     painter->setBrush(QBrush(QColor::fromRgba(stick()->color())));
0237     painter->setRenderHint(QPainter::Antialiasing, true);
0238     painter->drawRect(QRectF(0, -_radius, stick()->restLength(), _radius*2));
0239 
0240     if(stick()->restLength() < _rnorm + _radius/RADIUS) {
0241         painter->setBrush(Qt::NoBrush);
0242         painter->setPen(QPen(QColor::fromRgba(stick()->color()), 0, Qt::DotLine));
0243         painter->drawLine(QLineF(stick()->restLength(), 0, _rnorm, 0));
0244     }
0245 
0246     if(isSelected()) {
0247         painter->setRenderHint(QPainter::Antialiasing, true);
0248         painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine));
0249         painter->setBrush(Qt::NoBrush);
0250         double m = SELECTION_MARGIN / currentViewScale();
0251         //painter->scale( 1/_rscale, 1/_radius );
0252         painter->drawRect(QRectF(-m, -_radius-m, _rnorm+m*2,  (_radius+m)*2));
0253     }
0254 }
0255 
0256 void StickGraphicsItem::viewScaleChanged()
0257 {
0258     prepareGeometryChange();
0259 
0260     double s = currentViewScale();
0261     double m = (SELECTION_MARGIN+1) / s;
0262     double u = 1/s;
0263     
0264     StepCore::Vector2d r = stick()->position2() - stick()->position1();
0265     _rnorm = r.norm();
0266     _radius = RADIUS/s;
0267 
0268     if(_rnorm < stick()->restLength()) r *= stick()->restLength() / _rnorm;
0269     
0270     _boundingRect = QRectF(0, 0, r[0], r[1]).normalized();
0271     _boundingRect.adjust(-_radius-m, -_radius-m, _radius+m, _radius+m);
0272 
0273     StepCore::Vector2d p1c, p2c;
0274     if(stick()->rigidBody1()) p1c = stick()->rigidBody1()->position();
0275     else if(stick()->particle1()) p1c = stick()->particle1()->position();
0276     else p1c = stick()->position1();
0277 
0278     if(stick()->rigidBody2()) p2c = stick()->rigidBody2()->position();
0279     else if(stick()->particle2()) p2c = stick()->particle2()->position();
0280     else p2c = stick()->position2();
0281 
0282     _boundingRect |= QRectF(QPoint(0, 0), vectorToPoint(p1c - stick()->position1())).normalized();
0283     _boundingRect |= QRectF(vectorToPoint(r), vectorToPoint(p2c - stick()->position2())).normalized();
0284 
0285     _painterPath.addRect(QRectF(-u, -_radius-u, _rnorm+u, _radius*2+u));
0286     _painterPath = QTransform().rotate(atan2(r[1], r[0])*180/3.14).map(_painterPath);
0287         
0288     //update(); // XXX: documentation says this is unnecessary, but it doesn't work without it
0289 }
0290 
0291 void StickGraphicsItem::worldDataChanged(bool dynamicOnly)
0292 {
0293     Q_UNUSED(dynamicOnly)
0294     // XXX: TODO do not redraw everything each time
0295     setPos(vectorToPoint(stick()->position1()));
0296     viewScaleChanged();
0297     update();
0298 }
0299 
0300 void StickGraphicsItem::stateChanged()
0301 {
0302     if(_isSelected) {
0303         _handler1->setVisible(true);
0304         _handler2->setVisible(true);
0305     }
0306     else {
0307         _handler1->setVisible(false);
0308         _handler2->setVisible(false);
0309     }
0310     viewScaleChanged();
0311     update();
0312 }
0313 
0314 void StickGraphicsItem::mouseSetPos(const QPointF& /*pos*/, const QPointF& diff, MovingState)
0315 {
0316     _worldModel->simulationPause();
0317 
0318     if(stick()->body1()) {
0319         Q_ASSERT(stick()->body1()->metaObject()->inherits<StepCore::Item>());
0320         StepGraphicsItem* gItem = static_cast<WorldScene*>(
0321             scene())->graphicsFromItem(static_cast<StepCore::Item*>(stick()->body1()));
0322         Q_ASSERT(gItem != nullptr);
0323         if(!gItem->isSelected()) {
0324             _worldModel->setProperty(_item, QStringLiteral("localPosition1"),
0325                             _item->metaObject()->property(QStringLiteral("position1"))->readVariant(_item));
0326             _worldModel->setProperty(_item, QStringLiteral("body1"),
0327                             QVariant::fromValue<StepCore::Object*>(NULL), WorldModel::UndoNoMerge);
0328         }
0329     } else {
0330         _worldModel->setProperty(_item, QStringLiteral("localPosition1"),
0331             QVariant::fromValue( (stick()->position1() + pointToVector(diff)).eval() ));
0332     }
0333 
0334     if(stick()->body2()) {
0335         Q_ASSERT(stick()->body2()->metaObject()->inherits<StepCore::Item>());
0336         StepGraphicsItem* gItem = static_cast<WorldScene*>(
0337             scene())->graphicsFromItem(static_cast<StepCore::Item*>(stick()->body2()));
0338         Q_ASSERT(gItem != nullptr);
0339         if(!gItem->isSelected()) {
0340             _worldModel->setProperty(_item, QStringLiteral("localPosition2"),
0341                             _item->metaObject()->property(QStringLiteral("position2"))->readVariant(_item));
0342             _worldModel->setProperty(_item, QStringLiteral("body2"), QString(), WorldModel::UndoNoMerge);
0343         }
0344     } else {
0345         _worldModel->setProperty(_item, QStringLiteral("localPosition2"),
0346             QVariant::fromValue( (stick()->position2() + pointToVector(diff)).eval() ));
0347     }
0348 }