summaryrefslogtreecommitdiffstats
path: root/src/mechanics/mechanicsitem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mechanics/mechanicsitem.cpp')
-rw-r--r--src/mechanics/mechanicsitem.cpp433
1 files changed, 433 insertions, 0 deletions
diff --git a/src/mechanics/mechanicsitem.cpp b/src/mechanics/mechanicsitem.cpp
new file mode 100644
index 0000000..acf2239
--- /dev/null
+++ b/src/mechanics/mechanicsitem.cpp
@@ -0,0 +1,433 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by David Saxton *
+ * david@bluehaze.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+
+#include "itemdocumentdata.h"
+#include "mechanicsitem.h"
+#include "mechanicsdocument.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qbitarray.h>
+#include <qpainter.h>
+#include <qwmatrix.h>
+#include <cmath>
+
+/**
+@returns an angle between 0 and 2 pi
+*/
+double normalizeAngle( double angle )
+{
+ if ( angle < 0 )
+ angle += 6.2832*(std::ceil(-angle));
+
+ return angle - 6.2832*std::floor(angle/6.2832);
+}
+
+MechanicsItem::MechanicsItem( MechanicsDocument *mechanicsDocument, bool newItem, const QString &id )
+ : Item( mechanicsDocument, newItem, id )
+{
+ p_mechanicsDocument = mechanicsDocument;
+ m_selectionMode = MechanicsItem::sm_move;
+
+ createProperty( "mass", Variant::Type::Double );
+ property("mass")->setCaption( i18n("Mass") );
+ property("mass")->setUnit("g");
+ property("mass")->setValue(10.0);
+ property("mass")->setMinValue(1e-3);
+ property("mass")->setMaxValue(1e12);
+ property("mass")->setAdvanced(true);
+
+ createProperty( "moi", Variant::Type::Double );
+ property("moi")->setCaption( i18n("Moment of Inertia") );
+ property("moi")->setUnit("gm");
+ property("moi")->setValue(0.01);
+ property("moi")->setMinValue(1e-3);
+ property("moi")->setMaxValue(1e12);
+ property("moi")->setAdvanced(true);
+
+ setZ(ItemDocument::Z::Item);
+ setAnimated(true);
+ p_mechanicsDocument->registerItem(this);
+}
+
+
+MechanicsItem::~MechanicsItem()
+{
+}
+
+
+int MechanicsItem::rtti() const
+{
+ return ItemDocument::RTTI::MechanicsItem;
+}
+
+
+void MechanicsItem::setSelectionMode( SelectionMode sm )
+{
+ if ( sm == m_selectionMode )
+ return;
+
+ m_selectionMode = sm;
+}
+
+
+void MechanicsItem::setSelected( bool yes )
+{
+ if ( yes == isSelected() )
+ return;
+
+ if (!yes)
+ // Reset the selection mode
+ m_selectionMode = MechanicsItem::sm_resize;
+
+ Item::setSelected(yes);
+}
+
+
+void MechanicsItem::dataChanged()
+{
+ Item::dataChanged();
+ m_mechanicsInfo.mass = dataDouble("mass");
+ m_mechanicsInfo.momentOfInertia = dataDouble("moi");
+ updateMechanicsInfoCombined();
+}
+
+
+PositionInfo MechanicsItem::absolutePosition() const
+{
+ MechanicsItem *parentMechItem = dynamic_cast<MechanicsItem*>((Item*)(p_parentItem));
+ if (parentMechItem)
+ return parentMechItem->absolutePosition() + m_relativePosition;
+
+ return m_relativePosition;
+}
+
+
+void MechanicsItem::reparented( Item *oldItem, Item *newItem )
+{
+ MechanicsItem *oldMechItem = dynamic_cast<MechanicsItem*>(oldItem);
+ MechanicsItem *newMechItem = dynamic_cast<MechanicsItem*>(newItem);
+
+ if (oldMechItem)
+ {
+ m_relativePosition = oldMechItem->absolutePosition() + m_relativePosition;
+ disconnect( oldMechItem, SIGNAL(moved()), this, SLOT(parentMoved()) );
+ }
+
+ if (newMechItem)
+ {
+ m_relativePosition = m_relativePosition - newMechItem->absolutePosition();
+ connect( newMechItem, SIGNAL(moved()), this, SLOT(parentMoved()) );
+ }
+
+ updateCanvasPoints();
+}
+
+
+void MechanicsItem::childAdded( Item *child )
+{
+ MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(child);
+ if (!mechItem)
+ return;
+
+ connect( mechItem, SIGNAL(updateMechanicsInfoCombined()), this, SLOT(childMoved()) );
+ updateMechanicsInfoCombined();
+}
+
+
+void MechanicsItem::childRemoved( Item *child )
+{
+ MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(child);
+ if (!mechItem)
+ return;
+
+ disconnect( mechItem, SIGNAL(updateMechanicsInfoCombined()), this, SLOT(childMoved()) );
+ updateMechanicsInfoCombined();
+}
+
+
+void MechanicsItem::parentMoved()
+{
+ PositionInfo absPos = absolutePosition();
+ Item::moveBy( absPos.x() - x(), absPos.y() - y() );
+ updateCanvasPoints();
+ emit moved();
+}
+
+
+void MechanicsItem::updateCanvasPoints()
+{
+ const QRect ipbr = m_itemPoints.boundingRect();
+
+ double scalex = double(m_sizeRect.width()) / double(ipbr.width());
+ double scaley = double(m_sizeRect.height()) / double(ipbr.height());
+
+ PositionInfo abs = absolutePosition();
+
+ QWMatrix m;
+ m.rotate(abs.angle()*57.29577951308232);
+ m.translate( m_sizeRect.left(), m_sizeRect.top() );
+ m.scale( scalex, scaley );
+ m.translate( -int(ipbr.left()), -int(ipbr.top()) );
+ setPoints( m.map(m_itemPoints) );
+
+ QRect tempt = m.mapRect(ipbr);
+}
+
+
+void MechanicsItem::rotateBy( double dtheta )
+{
+ m_relativePosition.rotate(dtheta);
+ updateCanvasPoints();
+ updateMechanicsInfoCombined();
+ emit moved();
+}
+
+
+void MechanicsItem::moveBy( double dx, double dy )
+{
+ m_relativePosition.translate( dx, dy );
+ Item::moveBy( m_relativePosition.x() - x(), m_relativePosition.y() - y() );
+ emit moved();
+}
+
+
+void MechanicsItem::updateMechanicsInfoCombined()
+{
+ m_mechanicsInfoCombined = m_mechanicsInfo;
+
+ double mass_x = 0.;
+ double mass_y = 0.;
+
+ const ItemList::const_iterator end = m_children.end();
+ for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it )
+ {
+ MechanicsItem *child = dynamic_cast<MechanicsItem*>((Item*)*it);
+ if (child)
+ {
+ CombinedMechanicsInfo *childInfo = child->mechanicsInfoCombined();
+ const PositionInfo relativeChildPosition = child->relativePosition();
+
+ double mass = childInfo->mass;
+// double angle = relativeChildPosition.angle();
+ double dx = relativeChildPosition.x() /*+ cos(angle)*childInfo->m_x - sin(angle)*childInfo->m_y*/;
+ double dy = relativeChildPosition.y() /*+ sin(angle)*childInfo->m_x + cos(angle)*childInfo->m_y*/;
+
+ m_mechanicsInfoCombined.mass += mass;
+ mass_x += mass * dx;
+ mass_y += mass * dy;
+
+ double length_squared = dx*dx + dy*dy;
+ m_mechanicsInfoCombined.momentOfInertia += length_squared * childInfo->momentOfInertia;
+ }
+ }
+
+ m_mechanicsInfoCombined.x = mass_x / m_mechanicsInfoCombined.mass;
+ m_mechanicsInfoCombined.y = mass_y / m_mechanicsInfoCombined.mass;
+}
+
+
+ItemData MechanicsItem::itemData() const
+{
+ ItemData itemData = Item::itemData();
+ itemData.angleDegrees = m_relativePosition.angle()*57.29577951308232;
+ return itemData;
+}
+
+
+bool MechanicsItem::mousePressEvent( const EventInfo &eventInfo )
+{
+ Q_UNUSED(eventInfo);
+ return false;
+}
+
+
+bool MechanicsItem::mouseReleaseEvent( const EventInfo &eventInfo )
+{
+ Q_UNUSED(eventInfo);
+ return false;
+}
+
+
+bool MechanicsItem::mouseDoubleClickEvent( const EventInfo &eventInfo )
+{
+ Q_UNUSED(eventInfo);
+ return false;
+}
+
+
+bool MechanicsItem::mouseMoveEvent( const EventInfo &eventInfo )
+{
+ Q_UNUSED(eventInfo);
+ return false;
+}
+
+
+bool MechanicsItem::wheelEvent( const EventInfo &eventInfo )
+{
+ Q_UNUSED(eventInfo);
+ return false;
+}
+
+
+void MechanicsItem::enterEvent()
+{
+}
+
+
+void MechanicsItem::leaveEvent()
+{
+}
+
+
+QRect MechanicsItem::maxInnerRectangle( const QRect &outerRect ) const
+{
+ QRect normalizedOuterRect = outerRect.normalize();
+ const double LEFT = normalizedOuterRect.left();
+ const double TOP = normalizedOuterRect.top();
+ const double X = normalizedOuterRect.width();
+ const double Y = normalizedOuterRect.height();
+ const double a = normalizeAngle(absolutePosition().angle());
+
+ double left;
+ double top;
+ double width;
+ double height;
+
+// if ( can change width/height ratio )
+ {
+ double x1 = X*std::cos(a) - Y*std::sin(a);
+ double y1 = X*std::sin(a) + Y*std::cos(a);
+ double x2 = X*std::cos(a);
+ double y2 = X*std::sin(a);
+ double x3 = -Y*std::sin(a);
+ double y3 = Y*std::cos(a);
+
+ double xbig;/* = std::max( std::abs(x2-x3), std::abs(x1) );*/
+ double ybig;/* = std::max( std::abs(y2-y3), std::abs(y1) );*/
+ if ( (a - floor(a/6.2832)*6.2832) < 3.1416 )
+ {
+ xbig = std::abs(x3-x2);
+ ybig = std::abs(y1);
+ }
+ else
+ {
+ xbig = std::abs(x1);
+ ybig = std::abs(y3-y2);
+ }
+
+ width = X*(X/xbig);
+ height = Y*(Y/ybig);
+
+ top = -std::sin(a) * (LEFT + width*std::sin(a)) + std::cos(a)*TOP;
+ left = std::cos(a) * (LEFT + width*std::sin(a)) + std::sin(a)*TOP;
+ }
+
+ return QRect( int(left), int(top), int(width), int(height) );
+}
+
+
+void MechanicsItem::initPainter( QPainter &p )
+{
+ PositionInfo absPos = absolutePosition();
+ p.translate( absPos.x(), absPos.y() );
+ // 57.29577951308232 is the number of degrees per radian.
+ p.rotate( absPos.angle()*57.29577951308232 );
+ p.translate( -absPos.x(), -absPos.y() );
+}
+
+
+void MechanicsItem::deinitPainter( QPainter &p )
+{
+ PositionInfo absPos = absolutePosition();
+ p.translate( absPos.x(), absPos.y() );
+ // 57.29577951308232 is the number of degrees per radian.
+ p.rotate( -absPos.angle()*57.29577951308232 );
+ p.translate( -absPos.x(), -absPos.y() );
+}
+
+
+
+
+
+PositionInfo::PositionInfo()
+{
+ reset();
+}
+
+
+const PositionInfo PositionInfo::operator+( const PositionInfo &info )
+{
+ // Copy the child to a new position
+ PositionInfo newInfo = info;
+
+ // Translate the newInfo by our translation amount
+ newInfo.translate( x(), y() );
+
+ // Rotate the child about us
+ newInfo.rotateAboutPoint( x(), y(), angle() );
+
+ return newInfo;
+}
+
+
+const PositionInfo PositionInfo::operator-( const PositionInfo &info )
+{
+
+ PositionInfo newInfo = *this;
+
+ newInfo.translate( -info.x(), -info.y() );
+ newInfo.rotate( -info.angle() );
+
+ return newInfo;
+}
+
+
+void PositionInfo::rotateAboutPoint( double x, double y, double angle )
+{
+ m_angle += angle;
+
+ double newx = x + (m_x-x)*std::cos(angle) - (m_y-y)*std::sin(angle);
+ double newy = y + (m_x-x)*std::sin(angle) + (m_y-y)*std::cos(angle);
+
+ m_x = newx;
+ m_y = newy;
+}
+
+
+void PositionInfo::reset()
+{
+ m_x = 0.;
+ m_y = 0.;
+ m_angle = 0.;
+}
+
+
+
+MechanicsInfo::MechanicsInfo()
+{
+ mass = 0.;
+ momentOfInertia = 0.;
+}
+CombinedMechanicsInfo::CombinedMechanicsInfo()
+ : MechanicsInfo()
+{
+ x = 0.;
+ y = 0.;
+}
+CombinedMechanicsInfo::CombinedMechanicsInfo( const MechanicsInfo &info )
+ : MechanicsInfo(info)
+{
+ x = 0.;
+ y = 0.;
+}
+
+
+#include "mechanicsitem.moc"