/*************************************************************************** * 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 #include #include #include #include #include /** @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((Item*)(p_parentItem)); if (parentMechItem) return parentMechItem->absolutePosition() + m_relativePosition; return m_relativePosition; } void MechanicsItem::reparented( Item *oldItem, Item *newItem ) { MechanicsItem *oldMechItem = dynamic_cast(oldItem); MechanicsItem *newMechItem = dynamic_cast(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(child); if (!mechItem) return; connect( mechItem, SIGNAL(updateMechanicsInfoCombined()), this, SLOT(childMoved()) ); updateMechanicsInfoCombined(); } void MechanicsItem::childRemoved( Item *child ) { MechanicsItem *mechItem = dynamic_cast(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((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"