Logo Search packages:      
Sourcecode: ktechlab version File versions  Download package

mechanicsitem.cpp

/***************************************************************************
 *   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>

#define DPR ( 180.0 / M_PI )

/**
@returns an angle between 0 and 2 pi
*/
double normalizeAngle( double angle )
{
      if ( angle < 0 )
            angle += 2 * M_PI *(std::ceil(-angle));
      return angle - 2 * M_PI * std::floor(angle/(2 * M_PI));
}

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()
{
}


00067 void MechanicsItem::setSelectionMode( SelectionMode sm )
{
      if ( sm == m_selectionMode )
            return;
      
      m_selectionMode = sm;
}


00076 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();
}


00098 PositionInfo MechanicsItem::absolutePosition() const
{
      MechanicsItem *parentMechItem = dynamic_cast<MechanicsItem*>((Item*)(p_parentItem));
      if (parentMechItem)
            return parentMechItem->absolutePosition() + m_relativePosition;
      
      return m_relativePosition;
}


00108 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();
}


00129 void MechanicsItem::childAdded( Item *child )
{
      MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(child);
      if (!mechItem)
            return;
      
      connect( mechItem, SIGNAL(updateMechanicsInfoCombined()), this, SLOT(childMoved()) );
      updateMechanicsInfoCombined();
}


00140 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();
}


00160 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() * DPR);
      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);
}


00180 void MechanicsItem::rotateBy( double dtheta )
{
      m_relativePosition.rotate(dtheta);
      updateCanvasPoints();
      updateMechanicsInfoCombined();
      emit moved();
}


00189 void MechanicsItem::moveBy( double dx, double dy )
{
      m_relativePosition.translate( dx, dy );
      Item::moveBy( m_relativePosition.x() - x(), m_relativePosition.y() - y() );
      emit moved();
}


00197 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() * DPR;
      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()
{
}


00285 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) < M_PI )
            {
                  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) );
}


00332 void MechanicsItem::initPainter( QPainter &p )
{
       PositionInfo absPos = absolutePosition();
       p.translate( absPos.x(), absPos.y() );
       p.rotate( absPos.angle() * DPR);
       p.translate( -absPos.x(), -absPos.y() );
}


00341 void MechanicsItem::deinitPainter( QPainter &p )
{
       PositionInfo absPos = absolutePosition();
       p.translate( absPos.x(), absPos.y() );
       p.rotate( -absPos.angle() * DPR);
       p.translate( -absPos.x(), -absPos.y() );
}

PositionInfo::PositionInfo()
{
      reset();
}


00355 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;
}


00370 const PositionInfo PositionInfo::operator-( const PositionInfo &info )
{

      PositionInfo newInfo = *this;
      
      newInfo.translate( -info.x(), -info.y() );
      newInfo.rotate( -info.angle() );
      
      return newInfo;
}


00382 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;
}


00394 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"

Generated by  Doxygen 1.6.0   Back to index