File indexing completed on 2024-06-09 09:27:47

0001 /***************************************************************************
0002  *   Copyright (C) 2004-2005 by David Saxton                               *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "mechanicsitem.h"
0012 #include "itemdocumentdata.h"
0013 #include "mechanicsdocument.h"
0014 
0015 #include <KLocalizedString>
0016 
0017 #include <QBitArray>
0018 #include <QDebug>
0019 #include <QPainter>
0020 #include <cmath>
0021 
0022 #define DPR (180.0 / M_PI)
0023 
0024 /**
0025 @returns an angle between 0 and 2 pi
0026 */
0027 double normalizeAngle(double angle)
0028 {
0029     if (angle < 0)
0030         angle += 2 * M_PI * (std::ceil(-angle));
0031     return angle - 2 * M_PI * std::floor(angle / (2 * M_PI));
0032 }
0033 
0034 MechanicsItem::MechanicsItem(MechanicsDocument *mechanicsDocument, bool newItem, const QString &id)
0035     : Item(mechanicsDocument, newItem, id)
0036 {
0037     p_mechanicsDocument = mechanicsDocument;
0038     m_selectionMode = MechanicsItem::sm_move;
0039 
0040     createProperty("mass", Variant::Type::Double);
0041     property("mass")->setCaption(i18n("Mass"));
0042     property("mass")->setUnit("g");
0043     property("mass")->setValue(10.0);
0044     property("mass")->setMinValue(1e-3);
0045     property("mass")->setMaxValue(1e12);
0046     property("mass")->setAdvanced(true);
0047 
0048     createProperty("moi", Variant::Type::Double);
0049     property("moi")->setCaption(i18n("Moment of Inertia"));
0050     property("moi")->setUnit("gm");
0051     property("moi")->setValue(0.01);
0052     property("moi")->setMinValue(1e-3);
0053     property("moi")->setMaxValue(1e12);
0054     property("moi")->setAdvanced(true);
0055 
0056     setZ(ItemDocument::Z::Item);
0057     //  setAnimated(true);
0058     p_mechanicsDocument->registerItem(this);
0059 }
0060 
0061 MechanicsItem::~MechanicsItem()
0062 {
0063 }
0064 
0065 void MechanicsItem::setSelectionMode(SelectionMode sm)
0066 {
0067     if (sm == m_selectionMode)
0068         return;
0069 
0070     m_selectionMode = sm;
0071 }
0072 
0073 void MechanicsItem::setSelected(bool yes)
0074 {
0075     if (yes == isSelected())
0076         return;
0077 
0078     if (!yes)
0079         // Reset the selection mode
0080         m_selectionMode = MechanicsItem::sm_resize;
0081 
0082     Item::setSelected(yes);
0083 }
0084 
0085 void MechanicsItem::dataChanged()
0086 {
0087     Item::dataChanged();
0088     m_mechanicsInfo.mass = dataDouble("mass");
0089     m_mechanicsInfo.momentOfInertia = dataDouble("moi");
0090     updateMechanicsInfoCombined();
0091 }
0092 
0093 PositionInfo MechanicsItem::absolutePosition() const
0094 {
0095     MechanicsItem *parentMechItem = dynamic_cast<MechanicsItem *>(static_cast<Item *>(p_parentItem));
0096     if (parentMechItem)
0097         return parentMechItem->absolutePosition() + m_relativePosition;
0098 
0099     return m_relativePosition;
0100 }
0101 
0102 void MechanicsItem::reparented(Item *oldItem, Item *newItem)
0103 {
0104     MechanicsItem *oldMechItem = dynamic_cast<MechanicsItem *>(oldItem);
0105     MechanicsItem *newMechItem = dynamic_cast<MechanicsItem *>(newItem);
0106 
0107     if (oldMechItem) {
0108         m_relativePosition = oldMechItem->absolutePosition() + m_relativePosition;
0109         disconnect(oldMechItem, SIGNAL(moved()), this, SLOT(parentMoved()));
0110     }
0111 
0112     if (newMechItem) {
0113         m_relativePosition = m_relativePosition - newMechItem->absolutePosition();
0114         connect(newMechItem, SIGNAL(moved()), this, SLOT(parentMoved()));
0115     }
0116 
0117     updateCanvasPoints();
0118 }
0119 
0120 void MechanicsItem::childAdded(Item *child)
0121 {
0122     MechanicsItem *mechItem = dynamic_cast<MechanicsItem *>(child);
0123     if (!mechItem)
0124         return;
0125 
0126     connect(mechItem, SIGNAL(updateMechanicsInfoCombined()), this, SLOT(childMoved()));
0127     updateMechanicsInfoCombined();
0128 }
0129 
0130 void MechanicsItem::childRemoved(Item *child)
0131 {
0132     MechanicsItem *mechItem = dynamic_cast<MechanicsItem *>(child);
0133     if (!mechItem)
0134         return;
0135 
0136     disconnect(mechItem, SIGNAL(updateMechanicsInfoCombined()), this, SLOT(childMoved()));
0137     updateMechanicsInfoCombined();
0138 }
0139 
0140 void MechanicsItem::parentMoved()
0141 {
0142     PositionInfo absPos = absolutePosition();
0143     Item::moveBy(absPos.x() - x(), absPos.y() - y());
0144     updateCanvasPoints();
0145     emit moved();
0146 }
0147 
0148 void MechanicsItem::updateCanvasPoints()
0149 {
0150     const QRect ipbr = m_itemPoints.boundingRect();
0151 
0152     double scalex = double(m_sizeRect.width()) / double(ipbr.width());
0153     double scaley = double(m_sizeRect.height()) / double(ipbr.height());
0154 
0155     PositionInfo abs = absolutePosition();
0156 
0157     QTransform m;
0158     m.rotate(abs.angle() * DPR);
0159     m.translate(m_sizeRect.left(), m_sizeRect.top());
0160     m.scale(scalex, scaley);
0161     m.translate(-int(ipbr.left()), -int(ipbr.top()));
0162     setPoints(m.map(m_itemPoints));
0163 
0164     // QRect tempt = m.mapRect(ipbr); // 2017.10.01 - comment out unused variable
0165 }
0166 
0167 void MechanicsItem::rotateBy(double dtheta)
0168 {
0169     m_relativePosition.rotate(dtheta);
0170     updateCanvasPoints();
0171     updateMechanicsInfoCombined();
0172     emit moved();
0173 }
0174 
0175 void MechanicsItem::moveBy(double dx, double dy)
0176 {
0177     m_relativePosition.translate(dx, dy);
0178     Item::moveBy(m_relativePosition.x() - x(), m_relativePosition.y() - y());
0179     emit moved();
0180 }
0181 
0182 void MechanicsItem::updateMechanicsInfoCombined()
0183 {
0184     m_mechanicsInfoCombined = m_mechanicsInfo;
0185 
0186     double mass_x = 0.;
0187     double mass_y = 0.;
0188 
0189     const ItemList::const_iterator end = m_children.end();
0190     for (ItemList::const_iterator it = m_children.begin(); it != end; ++it) {
0191         MechanicsItem *child = dynamic_cast<MechanicsItem *>(static_cast<Item *>(*it));
0192         if (child) {
0193             CombinedMechanicsInfo *childInfo = child->mechanicsInfoCombined();
0194             const PositionInfo relativeChildPosition = child->relativePosition();
0195 
0196             double mass = childInfo->mass;
0197             //          double angle = relativeChildPosition.angle();
0198             double dx = relativeChildPosition.x() /*+ cos(angle)*childInfo->m_x - sin(angle)*childInfo->m_y*/;
0199             double dy = relativeChildPosition.y() /*+ sin(angle)*childInfo->m_x + cos(angle)*childInfo->m_y*/;
0200 
0201             m_mechanicsInfoCombined.mass += mass;
0202             mass_x += mass * dx;
0203             mass_y += mass * dy;
0204 
0205             double length_squared = dx * dx + dy * dy;
0206             m_mechanicsInfoCombined.momentOfInertia += length_squared * childInfo->momentOfInertia;
0207         }
0208     }
0209 
0210     m_mechanicsInfoCombined.x = mass_x / m_mechanicsInfoCombined.mass;
0211     m_mechanicsInfoCombined.y = mass_y / m_mechanicsInfoCombined.mass;
0212 }
0213 
0214 ItemData MechanicsItem::itemData() const
0215 {
0216     ItemData itemData = Item::itemData();
0217     itemData.angleDegrees = m_relativePosition.angle() * DPR;
0218     return itemData;
0219 }
0220 
0221 bool MechanicsItem::mousePressEvent(const EventInfo &eventInfo)
0222 {
0223     Q_UNUSED(eventInfo);
0224     return false;
0225 }
0226 
0227 bool MechanicsItem::mouseReleaseEvent(const EventInfo &eventInfo)
0228 {
0229     Q_UNUSED(eventInfo);
0230     return false;
0231 }
0232 
0233 bool MechanicsItem::mouseDoubleClickEvent(const EventInfo &eventInfo)
0234 {
0235     Q_UNUSED(eventInfo);
0236     return false;
0237 }
0238 
0239 bool MechanicsItem::mouseMoveEvent(const EventInfo &eventInfo)
0240 {
0241     Q_UNUSED(eventInfo);
0242     return false;
0243 }
0244 
0245 bool MechanicsItem::wheelEvent(const EventInfo &eventInfo)
0246 {
0247     Q_UNUSED(eventInfo);
0248     return false;
0249 }
0250 
0251 void MechanicsItem::enterEvent(QEvent *)
0252 {
0253 }
0254 
0255 void MechanicsItem::leaveEvent(QEvent *)
0256 {
0257 }
0258 
0259 QRect MechanicsItem::maxInnerRectangle(const QRect &outerRect) const
0260 {
0261     QRect normalizedOuterRect = outerRect.normalized();
0262     const double LEFT = normalizedOuterRect.left();
0263     const double TOP = normalizedOuterRect.top();
0264     const double X = normalizedOuterRect.width();
0265     const double Y = normalizedOuterRect.height();
0266     const double a = normalizeAngle(absolutePosition().angle());
0267 
0268     double left;
0269     double top;
0270     double width;
0271     double height;
0272 
0273     //  if ( can change width/height ratio )
0274     {
0275         double x1 = X * std::cos(a) - Y * std::sin(a);
0276         double y1 = X * std::sin(a) + Y * std::cos(a);
0277         double x2 = X * std::cos(a);
0278         double y2 = X * std::sin(a);
0279         double x3 = -Y * std::sin(a);
0280         double y3 = Y * std::cos(a);
0281 
0282         double xbig; /* = std::max( std::abs(x2-x3), std::abs(x1) );*/
0283         double ybig; /* = std::max( std::abs(y2-y3), std::abs(y1) );*/
0284         if ((a - floor(a / 6.2832) * 6.2832) < M_PI) {
0285             xbig = std::abs(x3 - x2);
0286             ybig = std::abs(y1);
0287         } else {
0288             xbig = std::abs(x1);
0289             ybig = std::abs(y3 - y2);
0290         }
0291 
0292         width = X * (X / xbig);
0293         height = Y * (Y / ybig);
0294 
0295         top = -std::sin(a) * (LEFT + width * std::sin(a)) + std::cos(a) * TOP;
0296         left = std::cos(a) * (LEFT + width * std::sin(a)) + std::sin(a) * TOP;
0297     }
0298 
0299     return QRect(int(left), int(top), int(width), int(height));
0300 }
0301 
0302 void MechanicsItem::initPainter(QPainter &p)
0303 {
0304     PositionInfo absPos = absolutePosition();
0305     p.translate(absPos.x(), absPos.y());
0306     p.rotate(absPos.angle() * DPR);
0307     p.translate(-absPos.x(), -absPos.y());
0308 }
0309 
0310 void MechanicsItem::deinitPainter(QPainter &p)
0311 {
0312     PositionInfo absPos = absolutePosition();
0313     p.translate(absPos.x(), absPos.y());
0314     p.rotate(-absPos.angle() * DPR);
0315     p.translate(-absPos.x(), -absPos.y());
0316 }
0317 
0318 PositionInfo::PositionInfo()
0319 {
0320     reset();
0321 }
0322 
0323 const PositionInfo PositionInfo::operator+(const PositionInfo &info)
0324 {
0325     // Copy the child to a new position
0326     PositionInfo newInfo = info;
0327 
0328     // Translate the newInfo by our translation amount
0329     newInfo.translate(x(), y());
0330 
0331     // Rotate the child about us
0332     newInfo.rotateAboutPoint(x(), y(), angle());
0333 
0334     return newInfo;
0335 }
0336 
0337 const PositionInfo PositionInfo::operator-(const PositionInfo &info)
0338 {
0339     PositionInfo newInfo = *this;
0340 
0341     newInfo.translate(-info.x(), -info.y());
0342     newInfo.rotate(-info.angle());
0343 
0344     return newInfo;
0345 }
0346 
0347 void PositionInfo::rotateAboutPoint(double x, double y, double angle)
0348 {
0349     m_angle += angle;
0350 
0351     double newx = x + (m_x - x) * std::cos(angle) - (m_y - y) * std::sin(angle);
0352     double newy = y + (m_x - x) * std::sin(angle) + (m_y - y) * std::cos(angle);
0353 
0354     m_x = newx;
0355     m_y = newy;
0356 }
0357 
0358 void PositionInfo::reset()
0359 {
0360     m_x = 0.;
0361     m_y = 0.;
0362     m_angle = 0.;
0363 }
0364 
0365 MechanicsInfo::MechanicsInfo()
0366 {
0367     mass = 0.;
0368     momentOfInertia = 0.;
0369 }
0370 CombinedMechanicsInfo::CombinedMechanicsInfo()
0371     : MechanicsInfo()
0372 {
0373     x = 0.;
0374     y = 0.;
0375 }
0376 CombinedMechanicsInfo::CombinedMechanicsInfo(const MechanicsInfo &info)
0377     : MechanicsInfo(info)
0378 {
0379     x = 0.;
0380     y = 0.;
0381 }
0382 
0383 #include "moc_mechanicsitem.cpp"