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

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 "springgraphics.h"
0008 
0009 #include <stepcore/spring.h>
0010 
0011 #include "worldmodel.h"
0012 #include "worldscene.h"
0013 #include "worldfactory.h"
0014 
0015 #include <QGraphicsSceneMouseEvent>
0016 #include <QPainter>
0017 
0018 #include <KLocalizedString>
0019 
0020 #include <cmath>
0021 
0022 SpringHandlerGraphicsItem::SpringHandlerGraphicsItem(StepCore::Item* item, WorldModel* worldModel, 
0023                                 QGraphicsItem* parent, int num)
0024     : StepGraphicsItem(item, worldModel, parent), _num(num)
0025 {
0026     Q_ASSERT(_num == 1 || _num == 2);
0027     setFlag(QGraphicsItem::ItemIsMovable);
0028     setZValue(HANDLER_ZVALUE);
0029     setExclusiveMoving(true);
0030     setExclusiveMovingMessage(i18n("Move end of %1", _item->name()));
0031     setPos(0, 0);
0032 }
0033 
0034 void SpringHandlerGraphicsItem::viewScaleChanged()
0035 {
0036     prepareGeometryChange();
0037     double w = HANDLER_SIZE/currentViewScale()/2;
0038     _boundingRect = QRectF(-w, -w, w*2, w*2);
0039 }
0040 
0041 void SpringHandlerGraphicsItem::worldDataChanged(bool)
0042 {
0043     if(_num == 2)
0044         setPos(vectorToPoint(static_cast<StepCore::Spring*>(_item)->position2()-
0045                              static_cast<StepCore::Spring*>(_item)->position1()));
0046 }
0047 
0048 void SpringHandlerGraphicsItem::mouseSetPos(const QPointF& pos, const QPointF&, MovingState movingState)
0049 {
0050     static_cast<WorldScene*>(scene())->snapItem(parentItem()->mapToParent(pos),
0051                     WorldScene::SnapParticle | WorldScene::SnapRigidBody |
0052                     WorldScene::SnapSetLocalPosition, nullptr, movingState, _item, _num);
0053 
0054 }
0055 
0056 SpringGraphicsItem::SpringGraphicsItem(StepCore::Item* item, WorldModel* worldModel)
0057     : StepGraphicsItem(item, worldModel)
0058 {
0059     Q_ASSERT(dynamic_cast<StepCore::Spring*>(_item) != nullptr);
0060     setFlag(QGraphicsItem::ItemIsSelectable);
0061     setFlag(QGraphicsItem::ItemIsMovable);
0062     setZValue(FORCE_ZVALUE);
0063     _handler1 = new SpringHandlerGraphicsItem(item, worldModel, this, 1);
0064     _handler2 = new SpringHandlerGraphicsItem(item, worldModel, this, 2);
0065     _handler1->setVisible(false);
0066     _handler2->setVisible(false);
0067 }
0068 
0069 QPainterPath SpringGraphicsItem::shape() const
0070 {
0071     return _painterPath;
0072     /*
0073     QPainterPath path;
0074 
0075     double u = 1/currentViewScale();
0076     path.addRect(QRectF(-u, -_radius-u, _rnorm+u, _radius*2+u));
0077 
0078     _worldModel->simulationPause();
0079     StepCore::Vector2d r = spring()->position2() - spring()->position1();
0080     return QMatrix().rotate(atan2(r[1], r[0])*180/3.14).map(path);
0081     */
0082 }
0083 
0084 void SpringGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
0085 {
0086     static const int seq[4] = { 0,1,0,-1 };
0087 
0088     StepCore::Vector2d r = spring()->position2() - spring()->position1();
0089 
0090     painter->setPen(QPen(QColor::fromRgba(spring()->color()), 0));
0091 
0092     StepCore::Vector2d p1c, p2c;
0093     if(spring()->rigidBody1()) p1c = spring()->rigidBody1()->position();
0094     else if(spring()->particle1()) p1c = spring()->particle1()->position();
0095     else p1c = spring()->position1();
0096 
0097     if(spring()->rigidBody2()) p2c = spring()->rigidBody2()->position();
0098     else if(spring()->particle2()) p2c = spring()->particle2()->position();
0099     else p2c = spring()->position2();
0100 
0101     painter->drawLine(QPointF(0, 0), vectorToPoint(p1c - spring()->position1()));
0102     painter->drawLine(vectorToPoint(r), vectorToPoint(p2c - spring()->position1()));
0103 
0104     painter->rotate(atan2(r[1], r[0])*180/3.14);
0105 
0106     if(_rscale != 0) {
0107         painter->scale( _rscale, _radius );
0108         int n = int(_rnorm/_rscale) & ~1;
0109         for(int i=1; i<=n; i++) {
0110             painter->drawLine(QLineF( i-1, seq[(i-1)&3], i, seq[i&3] ));
0111         }
0112         painter->drawLine(QLineF(n, seq[n&3], _rnorm/_rscale, 0));
0113         painter->scale( 1/_rscale, 1/_radius );
0114     } else {
0115         painter->drawLine(QLineF( 0, 0, _rnorm, 0 ));
0116     }
0117 
0118     if(isSelected()) {
0119         painter->setRenderHint(QPainter::Antialiasing, true);
0120         painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine));
0121         double m = SELECTION_MARGIN / currentViewScale();
0122         painter->drawRect(QRectF(-m, -_radius-m, _rnorm+m*2,  (_radius+m)*2));
0123     }
0124 }
0125 
0126 void SpringGraphicsItem::viewScaleChanged()
0127 {
0128     prepareGeometryChange();
0129 
0130     StepCore::Vector2d r = spring()->position2() - spring()->position1();
0131     _rnorm = r.norm();
0132     
0133     double sc = _rnorm / spring()->restLength();
0134     if(sc < 1.41) {
0135         _radius = sqrt(2-sc*sc)*RADIUS;
0136         _rscale = sc*RADIUS;
0137         if(_radius < 1) { _rscale = 0; _radius = 1; }
0138     } else { _rscale = 0; _radius = 1; }
0139 
0140     double s = currentViewScale();
0141     double m = (SELECTION_MARGIN+1) / s;
0142     _radius /= s;
0143     _rscale /= s;
0144     
0145     _boundingRect = QRectF(0, 0, r[0], r[1]).normalized();
0146     _boundingRect.adjust(-_radius-m, -_radius-m, _radius+m, _radius+m);
0147 
0148     StepCore::Vector2d p1c, p2c;
0149     if(spring()->rigidBody1()) p1c = spring()->rigidBody1()->position();
0150     else if(spring()->particle1()) p1c = spring()->particle1()->position();
0151     else p1c = spring()->position1();
0152 
0153     if(spring()->rigidBody2()) p2c = spring()->rigidBody2()->position();
0154     else if(spring()->particle2()) p2c = spring()->particle2()->position();
0155     else p2c = spring()->position2();
0156 
0157     _boundingRect |= QRectF(QPoint(0, 0), vectorToPoint(p1c - spring()->position1())).normalized();
0158     _boundingRect |= QRectF(vectorToPoint(r), vectorToPoint(p2c - spring()->position2())).normalized();
0159 
0160     double u = 1/s;
0161     _painterPath.addRect(QRectF(-u, -_radius-u, _rnorm+u, _radius*2+u));
0162     _painterPath = QTransform().rotate(atan2(r[1], r[0])*180/3.14).map(_painterPath);
0163         
0164     //update(); // XXX: documentation says this is unnecessary, but it doesn't work without it
0165 }
0166 
0167 void SpringGraphicsItem::worldDataChanged(bool dynamicOnly)
0168 {
0169     Q_UNUSED(dynamicOnly)
0170     // XXX: TODO do not redraw everything each time
0171     setPos(vectorToPoint(spring()->position1()));
0172     viewScaleChanged();
0173     update();
0174 }
0175 
0176 void SpringGraphicsItem::stateChanged()
0177 {
0178     if(_isSelected) {
0179         _handler1->setVisible(true);
0180         _handler2->setVisible(true);
0181     }
0182     else {
0183         _handler1->setVisible(false);
0184         _handler2->setVisible(false);
0185     }
0186     viewScaleChanged();
0187     update();
0188 }
0189 
0190 void SpringGraphicsItem::mouseSetPos(const QPointF& /*pos*/, const QPointF& diff, MovingState)
0191 {
0192     _worldModel->simulationPause();
0193 
0194     if(spring()->body1()) {
0195         Q_ASSERT(spring()->body1()->metaObject()->inherits<StepCore::Item>());
0196         StepGraphicsItem* gItem = static_cast<WorldScene*>(
0197             scene())->graphicsFromItem(static_cast<StepCore::Item*>(spring()->body1()));
0198         Q_ASSERT(gItem != nullptr);
0199         if(!gItem->isSelected()) {
0200             _worldModel->setProperty(_item, QStringLiteral("localPosition1"),
0201                         _item->metaObject()->property(QStringLiteral("position1"))->readVariant(_item));
0202             _worldModel->setProperty(_item, QStringLiteral("body1"),
0203                         QVariant::fromValue<StepCore::Object*>(NULL), WorldModel::UndoNoMerge);
0204         }
0205     } else {
0206         _worldModel->setProperty(_item, QStringLiteral("localPosition1"), 
0207             QVariant::fromValue( (spring()->position1() + pointToVector(diff)).eval() ));
0208     }
0209 
0210     if(spring()->body2()) {
0211         Q_ASSERT(spring()->body2()->metaObject()->inherits<StepCore::Item>());
0212         StepGraphicsItem* gItem = static_cast<WorldScene*>(
0213             scene())->graphicsFromItem(static_cast<StepCore::Item*>(spring()->body2()));
0214         Q_ASSERT(gItem != nullptr);
0215         if(!gItem->isSelected()) {
0216             _worldModel->setProperty(_item, QStringLiteral("localPosition2"),
0217                         _item->metaObject()->property(QStringLiteral("position2"))->readVariant(_item));
0218             _worldModel->setProperty(_item, QStringLiteral("body2"), QString(), WorldModel::UndoNoMerge);
0219         }
0220     } else {
0221         _worldModel->setProperty(_item, QStringLiteral("localPosition2"),
0222             QVariant::fromValue( (spring()->position2() + pointToVector(diff)).eval() ));
0223     }
0224 }