diff options
Diffstat (limited to 'src/mechanics/mechanicsitem.cpp')
-rw-r--r-- | src/mechanics/mechanicsitem.cpp | 433 |
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 * + * [email protected] * + * * + * 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" |