summaryrefslogtreecommitdiffstats
path: root/src/mechanics
diff options
context:
space:
mode:
Diffstat (limited to 'src/mechanics')
-rw-r--r--src/mechanics/Makefile.am8
-rw-r--r--src/mechanics/chassiscircular2.cpp144
-rw-r--r--src/mechanics/chassiscircular2.h42
-rw-r--r--src/mechanics/mechanicsdocument.cpp197
-rw-r--r--src/mechanics/mechanicsdocument.h66
-rw-r--r--src/mechanics/mechanicsgroup.cpp243
-rw-r--r--src/mechanics/mechanicsgroup.h72
-rw-r--r--src/mechanics/mechanicsitem.cpp433
-rw-r--r--src/mechanics/mechanicsitem.h237
-rw-r--r--src/mechanics/mechanicssimulation.cpp139
-rw-r--r--src/mechanics/mechanicssimulation.h133
-rw-r--r--src/mechanics/mechanicsview.cpp42
-rw-r--r--src/mechanics/mechanicsview.h33
13 files changed, 1789 insertions, 0 deletions
diff --git a/src/mechanics/Makefile.am b/src/mechanics/Makefile.am
new file mode 100644
index 0000000..0444358
--- /dev/null
+++ b/src/mechanics/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/drawparts $(all_includes)
+METASOURCES = AUTO
+noinst_HEADERS = mechanicsitem.h chassiscircular2.h mechanicssimulation.h \
+ mechanicsdocument.h mechanicsgroup.h mechanicsview.h
+
+noinst_LTLIBRARIES = libmechanics.la
+libmechanics_la_SOURCES = mechanicsitem.cpp chassiscircular2.cpp \
+ mechanicssimulation.cpp mechanicsdocument.cpp mechanicsgroup.cpp mechanicsview.cpp
diff --git a/src/mechanics/chassiscircular2.cpp b/src/mechanics/chassiscircular2.cpp
new file mode 100644
index 0000000..91b9f0a
--- /dev/null
+++ b/src/mechanics/chassiscircular2.cpp
@@ -0,0 +1,144 @@
+/***************************************************************************
+ * Copyright (C) 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 "chassiscircular2.h"
+
+#include "libraryitem.h"
+
+#include <klocale.h>
+#include <qpainter.h>
+#include <qwmatrix.h>
+
+#include <algorithm>
+#include <cmath>
+
+double normalizeAngle( double angle );
+
+
+Item* ChassisCircular2::construct( ItemDocument *itemDocument, bool newItem, const char *id )
+{
+ return new ChassisCircular2( (MechanicsDocument*)itemDocument, newItem, id );
+}
+
+
+LibraryItem* ChassisCircular2::libraryItem()
+{
+ return new LibraryItem(
+ QString("mech/chassis_circular_2"),
+ i18n("Circular 2-Wheel Chassis"),
+ i18n("Chassis'"),
+ "chassis.png",
+ LibraryItem::lit_mechanical,
+ ChassisCircular2::construct );
+}
+
+
+ChassisCircular2::ChassisCircular2( MechanicsDocument *mechanicsDocument, bool newItem, const char *id )
+ : MechanicsItem( mechanicsDocument, newItem, (id) ? id : "chassis_circular_2" )
+{
+ m_name = i18n("Circular 2-Wheel Chassis");
+ m_desc = i18n("A circular base with two wheels and a support point.");
+
+ m_theta1 = 0.0;
+ m_theta2 = 0.0;
+
+ QPointArray pa;
+ pa.makeEllipse( -25, -25, 50, 50 );
+ QWMatrix m(4,0,0,4,0,0);
+ m.setTransformationMode( QWMatrix::Areas );
+ pa = m.map(pa);
+ setItemPoints(pa);
+
+ itemResized();
+}
+
+
+ChassisCircular2::~ChassisCircular2()
+{
+}
+
+
+void ChassisCircular2::itemResized()
+{
+ const double w = sizeRect().width();
+ const double h = sizeRect().height();
+
+ m_wheel1Pos = QRect( int(w/5), int(h/6), int(w/4), int(h/8) );
+ m_wheel2Pos = QRect( int(w/5), int(5*h/6-h/8), int(w/4), int(h/8) );
+}
+
+
+void ChassisCircular2::advance( int phase )
+{
+ if ( phase != 1 )
+ return;
+
+ double speed1 = 60.; // pixels per second
+ double speed2 = 160.; // pixels per second
+
+ m_theta1 = normalizeAngle( m_theta1 + (speed1/1000.)/m_wheel1Pos.width() );
+ m_theta2 = normalizeAngle( m_theta2 + (speed2/1000.)/m_wheel2Pos.width() );
+
+ const double d1 = speed1/1000.;
+ const double d2 = speed2/1000.;
+ const double sep = m_wheel2Pos.center().y()-m_wheel1Pos.center().y();
+
+ double dtheta = std::atan( (d2-d1)/sep ); // Change in orientation of chassis
+ double moveAngle = absolutePosition().angle()+dtheta/2;
+ rotateBy(dtheta);
+ moveBy( ((d1+d2)/2.)*std::cos(moveAngle), ((d1+d2)/2.)*std::sin(moveAngle) );
+}
+
+
+void ChassisCircular2::drawShape( QPainter &p )
+{
+ const double _x = int(sizeRect().x() + x());
+ const double _y = int(sizeRect().y() + y());
+ const double w = sizeRect().width();
+ const double h = sizeRect().height();
+
+ initPainter(p);
+ p.setBrush( QColor( 255, 246, 210 ) );
+ QRect circleRect = sizeRect();
+ circleRect.moveLeft( int(circleRect.left() + x()) );
+ circleRect.moveTop( int(circleRect.top() + y()) );
+ p.drawEllipse(circleRect);
+
+ // Draw wheels
+ // TODO get this info from m_wheel1Pos and m_wheel2Pos
+ const double X = _x+(w/5); // Wheel's left pos
+ const double H = h/8; // Wheel's height
+ const double y1 = _y+(h/6); // Wheel 1 y-pos
+ const double y2 = _y+(5*h/6)-H; // Wheel 2 y-pos
+
+ p.setPen( Qt::NoPen );
+ const double stripeWidth = 5;
+ const double offset2 = 1 + int(m_theta1*m_wheel1Pos.width())%int(2*stripeWidth);
+ const double offset1 = 1 + int(m_theta2*m_wheel2Pos.width())%int(2*stripeWidth);
+ p.setBrush( QColor( 255, 232, 182 ) );
+ for ( double i=-1; i<std::ceil(m_wheel1Pos.width()/stripeWidth); ++i )
+ {
+
+ p.setClipRect( QRect( int(_x+m_wheel1Pos.x()+2), int(_y+m_wheel1Pos.y()+2), int(m_wheel1Pos.width()-4), int(m_wheel1Pos.height()-4) ), QPainter::CoordPainter );
+ p.drawRect( int(offset1+X + i*stripeWidth*2), int(y1+1), int(stripeWidth), int(H-2) );
+
+ p.setClipRect( QRect( int(_x+m_wheel2Pos.x()+2), int(_y+m_wheel2Pos.y()+2), int(m_wheel2Pos.width()-4), int(m_wheel2Pos.height()-4) ), QPainter::CoordPainter );
+ p.drawRect( int(offset2+X + i*stripeWidth*2), int(y2+1), int(stripeWidth), int(H-2) );
+ }
+ p.setClipping(false);
+
+ p.setPen( Qt::black );
+ p.setBrush( Qt::NoBrush );
+ p.drawRoundRect( int(X), int(y1), int(w/4), int(H), 25, 50 );
+ p.drawRoundRect( int(X), int(y2), int(w/4), int(H), 25, 50 );
+
+
+ deinitPainter(p);
+}
diff --git a/src/mechanics/chassiscircular2.h b/src/mechanics/chassiscircular2.h
new file mode 100644
index 0000000..1c8897a
--- /dev/null
+++ b/src/mechanics/chassiscircular2.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * Copyright (C) 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. *
+ ***************************************************************************/
+
+#ifndef CHASSISCIRCULAR2_H
+#define CHASSISCIRCULAR2_H
+
+#include "mechanicsitem.h"
+
+/**
+@short Mechanics Framework, circular base, two wheels
+@author David Saxton
+*/
+class ChassisCircular2 : public MechanicsItem
+{
+public:
+ ChassisCircular2( MechanicsDocument *mechanicsDocument, bool newItem, const char *id = 0l );
+ ~ChassisCircular2();
+
+ static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id );
+ static LibraryItem *libraryItem();
+
+ virtual void advance( int phase );
+
+protected:
+ virtual void itemResized();
+ void drawShape( QPainter &p );
+
+ double m_theta1; // Angle of rotation of wheel 1 (used for drawing)
+ double m_theta2; // Angle of rotation of wheel 1 (used for drawing)
+
+ QRect m_wheel1Pos; // Position of first wheel, with respect to top left of item
+ QRect m_wheel2Pos; // Position of second wheel, with respect to top left of item
+};
+
+#endif
diff --git a/src/mechanics/mechanicsdocument.cpp b/src/mechanics/mechanicsdocument.cpp
new file mode 100644
index 0000000..0b2fa3b
--- /dev/null
+++ b/src/mechanics/mechanicsdocument.cpp
@@ -0,0 +1,197 @@
+/***************************************************************************
+ * 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 "canvasmanipulator.h"
+#include "documentiface.h"
+#include "drawpart.h"
+#include "itemlibrary.h"
+#include "mechanicsdocument.h"
+#include "mechanicsitem.h"
+#include "mechanicsgroup.h"
+#include "mechanicssimulation.h"
+#include "mechanicsview.h"
+
+#include <klocale.h>
+
+MechanicsDocument::MechanicsDocument( const QString &caption, KTechlab *ktechlab, const char *name )
+ : ItemDocument( caption, ktechlab, name )
+{
+ m_type = Document::dt_mechanics;
+ m_pDocumentIface = new MechanicsDocumentIface(this);
+ m_fileExtensionInfo = i18n("*.mechanics|Mechanics (*.mechanics)\n*|All Files");
+ m_canvas->retune(128);
+
+ m_selectList = new MechanicsGroup(this);
+
+ m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() );
+ m_cmManager->addManipulatorInfo( CMDraw::manipulatorInfo() );
+ m_cmManager->addManipulatorInfo( CMRightClick::manipulatorInfo() );
+ m_cmManager->addManipulatorInfo( CMRepeatedItemAdd::manipulatorInfo() );
+ m_cmManager->addManipulatorInfo( CMItemResize::manipulatorInfo() );
+ m_cmManager->addManipulatorInfo( CMMechItemMove::manipulatorInfo() );
+ m_mechanicsSimulation = new MechanicsSimulation(this);
+ requestStateSave();
+}
+
+
+MechanicsDocument::~MechanicsDocument()
+{
+ m_bDeleted = true;
+
+ // Remove all items from the canvas
+ selectAll();
+ deleteSelection();
+ delete m_mechanicsSimulation;
+}
+
+View *MechanicsDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name )
+{
+ ItemView *itemView = new MechanicsView( this, viewContainer, viewAreaId, name );
+ handleNewView(itemView);
+ return itemView;
+}
+
+
+ItemGroup *MechanicsDocument::selectList() const
+{
+ return m_selectList;
+}
+
+
+
+
+bool MechanicsDocument::isValidItem( const QString &itemId )
+{
+ return itemId.startsWith("mech/") || itemId.startsWith("dp/");
+}
+
+
+bool MechanicsDocument::isValidItem( Item *item )
+{
+ return item && ((dynamic_cast<MechanicsItem*>(item)) || (dynamic_cast<DrawPart*>(item)));
+}
+
+
+Item* MechanicsDocument::addItem( const QString &id, const QPoint &p, bool newItem )
+{
+ if ( !isValidItem(id) )
+ return 0l;
+
+ Item *item = itemLibrary()->createItem( id, this, newItem );
+ if (!item)
+ return 0L;
+
+ QRect rect = item->boundingRect();
+
+ int dx = (int)(p.x())-rect.width()/2;
+ int dy = (int)(p.y())-rect.height()/2;
+
+ if ( dx < 16 || dx > canvas()->width() ) dx = 16;
+ if ( dy < 16 || dy > canvas()->height() ) dy = 16;
+
+ item->move( dx, dy );
+ item->show();
+
+ registerItem(item);
+// setModified(true);
+ requestStateSave();
+ return item;
+}
+
+
+void MechanicsDocument::deleteSelection()
+{
+ // End whatever editing mode we are in, as we don't want to start editing
+ // something that is about to no longer exist...
+ m_cmManager->cancelCurrentManipulation();
+
+ if ( m_selectList->isEmpty() )
+ return;
+
+ // We nee to tell the selete items to remove themselves, and then
+ // pass the items that have add themselves to the delete list to the
+ // CommandAddItems command
+
+ m_selectList->deleteAllItems();
+ flushDeleteList();
+ setModified(true);
+
+ // We need to emit this so that property widgets etc...
+ // can clear themselves.
+ emit itemUnselected(0L);
+ requestStateSave();
+}
+
+
+bool MechanicsDocument::registerItem( QCanvasItem *qcanvasItem )
+{
+ return ItemDocument::registerItem(qcanvasItem);
+}
+
+
+void MechanicsDocument::appendDeleteList( QCanvasItem *qcanvasItem )
+{
+ MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(qcanvasItem);
+ if ( !mechItem || m_itemDeleteList.contains(mechItem) ) {
+ return;
+ }
+
+ m_itemDeleteList.append(mechItem);
+ m_itemList.remove(mechItem);
+
+ disconnect( mechItem, SIGNAL(selected(Item*,bool)), this, SIGNAL(itemSelected(Item*)) );
+ disconnect( mechItem, SIGNAL(unselected(Item*,bool)), this, SIGNAL(itemUnselected(Item*)) );
+
+ mechItem->removeItem();
+}
+
+
+void MechanicsDocument::flushDeleteList()
+{
+ // Remove duplicate items in the delete list
+ ItemList::iterator end = m_itemDeleteList.end();
+ for ( ItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it )
+ {
+ if ( *it && m_itemDeleteList.contains(*it) > 1 )
+ *it = 0l;
+ }
+ m_itemDeleteList.remove(QGuardedPtr<Item>(0l));
+
+ end = m_itemDeleteList.end();
+ for ( ItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it )
+ {
+ m_itemList.remove(*it);
+ (*it)->setCanvas(0l);
+ delete *it;
+ }
+}
+
+
+MechanicsItem* MechanicsDocument::mechanicsItemWithID( const QString &id )
+{
+ return dynamic_cast<MechanicsItem*>(itemWithID(id));
+}
+
+
+void MechanicsDocument::selectAll()
+{
+ const ItemList::iterator end = m_itemList.end();
+ for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it )
+ {
+ select(*it);
+ }
+}
+
+
+void MechanicsDocument::copy()
+{
+}
+
+#include "mechanicsdocument.moc"
diff --git a/src/mechanics/mechanicsdocument.h b/src/mechanics/mechanicsdocument.h
new file mode 100644
index 0000000..dfe454f
--- /dev/null
+++ b/src/mechanics/mechanicsdocument.h
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * Copyright (C) 2004 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. *
+ ***************************************************************************/
+
+#ifndef MECHANICSDOCUMENT_H
+#define MECHANICSDOCUMENT_H
+
+#include "itemdocument.h"
+
+class KTechlab;
+class MechanicsGroup;
+class MechanicsItem;
+class MechanicsSimulation;
+
+typedef QValueList<MechanicsItem*> MechItemList;
+typedef QValueList<MechanicsItem*> MechanicsItemList;
+
+/**
+@author David Saxton
+*/
+class MechanicsDocument : public ItemDocument
+{
+Q_OBJECT
+public:
+ MechanicsDocument( const QString &caption, KTechlab *ktechlab, const char *name = 0 );
+ ~MechanicsDocument();
+
+ virtual View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l );
+
+ virtual bool isValidItem( const QString &itemId );
+ virtual bool isValidItem( Item *item );
+
+ virtual void deleteSelection();
+ virtual void copy();
+ virtual void selectAll();
+ virtual ItemGroup *selectList() const;
+ MechanicsItem *mechanicsItemWithID( const QString &id );
+ virtual Item* addItem( const QString &id, const QPoint &p, bool newItem );
+ /**
+ * Adds a QCanvasItem to the delete list to be deleted, when
+ * flushDeleteList() is called
+ */
+ virtual void appendDeleteList( QCanvasItem *qcanvasItem );
+ /**
+ * Permantly deletes all items that have been added to the delete list with
+ * the appendDeleteList( QCanvasItem *qcanvasItem ) function.
+ */
+ virtual void flushDeleteList();
+ /**
+ * Register an item with the ICNDocument.
+ */
+ virtual bool registerItem( QCanvasItem *qcanvasItem );
+
+protected:
+ MechanicsGroup *m_selectList;
+ MechanicsSimulation *m_mechanicsSimulation;
+};
+
+
+#endif
diff --git a/src/mechanics/mechanicsgroup.cpp b/src/mechanics/mechanicsgroup.cpp
new file mode 100644
index 0000000..94fe703
--- /dev/null
+++ b/src/mechanics/mechanicsgroup.cpp
@@ -0,0 +1,243 @@
+/***************************************************************************
+ * Copyright (C) 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 "mechanicsgroup.h"
+#include "mechanicsitem.h"
+#include "mechanicsdocument.h"
+
+MechanicsGroup::MechanicsGroup( MechanicsDocument *mechanicsDocument, const char *name )
+ : ItemGroup( mechanicsDocument, name )
+{
+ b_isRaised = false;
+}
+
+
+MechanicsGroup::~MechanicsGroup()
+{
+}
+
+
+bool MechanicsGroup::addItem( Item *item )
+{
+ if ( !item || !item->canvas() || m_itemList.contains(item) ) {
+ return false;
+ }
+
+ // Check that the item's parent isn't already selected
+ Item *parent = item->parentItem();
+ while (parent)
+ {
+ if ( m_itemList.contains(parent) )
+ return false;
+ parent = parent->parentItem();
+ }
+ removeChildren(item);
+
+ registerItem(item);
+ updateInfo();
+ item->setSelected(true);
+ if ( MechanicsItem *mechanicsItem = dynamic_cast<MechanicsItem*>(item) )
+ mechanicsItem->setRaised(b_isRaised);
+ emit itemAdded(item);
+ return true;
+}
+
+
+bool MechanicsGroup::removeItem( Item *item )
+{
+ if ( !item || !m_itemList.contains(item) ) {
+ return false;
+ }
+ unregisterItem(item);
+ updateInfo();
+ item->setSelected(false);
+ MechanicsItem *mechanicsItem = dynamic_cast<MechanicsItem*>(item);
+ if (mechanicsItem)
+ mechanicsItem->setRaised(false);
+ emit itemRemoved(item);
+ return true;
+}
+
+
+void MechanicsGroup::removeChildren( Item *item )
+{
+ if (!item)
+ return;
+
+ const ItemList children = item->children();
+ const ItemList::const_iterator end = children.end();
+ for ( ItemList::const_iterator it = children.begin(); it != end; ++it )
+ {
+ removeChildren(*it);
+ removeItem(*it);
+ }
+}
+
+
+void MechanicsGroup::setRaised( bool isRaised )
+{
+ b_isRaised = isRaised;
+ const ItemList::iterator end = m_itemList.end();
+ for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it )
+ {
+ MechanicsItem *mechanicsItem = dynamic_cast<MechanicsItem*>((Item*)*it);
+ if (mechanicsItem)
+ mechanicsItem->setRaised(b_isRaised);
+ }
+}
+
+
+void MechanicsGroup::setSelectionMode( uint sm )
+{
+ const ItemList::iterator end = m_itemList.end();
+ for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it )
+ {
+ MechanicsItem *mechanicsItem = dynamic_cast<MechanicsItem*>((Item*)*it);
+ if (mechanicsItem)
+ mechanicsItem->setSelectionMode( (MechanicsItem::SelectionMode)sm );
+ }
+}
+
+
+MechanicsItemList MechanicsGroup::extractMechanicsItems() const
+{
+ MechanicsItemList mechanicsItemList;
+
+ const ItemList::const_iterator end = m_itemList.end();
+ for ( ItemList::const_iterator it = m_itemList.begin(); it != end; ++it )
+ {
+ MechanicsItem *mechanicsItem = dynamic_cast<MechanicsItem*>((Item*)*it);
+ if (mechanicsItem)
+ mechanicsItemList.append(mechanicsItem);
+ }
+
+ return mechanicsItemList;
+}
+
+
+MechanicsItemList MechanicsGroup::toplevelMechItemList() const
+{
+ MechanicsItemList toplevel;
+
+ MechanicsItemList mechItemList = extractMechanicsItems();
+
+ const MechanicsItemList::const_iterator end = mechItemList.end();
+ for ( MechanicsItemList::const_iterator it = mechItemList.begin(); it != end; ++it )
+ {
+ MechanicsItem* parent = *it;
+ while (parent)
+ {
+ if ( !parent->parentItem() && !toplevel.contains(parent) )
+ toplevel.append(parent);
+
+ parent = dynamic_cast<MechanicsItem*>(parent->parentItem());
+ }
+ }
+
+ return toplevel;
+}
+
+
+void MechanicsGroup::setSelected( bool sel )
+{
+ const ItemList::iterator end = m_itemList.end();
+ for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it )
+ {
+ if (*it && (*it)->isSelected() != sel ) {
+ (*it)->setSelected(sel);
+ }
+ }
+}
+
+
+bool MechanicsGroup::addQCanvasItem( QCanvasItem* item )
+{
+ return addItem( dynamic_cast<Item*>(item) );
+}
+
+bool MechanicsGroup::contains(QCanvasItem* item) const
+{
+ return m_itemList.contains(dynamic_cast<Item*>(item));
+}
+
+
+void MechanicsGroup::deleteAllItems()
+{
+ const ItemList::iterator end = m_itemList.end();
+ for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it )
+ {
+ if (*it)
+ (*it)->removeItem();
+ }
+
+ removeAllItems();
+}
+
+void MechanicsGroup::mergeGroup(ItemGroup* itemGroup)
+{
+ MechanicsGroup *group = dynamic_cast<MechanicsGroup*>(itemGroup);
+ if (!group) {
+ return;
+ }
+
+ const ItemList items = group->items();
+ const ItemList::const_iterator end = items.end();
+ for ( ItemList::const_iterator it = items.begin(); it != end; ++it )
+ {
+ addItem(*it);
+ }
+}
+
+void MechanicsGroup::removeAllItems()
+{
+ while ( !m_itemList.isEmpty() )
+ removeItem(m_itemList.first());
+}
+
+void MechanicsGroup::removeQCanvasItem(QCanvasItem* item)
+{
+ removeItem(dynamic_cast<Item*>(item));
+}
+
+
+void MechanicsGroup::setItems(QCanvasItemList list)
+{
+ {
+ ItemList removeList;
+ const ItemList::iterator end = m_itemList.end();
+ for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it )
+ {
+ if ( !list.contains(*it) ) {
+ removeList.append(*it);
+ }
+ }
+ const ItemList::iterator rend = removeList.end();
+ for ( ItemList::iterator it = removeList.begin(); it != rend; ++it )
+ {
+ removeItem(*it);
+ (*it)->setSelected(false);
+ }
+ }
+
+ const QCanvasItemList::iterator end = list.end();
+ for ( QCanvasItemList::iterator it = list.begin(); it != end; ++it )
+ {
+ // We don't need to check that we've already got the item as it will
+ // be checked in the function call
+ addQCanvasItem(*it);
+ }
+}
+
+
+void MechanicsGroup::updateInfo()
+{
+}
+
+#include "mechanicsgroup.moc"
diff --git a/src/mechanics/mechanicsgroup.h b/src/mechanics/mechanicsgroup.h
new file mode 100644
index 0000000..6b49966
--- /dev/null
+++ b/src/mechanics/mechanicsgroup.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * Copyright (C) 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. *
+ ***************************************************************************/
+
+#ifndef MECHANICSGROUP_H
+#define MECHANICSGROUP_H
+
+#include <itemgroup.h>
+
+
+class MechanicsItem;
+class MechanicsDocument;
+typedef QValueList<MechanicsItem*> MechanicsItemList;
+
+/**
+@author David Saxton
+*/
+class MechanicsGroup : public ItemGroup
+{
+Q_OBJECT
+public:
+ MechanicsGroup( MechanicsDocument *mechanicsDocument, const char *name = 0);
+ ~MechanicsGroup();
+
+ /**
+ * Returns a list of top-level mechanics items only
+ */
+ MechanicsItemList toplevelMechItemList() const;
+ /**
+ * Sets the selection mode of all MechanicsItems in the group
+ */
+ void setSelectionMode( uint sm );
+ /**
+ * "Raises" (increases the z value of) the selected group of items
+ */
+ void setRaised( bool isRaised );
+ /**
+ * Removes all the children of the given item from the group
+ */
+ void removeChildren( Item *item );
+ bool addItem( Item *item );
+ bool removeItem( Item *item );
+ virtual bool addQCanvasItem(QCanvasItem* item);
+ virtual bool contains(QCanvasItem* item) const;
+ virtual uint count() const { return itemCount(); }
+ virtual void deleteAllItems();
+ virtual void mergeGroup(ItemGroup* group);
+ virtual void removeAllItems();
+ virtual void removeQCanvasItem(QCanvasItem* item);
+ virtual void setItems(QCanvasItemList list);
+ /**
+ * Sets the selected state of all items in the group
+ */
+ virtual void setSelected( bool sel );
+ /**
+ * Extracts the mechanics items from the item list
+ */
+ MechanicsItemList extractMechanicsItems() const;
+
+protected:
+ void updateInfo();
+
+ bool b_isRaised;
+};
+
+#endif
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"
diff --git a/src/mechanics/mechanicsitem.h b/src/mechanics/mechanicsitem.h
new file mode 100644
index 0000000..db500fc
--- /dev/null
+++ b/src/mechanics/mechanicsitem.h
@@ -0,0 +1,237 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef MECHANICSITEM_H
+#define MECHANICSITEM_H
+
+#include <item.h>
+#include <qvaluelist.h>
+
+class LibraryItem;
+class MechanicsItem;
+// class MechanicsItemOverlayItem;
+class MechanicsDocument;
+typedef QValueList<MechanicsItem*> MechanicsItemList;
+
+/**
+@short Stores mass, moment of inertia
+@author David Saxton
+*/
+class MechanicsInfo
+{
+public:
+ MechanicsInfo();
+
+ double mass; // Mass
+ double momentOfInertia; // Moment of inertia
+};
+
+class CombinedMechanicsInfo : public MechanicsInfo
+{
+public:
+ CombinedMechanicsInfo();
+ CombinedMechanicsInfo( const MechanicsInfo &info );
+
+ double x; // X coordinate of center of mass
+ double y; // Y coordinate of center of mass
+};
+
+/**
+@short Stores a position and orientation
+@author David Saxton
+*/
+class PositionInfo
+{
+public:
+ PositionInfo();
+ /**
+ * Adds together two positions: for this=PARENT +(CHILD), the new position
+ * is formed by translating this position by that of the CHILDs
+ * translation, and then rotating everything about the center of this item
+ */
+ const PositionInfo operator+( const PositionInfo &info );
+ /**
+ * Not quite the inverse of operator+. Subtracts the given position info
+ * as if it was applied before this current info.
+ */
+ const PositionInfo operator-( const PositionInfo &info );
+ /**
+ * x position (0 is left)
+ */
+ double x() const { return m_x; }
+ /**
+ * y position (0 is top)
+ */
+ double y() const { return m_y; }
+ /**
+ * Angle in radians, positive direction is anticlockwise
+ */
+ double angle() const { return m_angle; }
+ /**
+ * Sets the x-position
+ */
+ void setX( double x ) { m_x = x; }
+ /**
+ * Sets the y-position
+ */
+ void setY( double y ) { m_y = y; }
+ /**
+ * Sets the angle
+ */
+ void setAngle( double angle ) { m_angle = angle; }
+ /**
+ * Adds (x,y) to the current position
+ */
+ void translate( double dx, const double dy ) { m_x += dx; m_y += dy; }
+ /**
+ * Rotates anticlockwise by the given amount (in radians)
+ */
+ void rotate( double angle ) { m_angle += angle; }
+ /**
+ * Resets the position to (0,0), and the orientation to 0
+ */
+ void reset();
+ /**
+ * Rotates the current position about the given point through the given
+ * angle in radians anticlockwise. This will change the position and
+ * orientation.
+ */
+ void rotateAboutPoint( double x, double y, double angle );
+
+protected:
+ double m_x;
+ double m_y;
+ double m_angle;
+};
+
+
+/**
+@author David Saxton
+*/
+class MechanicsItem : public Item
+{
+Q_OBJECT
+public:
+ MechanicsItem( MechanicsDocument *mechanicsDocument, bool newItem, const QString &id );
+ virtual ~MechanicsItem();
+
+ enum SelectionMode
+ {
+ sm_move,
+ sm_resize,
+ sm_rotate
+ };
+ /**
+ * Returns the run-time identifier for the MechanicsItem
+ */
+ int rtti() const;
+ /**
+ * Sets the selection mode (sm_resize or sm_rotate). Note that setSelected
+ * also needs to be called to select the item.
+ */
+ void setSelectionMode( SelectionMode sm );
+ virtual void setSelected( bool yes );
+ /**
+ * @returns the selection mode
+ */
+ SelectionMode selectionMode() const { return m_selectionMode; }
+ /**
+ * Move the MechanicsItem by the given amount
+ */
+ virtual void moveBy( double dx, double dy );
+ /**
+ * Returns the absolute position on the canvas
+ */
+ PositionInfo absolutePosition() const;
+ /**
+ * Returns the position relative to the parent item (or the absolute
+ * position if there is no parent item)
+ */
+ PositionInfo relativePosition() const { return m_relativePosition; }
+ /**
+ * Returns the mechanics info for this item (so not taking into account that
+ * of attached children)
+ */
+ MechanicsInfo *mechanicsInfo() { return &m_mechanicsInfo; }
+ /**
+ * Returns the combined mechanics info for this item (which takes into
+ * account that of attached children).
+ */
+ CombinedMechanicsInfo *mechanicsInfoCombined() { return &m_mechanicsInfoCombined; }
+ /**
+ * Returns the rectangle that can legitimately fit inside the given bounding
+ * rectangle, given this items current rotation. Legitimately means that
+ * whether this item is allowed to be distorted, inverted, resized, etc.
+ */
+ QRect maxInnerRectangle( const QRect &outerRect ) const;
+
+ virtual ItemData itemData() const;
+
+ virtual bool mousePressEvent( const EventInfo &eventInfo );
+ virtual bool mouseReleaseEvent( const EventInfo &eventInfo );
+ virtual bool mouseDoubleClickEvent ( const EventInfo &eventInfo );
+ virtual bool mouseMoveEvent( const EventInfo &eventInfo );
+ virtual bool wheelEvent( const EventInfo &eventInfo );
+ virtual void enterEvent();
+ virtual void leaveEvent();
+
+public slots:
+ /**
+ * Rotate the item by the given amount (in radians)
+ */
+ void rotateBy( double dtheta );
+ void parentMoved();
+
+signals:
+ /**
+ * Emitted when this item moves (translates or rotates)
+ */
+ void moved();
+
+protected slots:
+ /**
+ * Recalculate the combined mechanics info (e.g. when mass is changed, or child added)
+ */
+ void updateMechanicsInfoCombined();
+
+protected:
+ virtual void reparented( Item *oldItem, Item *newItem );
+ virtual void childAdded( Item *child );
+ virtual void childRemoved( Item *child );
+ /**
+ * Called when this item is resized, so that sub classes can do whatever
+ */
+ virtual void itemResized() {};
+ /**
+ * Sets the correct orientation on the painter
+ */
+ void initPainter( QPainter &p );
+ /**
+ * *Must* be called after calling initPainter, if initPainter was called
+ */
+ void deinitPainter( QPainter &p );
+ virtual void dataChanged();
+ virtual void itemPointsChanged() { updateCanvasPoints(); }
+ /**
+ * Calculates the setPoints required from the current m_itemPoints and the
+ * current position / angle
+ */
+ void updateCanvasPoints();
+
+ MechanicsDocument *p_mechanicsDocument;
+ PositionInfo m_relativePosition; // Absolution position if not attached to a parent item, or otherwise relative to parent item
+ MechanicsInfo m_mechanicsInfo;
+ CombinedMechanicsInfo m_mechanicsInfoCombined;
+
+private:
+ SelectionMode m_selectionMode;
+};
+
+#endif
diff --git a/src/mechanics/mechanicssimulation.cpp b/src/mechanics/mechanicssimulation.cpp
new file mode 100644
index 0000000..d5ec98d
--- /dev/null
+++ b/src/mechanics/mechanicssimulation.cpp
@@ -0,0 +1,139 @@
+/***************************************************************************
+ * Copyright (C) 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 "mechanicsdocument.h"
+#include "mechanicsitem.h"
+#include "mechanicssimulation.h"
+
+#include <cmath>
+#include <qtimer.h>
+
+MechanicsSimulation::MechanicsSimulation( MechanicsDocument *mechanicsDocument )
+ : QObject(mechanicsDocument)
+{
+ p_mechanicsDocument = mechanicsDocument;
+ m_advanceTmr = new QTimer(this);
+ connect( m_advanceTmr, SIGNAL(timeout()), this, SLOT(slotAdvance()) );
+ m_advanceTmr->start(1);
+}
+
+
+MechanicsSimulation::~MechanicsSimulation()
+{
+}
+
+
+void MechanicsSimulation::slotAdvance()
+{
+ if ( p_mechanicsDocument && p_mechanicsDocument->canvas() )
+ p_mechanicsDocument->canvas()->advance();
+}
+
+
+RigidBody::RigidBody( MechanicsDocument *mechanicsDocument )
+{
+ p_mechanicsDocument = mechanicsDocument;
+ p_overallParent = 0l;
+}
+
+
+RigidBody::~RigidBody()
+{
+}
+
+
+bool RigidBody::addMechanicsItem( MechanicsItem *item )
+{
+ if ( !item || m_mechanicsItemList.contains(item) )
+ return false;
+
+ m_mechanicsItemList.append(item);
+ findOverallParent();
+ return true;
+}
+
+
+void RigidBody::moveBy( double dx, double dy )
+{
+ if (overallParent())
+ overallParent()->moveBy( dx, dy );
+}
+
+
+void RigidBody::rotateBy( double dtheta )
+{
+ if (overallParent())
+ overallParent()->rotateBy(dtheta);
+}
+
+
+bool RigidBody::findOverallParent()
+{
+ p_overallParent = 0l;
+ if ( m_mechanicsItemList.isEmpty() )
+ return false;
+
+ m_mechanicsItemList.remove(0l);
+
+ const MechanicsItemList::iterator end = m_mechanicsItemList.end();
+ for ( MechanicsItemList::iterator it = m_mechanicsItemList.begin(); it != end; ++it )
+ {
+ MechanicsItem *parentItem = *it;
+ MechanicsItem *parentCandidate = dynamic_cast<MechanicsItem*>((*it)->parentItem());
+
+ while (parentCandidate)
+ {
+ parentItem = parentCandidate;
+ parentCandidate = dynamic_cast<MechanicsItem*>(parentItem->parentItem());
+ }
+
+ if ( !p_overallParent )
+ // Must be the first item to test
+ p_overallParent = parentItem;
+
+ if ( p_overallParent != parentItem )
+ {
+ p_overallParent = 0l;
+ return false;
+ }
+ }
+ return true;
+}
+
+
+void RigidBody::updateRigidBodyInfo()
+{
+ if (!p_overallParent)
+ return;
+
+ m_mass = p_overallParent->mechanicsInfoCombined()->mass;
+ m_momentOfInertia = p_overallParent->mechanicsInfoCombined()->momentOfInertia;
+}
+
+
+Vector2D::Vector2D()
+{
+ x = 0.;
+ y = 0.;
+}
+
+double Vector2D::length() const
+{
+ return std::sqrt( x*x + y*y );
+}
+
+RigidBodyState::RigidBodyState()
+{
+ angularMomentum = 0.;
+}
+
+
+
+#include "mechanicssimulation.moc"
diff --git a/src/mechanics/mechanicssimulation.h b/src/mechanics/mechanicssimulation.h
new file mode 100644
index 0000000..19b474b
--- /dev/null
+++ b/src/mechanics/mechanicssimulation.h
@@ -0,0 +1,133 @@
+/***************************************************************************
+ * Copyright (C) 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. *
+ ***************************************************************************/
+
+#ifndef MECHANICSSIMULATION_H
+#define MECHANICSSIMULATION_H
+
+#include <qguardedptr.h>
+#include <qobject.h>
+#include <qvaluelist.h>
+
+class MechanicsItem;
+class MechanicsDocument;
+typedef QValueList<MechanicsItem*> MechanicsItemList;
+
+
+/**
+@short 2 dimensional vector with associated length functions et al
+@author David Saxton
+*/
+class Vector2D
+{
+public:
+ Vector2D();
+
+ double length() const;
+ double lengthSquared() const { return x*x + y*y; }
+
+ double x;
+ double y;
+};
+
+
+/**
+@short State of a rigid body, in an inertial MechanicsDocument frame
+@author David Saxton
+*/
+class RigidBodyState
+{
+public:
+ RigidBodyState();
+
+ Vector2D linearMomentum;
+ double angularMomentum;
+ Vector2D position; // Position of center of mass
+};
+
+
+/**
+@author David Saxton
+*/
+class MechanicsSimulation : public QObject
+{
+Q_OBJECT
+public:
+ MechanicsSimulation( MechanicsDocument *mechanicsDocument );
+ ~MechanicsSimulation();
+
+ MechanicsDocument* mechanicsDocument() const { return p_mechanicsDocument; }
+
+protected slots:
+ void slotAdvance();
+
+protected:
+ QGuardedPtr<MechanicsDocument> p_mechanicsDocument;
+ QTimer *m_advanceTmr;
+};
+
+
+/**
+Rigid body with mass, inertia, etc. Collection of mechanics items with
+functionality for moving them about, rotating, etc. Only one mother-parent
+(has no parent itself, all other items are descendents) allowed.
+@short Rigid body, handles MechanicsItems
+@author David Saxton
+*/
+class RigidBody
+{
+public:
+ RigidBody( MechanicsDocument *mechanicsDocument );
+ ~RigidBody();
+
+ /**
+ *
+ */
+ void advance( int phase, double delta );
+ /**
+ * Add the MechanicsItem to the entity.
+ * @returns true iff successful in adding
+ */
+ bool addMechanicsItem( MechanicsItem *item );
+ /**
+ * Pointer to the mother MechanicsItem.
+ */
+ MechanicsItem *overallParent() const { return p_overallParent; }
+ /**
+ * Updates the mass and the moment of inertia info
+ */
+ void updateRigidBodyInfo();
+
+protected:
+ /**
+ * Attempt to find the overall parent.
+ * @returns false iff unsucessful (including if there are no MechanicsItems present)
+ */
+ bool findOverallParent();
+ /**
+ * Move the set of MechanicsItems by the given amount
+ */
+ void moveBy( double dx, double dy );
+ /**
+ * Rotate the set of MechanicsItems by the given amount about the center of
+ * mass.
+ * @param angle Rotate amount in radians
+ */
+ void rotateBy( double dtheta );
+
+ MechanicsItemList m_mechanicsItemList;
+ MechanicsItem *p_overallParent;
+ MechanicsDocument *p_mechanicsDocument;
+
+ RigidBodyState m_rigidBodyState;
+ double m_mass;
+ double m_momentOfInertia;
+};
+
+#endif
diff --git a/src/mechanics/mechanicsview.cpp b/src/mechanics/mechanicsview.cpp
new file mode 100644
index 0000000..d49c66a
--- /dev/null
+++ b/src/mechanics/mechanicsview.cpp
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * Copyright (C) 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 "mechanicsdocument.h"
+#include "mechanicsview.h"
+#include "viewiface.h"
+
+
+MechanicsView::MechanicsView( MechanicsDocument *mechanicsDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name )
+ : ItemView( mechanicsDocument, viewContainer, viewAreaId, name )
+{
+ setXMLFile( "ktechlabmechanicsui.rc", true );
+ m_pViewIface = new MechanicsViewIface(this);
+}
+
+
+MechanicsView::~MechanicsView()
+{
+ delete m_pViewIface;
+ m_pViewIface = 0l;
+}
+
+
+
+
+void MechanicsView::dragEnterEvent( QDragEnterEvent * e )
+{
+ ItemView::dragEnterEvent(e);
+ if ( e->isAccepted() )
+ return;
+
+ e->accept( e->provides("ktechlab/mechanical") );
+}
+
+#include "mechanicsview.moc"
diff --git a/src/mechanics/mechanicsview.h b/src/mechanics/mechanicsview.h
new file mode 100644
index 0000000..f787b1c
--- /dev/null
+++ b/src/mechanics/mechanicsview.h
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright (C) 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. *
+ ***************************************************************************/
+
+#ifndef MECHANICSVIEW_H
+#define MECHANICSVIEW_H
+
+#include <itemview.h>
+
+class MechanicsDocument;
+
+/**
+@author David Saxton
+*/
+class MechanicsView : public ItemView
+{
+ Q_OBJECT
+ public:
+ MechanicsView( MechanicsDocument *mechanicsDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l );
+ ~MechanicsView();
+
+ protected:
+ virtual void dragEnterEvent( QDragEnterEvent * e );
+ MechanicsDocument *m_pMechanicsDocument;
+};
+
+#endif