|
|
|
/***************************************************************************
|
|
|
|
* 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 <tdelocale.h>
|
|
|
|
#include <tqbitarray.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqwmatrix.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 TQString &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, TQT_SIGNAL(moved()), this, TQT_SLOT(parentMoved()) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newMechItem)
|
|
|
|
{
|
|
|
|
m_relativePosition = m_relativePosition - newMechItem->absolutePosition();
|
|
|
|
connect( newMechItem, TQT_SIGNAL(moved()), this, TQT_SLOT(parentMoved()) );
|
|
|
|
}
|
|
|
|
|
|
|
|
updateCanvasPoints();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MechanicsItem::childAdded( Item *child )
|
|
|
|
{
|
|
|
|
MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(child);
|
|
|
|
if (!mechItem)
|
|
|
|
return;
|
|
|
|
|
|
|
|
connect( mechItem, TQT_SIGNAL(updateMechanicsInfoCombined()), this, TQT_SLOT(childMoved()) );
|
|
|
|
updateMechanicsInfoCombined();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MechanicsItem::childRemoved( Item *child )
|
|
|
|
{
|
|
|
|
MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(child);
|
|
|
|
if (!mechItem)
|
|
|
|
return;
|
|
|
|
|
|
|
|
disconnect( mechItem, TQT_SIGNAL(updateMechanicsInfoCombined()), this, TQT_SLOT(childMoved()) );
|
|
|
|
updateMechanicsInfoCombined();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MechanicsItem::parentMoved()
|
|
|
|
{
|
|
|
|
PositionInfo absPos = absolutePosition();
|
|
|
|
Item::moveBy( absPos.x() - x(), absPos.y() - y() );
|
|
|
|
updateCanvasPoints();
|
|
|
|
emit moved();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MechanicsItem::updateCanvasPoints()
|
|
|
|
{
|
|
|
|
const TQRect 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();
|
|
|
|
|
|
|
|
TQWMatrix 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) );
|
|
|
|
|
|
|
|
TQRect 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()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQRect MechanicsItem::maxInnerRectangle( const TQRect &outerRect ) const
|
|
|
|
{
|
|
|
|
TQRect 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 TQRect( int(left), int(top), int(width), int(height) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MechanicsItem::initPainter( TQPainter &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( TQPainter &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"
|