File indexing completed on 2024-12-01 08:18:30
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"