samples/guiAndGfx: eC port of JFD's Mekano
authorJerome St-Louis <jerome@ecere.com>
Tue, 29 Sep 2015 19:32:47 +0000 (15:32 -0400)
committerJerome St-Louis <jerome@ecere.com>
Mon, 21 Dec 2015 19:31:31 +0000 (14:31 -0500)
- A mechanic simulation featuring springs by Jonathan Fillion-Deneault
- Ported this classic Ecere sample which was using the C++ Ecere class library from ~2003 to eC

14 files changed:
samples/guiAndGfx/mekano/mekano.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekano.epj [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanodisplay.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanoobject.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanoobjectfixed.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanoobjectgravity.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanoobjectpolygonal.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanoobjectspring.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanopoint.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanopolygon.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanopolygonalbox.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanosimulation.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/mekanownd.ec [new file with mode: 0644]
samples/guiAndGfx/mekano/vector.ec [new file with mode: 0644]

diff --git a/samples/guiAndGfx/mekano/mekano.ec b/samples/guiAndGfx/mekano/mekano.ec
new file mode 100644 (file)
index 0000000..b48d595
--- /dev/null
@@ -0,0 +1,3 @@
+import "mekanownd"
+
+MekanoWnd mekano { displayDriver = "OpenGL"; };
diff --git a/samples/guiAndGfx/mekano/mekano.epj b/samples/guiAndGfx/mekano/mekano.epj
new file mode 100644 (file)
index 0000000..ab8c3f3
--- /dev/null
@@ -0,0 +1,112 @@
+{
+   "Version" : 0.2,
+   "ModuleName" : "mekano",
+   "Options" : {
+      "Warnings" : "All",
+      "Debug" : false,
+      "Profile" : false,
+      "TargetType" : "Executable",
+      "TargetFileName" : "mekano",
+      "TargetDirectory" : "",
+      "Libraries" : [
+         "ecere"
+      ]
+   },
+   "Configurations" : [
+      {
+         "Name" : "Debug",
+         "Options" : {
+            "Debug" : true,
+            "PreprocessorDefinitions" : [
+               "IMPORT_STATIC=\"\""
+            ],
+            "Console" : true
+         }
+      },
+      {
+         "Name" : "Release",
+         "Options" : {
+            "Optimization" : "Speed",
+            "PreprocessorDefinitions" : [
+               "IMPORT_STATIC=static"
+            ],
+            "Libraries" : [
+               "ecereStatic"
+            ],
+            "LinkerOptions" : [
+               "-static"
+            ],
+            "LibraryDirs" : [
+               "../../../ecere/obj/static.win32"
+            ],
+            "Console" : true,
+            "Compress" : true,
+            "FastMath" : true
+         },
+         "Platforms" : [
+            {
+               "Name" : "win32",
+               "Options" : {
+                  "Libraries" : [
+                     "dxguid",
+                     "ddraw",
+                     "dinput",
+                     "winmm",
+                     "opengl32",
+                     "ws2_32",
+                     "kernel32",
+                     "user32",
+                     "gdi32",
+                     "mpr",
+                     "advapi32",
+                     "shell32",
+                     "winspool",
+                     "imm32",
+                     "ungif",
+                     "jpeg",
+                     "png",
+                     "z",
+                     "freetype"
+                  ]
+               }
+            }
+         ]
+      }
+   ],
+   "Files" : [
+      {
+         "Folder" : "src",
+         "Files" : [
+            "./mekano.ec",
+            "./mekanodisplay.ec",
+            "./mekanoobject.ec",
+            "./mekanoobjectfixed.ec",
+            "./mekanoobjectgravity.ec",
+            "./mekanoobjectpolygonal.ec",
+            "./mekanoobjectspring.ec",
+            "./mekanopoint.ec",
+            "./mekanopolygon.ec",
+            "./mekanopolygonalbox.ec",
+            "./mekanosimulation.ec",
+            "./mekanownd.ec",
+            "./vector.ec"
+         ]
+      }
+   ],
+   "ResourcesPath" : "..\\..\\..\\",
+   "Resources" : [
+      {
+         "Folder" : "ecere",
+         "Files" : [
+            {
+               "Folder" : "shaders",
+               "Files" : [
+                  "../../../ecere/src/gfx/drivers/gl3/fixed.frag",
+                  "../../../ecere/src/gfx/drivers/gl3/fixed.vertex"
+               ]
+            }
+         ]
+      },
+      "data/ecere.bmp"
+   ]
+}
diff --git a/samples/guiAndGfx/mekano/mekanodisplay.ec b/samples/guiAndGfx/mekano/mekanodisplay.ec
new file mode 100644 (file)
index 0000000..46552cd
--- /dev/null
@@ -0,0 +1,115 @@
+import "mekanopolygon"
+
+define COLORSPRING = orange;
+define COLORPOLY   = magenta;
+define COLORBOX    = blue;
+define COLORCROSS  = lime;
+
+class MekanoDisplay
+{
+   Surface surface;
+
+public:
+   void drawPolygon(Vector2D position, MekanoPolygon polygon)
+   {
+      List<MekanoPoint> points=polygon.points;
+      if(points.count > 1)
+      {
+         MekanoPoint point1, point2 = null, firstpoint = null;
+         Vector2D point_pos1, point_pos2;
+         bool first = true;
+
+         surface.foreground = COLORPOLY;
+
+         for(p : points)
+         {
+            if(first)
+            {
+               point2 = firstpoint = p;
+               first = false;
+            }
+            else
+            {
+               point1 = point2;
+               point2 = p;
+               point_pos1=point1.localPosition;
+               point_pos2=point2.localPosition;
+
+               surface.DrawLine((int)(position.x+point_pos1.x), (int)(position.y+point_pos1.y),
+                  (int)(position.x+point_pos2.x), (int)(position.y+point_pos2.y));
+            }
+         }
+         point_pos1=point2.localPosition;
+         point_pos2=firstpoint.localPosition;
+         surface.DrawLine((int)(position.x+point_pos1.x), (int)(position.y+point_pos1.y),
+            (int)(position.x+point_pos2.x), (int)(position.y+point_pos2.y));
+      }
+   }
+
+   void drawBox(Vector2D position, int width)
+   {
+      surface.foreground = COLORBOX;
+      surface.Rectangle(
+         (short)position.x-width/2, (short)position.y-width/2,
+         (short)position.x+width/2, (short)position.y+width/2);
+   }
+
+   void drawCrossHair(Vector2D position, int width)
+   {
+      surface.foreground = COLORCROSS;
+      surface.HLine((short)position.x-width/2, (short)position.x+width/2,
+         (short)position.y);
+      surface.VLine((short)position.y-width/2, (short)position.y+width/2,
+         (short)position.x);
+   }
+
+   void drawSpring(Vector2D pos1, Vector2D pos2, int zigs)
+   {
+      int t;
+      float lzig, h = 10.0;
+      Vector2D d=pos1;
+      Vector2D o;
+      Vector2D u, n;
+      Vector2D a1, a2;
+
+      surface.foreground = COLORSPRING;
+
+      o.subtract(pos2, pos1);
+
+      u=o.unit;
+      n=o.normal;
+      lzig=o.length/zigs;
+
+      // a1=u*(lzig*1/4)+n*h;
+      {
+         Vector2D t1, t2;
+         t1.scale(u, lzig*1/4);
+         t2.scale(n, h);
+         a1.add(t1, t2);
+      }
+
+      // a2=u*(lzig*2/4)-n*h*2.0;
+      {
+         Vector2D t1, t2;
+         t1.scale(u, lzig*2/4);
+         t2.scale(n, h*2.0f);
+         a2.subtract(t1, t2);
+      }
+
+      for (t=0; t<zigs; t++)
+      {
+         Vector2D end;
+         end.add(d, a1);
+         surface.DrawLine((int)d.x, (int)d.y, (int)end.x, (int)end.y);
+         d = end;
+
+         end.add(d, a2);
+         surface.DrawLine((int)d.x, (int)d.y, (int)end.x, (int)end.y);
+         d = end;
+
+         end.add(d, a1);
+         surface.DrawLine((int)d.x, (int)d.y, (int)end.x, (int)end.y);
+         d = end;
+      }
+   }
+}
diff --git a/samples/guiAndGfx/mekano/mekanoobject.ec b/samples/guiAndGfx/mekano/mekanoobject.ec
new file mode 100644 (file)
index 0000000..2ab84a5
--- /dev/null
@@ -0,0 +1,164 @@
+import "mekanopoint"
+import "mekanosimulation"
+
+class ObjectAttributes : uint { bool selected:1, steady:1, highlighted:1; };
+
+class MekanoObject
+{
+   void addPoint(MekanoPoint point)
+   {
+      m_Points.Add(point);
+   }
+
+   float m_fMass;
+   float m_fInertiaMoment;
+
+   float m_fCosRotation;
+   float m_fSinRotation;
+
+   float m_fAppliedTorque;
+   float m_fAngularAcceleration;
+   float m_fAngularSpeed;
+   float m_fRotation;
+   float m_fAngularFriction;
+
+   float m_fBoundingRadius;
+
+   Vector2D m_AppliedForce;
+   Vector2D m_Acceleration;
+   Vector2D m_Speed;
+   Vector2D m_Position;
+   Vector2D m_LastPosition;
+
+   ObjectAttributes m_Attributes;
+   List<MekanoPoint> m_Points { };
+
+public:
+
+   ~MekanoObject()
+   {
+      m_Points.Free();
+   }
+
+   property MekanoSimulation simulation { set { value.addObject(this); } }
+
+   mass = 1.0f;
+   inertiaMoment = 10;
+   angularFriction = 0.1f;
+
+   property float cosRotation { get { return m_fCosRotation; } };
+   property float sinRotation { get { return m_fSinRotation; } };
+
+   property float mass
+   {
+      set { m_fMass=value; }
+      get { return m_fMass; }
+   }
+
+   property float inertiaMoment
+   {
+      set { m_fInertiaMoment=value; }
+      get { return m_fInertiaMoment; }
+   }
+
+   property float boundingRadius
+   {
+      set { m_fBoundingRadius=value; }
+      get { return m_fBoundingRadius; }
+   }
+
+   property float angularSpeed
+   {
+      set { m_fAngularSpeed=value; }
+      get { return m_fAngularSpeed; }
+   }
+
+   property float angularAcceleration
+   {
+      set { m_fAngularAcceleration=value; }
+      get { return m_fAngularAcceleration; }
+   }
+
+   property float angularFriction
+   {
+      set { m_fAngularFriction=value; }
+      get { return m_fAngularFriction; }
+   }
+
+   property Vector2D lastPosition
+   {
+      set { m_LastPosition=value; }
+      get { value = m_LastPosition; }
+   }
+
+   property Vector2D position
+   {
+      set { m_Position=value; }
+      get { value = m_Position; }
+   }
+
+   property Vector2D speed
+   {
+      set { m_Speed=value; }
+      get { value = m_Speed; }
+   }
+
+   property Vector2D acceleration
+   {
+      set { m_Acceleration=value; }
+      get { value = m_Acceleration; }
+   }
+
+   property Vector2D deltaPosition { get { value.subtract(m_Position, m_LastPosition); } };
+
+   property ObjectAttributes attributes { set { m_Attributes=value; } get { return m_Attributes; } }
+
+   property Vector2D AppliedForce
+   {
+      set { m_AppliedForce=value; }
+      get { value = m_AppliedForce; }
+   }
+
+   property float appliedTorque
+   {
+      set { m_fAppliedTorque=value; }
+      get { return m_fAppliedTorque; }
+   }
+
+   property List<MekanoPoint> points { get { return m_Points; } };
+
+   property MekanoPoint centerPoint
+   {
+      get
+      {
+         for(p : m_Points; p.type == center)
+            return p;
+         return null;
+      }
+   }
+
+   property float rotation
+   {
+      get { return m_fRotation; }
+      set
+      {
+         m_fRotation=value;
+         m_fCosRotation=(float)cos(value);
+         m_fSinRotation=(float)sin(value);
+      }
+   }
+
+   virtual void resetForces() { m_AppliedForce = { }, m_fAppliedTorque = 0; };
+   virtual void exertForces(MekanoSimulation sim);
+   virtual void applyForce(Vector2D f, MekanoPoint pt);
+   virtual void draw(MekanoDisplay display);
+   virtual void step(Time dt) { lastPosition = m_Position; }
+   virtual bool isInside(Vector2D v) { return false; }
+   virtual bool isColliding(MekanoPoint pt, Vector2D normal) { return false; }
+
+   void computeBoundingRadius()
+   {
+      for(p : m_Points; p.type == vertex)
+         p.computeBoundingRadius();
+   }
+}
diff --git a/samples/guiAndGfx/mekano/mekanoobjectfixed.ec b/samples/guiAndGfx/mekano/mekanoobjectfixed.ec
new file mode 100644 (file)
index 0000000..21bfba9
--- /dev/null
@@ -0,0 +1,12 @@
+import "mekanoobject"
+
+define CROSSHAIRWIDTH = 10;
+
+class MekanoObjectFixed : MekanoObject
+{
+public:
+   void draw(MekanoDisplay display)
+   {
+      display.drawCrossHair(m_Position, CROSSHAIRWIDTH);
+   }
+};
diff --git a/samples/guiAndGfx/mekano/mekanoobjectgravity.ec b/samples/guiAndGfx/mekano/mekanoobjectgravity.ec
new file mode 100644 (file)
index 0000000..0c50aa4
--- /dev/null
@@ -0,0 +1,24 @@
+import "mekanosimulation"
+
+class MekanoObjectGravity : MekanoObject
+{
+   Vector2D gravity;
+
+public:
+   property Vector2D gravity
+   {
+      set { gravity = value; }
+      get { value = gravity; }
+   }
+
+   void exertForces(MekanoSimulation sim)
+   {
+      for(o : sim.objectList)
+         for(p : o.points; p.type == center)
+         {
+            Vector2D f;
+            f.scale(gravity, o.mass);
+            o.applyForce(f, p);
+         }
+   }
+}
diff --git a/samples/guiAndGfx/mekano/mekanoobjectpolygonal.ec b/samples/guiAndGfx/mekano/mekanoobjectpolygonal.ec
new file mode 100644 (file)
index 0000000..348f643
--- /dev/null
@@ -0,0 +1,54 @@
+import "mekanoobject"
+import "mekanopolygon"
+
+class MekanoObjectPolygonal : MekanoObject
+{
+private:
+   List<MekanoPolygon> m_Polygons { };
+
+public:
+   property List<MekanoPolygon> polygons { get { return m_Polygons; } }
+
+   void addPolygon(MekanoPolygon polygon)
+   {
+      m_Polygons.Add(polygon);
+   }
+
+   ~MekanoObjectPolygonal()
+   {
+      m_Polygons.Free();
+   }
+
+   void computePoints()
+   {
+      for(poly : m_Polygons)
+      {
+         for(p : poly.points)
+         {
+            p.lastLocalPosition = p.localPosition;
+            p.computeLocalPosition();
+         }
+      }
+   }
+
+   void draw(MekanoDisplay display)
+   {
+      MekanoObject::draw(display);
+      for(p : m_Polygons)
+         display.drawPolygon(m_Position, p);
+   }
+
+   bool isInside(Vector2D v)
+   {
+      Vector2D localposition;
+      float radius = boundingRadius;
+      localposition.subtract(v, position);
+      if(radius)
+         if(localposition.length > radius)
+            return false;
+
+      for(p : m_Polygons; p.isInside(localposition))
+         return true;
+      return false;
+   }
+}
diff --git a/samples/guiAndGfx/mekano/mekanoobjectspring.ec b/samples/guiAndGfx/mekano/mekanoobjectspring.ec
new file mode 100644 (file)
index 0000000..dda7c7b
--- /dev/null
@@ -0,0 +1,94 @@
+import "mekanoobject"
+
+define DEFAULTZIGS = 5;
+
+class MekanoObjectSpring : MekanoObject
+{
+private:
+   float m_fLength;
+   float m_fRestitution;
+   float m_fDamping;
+   bool m_bLinked;
+   int m_nZigs;
+
+   MekanoPoint m_pLinkPoints[2];
+
+public:
+
+   m_nZigs=DEFAULTZIGS;
+   m_fRestitution=1;
+   m_fDamping=2;
+
+   property float length
+   {
+      set { m_fLength=value; }
+      get { return m_fLength; }
+   }
+
+   property float damping
+   {
+      set { m_fDamping=value; }
+      get { return m_fDamping; }
+   }
+
+   property float restitution
+   {
+      set { m_fRestitution=value; }
+      get { return m_fRestitution; }
+   }
+
+   property int zigs
+   {
+      set { m_nZigs=value; }
+      get { return m_nZigs; }
+   }
+
+   void draw(MekanoDisplay display)
+   {
+      if(m_bLinked)
+      {
+         Vector2D pos0=m_pLinkPoints[0].screenPosition;
+         Vector2D pos1=m_pLinkPoints[1].screenPosition;
+         display.drawSpring(pos0, pos1, m_nZigs);
+      }
+   }
+
+   void exertForces(MekanoSimulation sim)
+   {
+      if(m_bLinked)
+      {
+         Vector2D relativepos;
+         Vector2D relativespeed;
+         Vector2D damping, force;
+         MekanoObject obj0, obj1;
+         float stretch;
+
+         obj0=m_pLinkPoints[0].object;
+         obj1=m_pLinkPoints[1].object;
+
+         relativepos.subtract(m_pLinkPoints[1].screenPosition, m_pLinkPoints[0].screenPosition);
+         relativespeed.subtract(obj1.speed, obj0.speed);
+         stretch=relativepos.length-m_fLength;
+
+         //damping=relativespeed*m_fDamping*(relativespeed%relativepos)/(relativepos%relativepos);
+         damping.scale(relativespeed, m_fDamping);
+         force.scale(relativepos.unit, stretch*m_fRestitution);
+
+         obj0.applyForce(force, m_pLinkPoints[0]);
+         obj0.applyForce(damping, m_pLinkPoints[0]);
+
+         force.scale(force, -1);
+         damping.scale(force, -1);
+
+         obj1.applyForce(force, m_pLinkPoints[1]);
+         obj1.applyForce(damping, m_pLinkPoints[1]);
+      }
+   }
+
+   void link(MekanoPoint p1, MekanoPoint p2)
+   {
+      m_pLinkPoints[0]=p1;
+      m_pLinkPoints[1]=p2;
+      m_bLinked=true;
+   }
+}
diff --git a/samples/guiAndGfx/mekano/mekanopoint.ec b/samples/guiAndGfx/mekano/mekanopoint.ec
new file mode 100644 (file)
index 0000000..4861087
--- /dev/null
@@ -0,0 +1,39 @@
+import "vector"
+import "mekanoobject"
+
+public enum MekanoPointType { unknown, center, vertex };
+
+class MekanoPoint : struct // TOFIX: Container issues with this : private Vector2D
+{
+private:
+   Vector2D pt;
+   MekanoPointType m_Type;
+   MekanoObject m_pObject;
+   Vector2D m_LastLocalPosition;
+   Vector2D m_LocalPosition;
+   Vector2D m_ScreenPosition;
+
+public:
+   property MekanoObject object          { set { m_pObject = value; value.addPoint(this); } get { return m_pObject; } }
+   property MekanoPointType type         { set { m_Type = value; } get { return m_Type; } }
+   property float x                      { set { pt.x = value; } get { return pt.x; } }
+   property float y                      { set { pt.y = value; } get { return pt.y; } }
+
+   property Vector2D lastLocalPosition   { set { m_LastLocalPosition = value; } }
+   property Vector2D localPosition       { get { value = m_LocalPosition; } }
+   property Vector2D deltaLocalPosition  { get { value.subtract(m_LocalPosition, m_LastLocalPosition); } }
+   property Vector2D screenPosition      { get { value.add(m_LocalPosition, m_pObject.position); } }
+   property Vector2D deltaScreenPosition { get { value.add(deltaLocalPosition, m_pObject.deltaPosition); } }
+
+   void computeLocalPosition()
+   {
+      m_LocalPosition = { x*m_pObject.cosRotation - y * m_pObject.sinRotation, x*m_pObject.sinRotation + y*m_pObject.cosRotation };
+   }
+
+   void computeBoundingRadius()
+   {
+      float l = pt.length;
+      if(m_pObject.boundingRadius < l)
+         m_pObject.boundingRadius = l;
+   }
+};
diff --git a/samples/guiAndGfx/mekano/mekanopolygon.ec b/samples/guiAndGfx/mekano/mekanopolygon.ec
new file mode 100644 (file)
index 0000000..5e29487
--- /dev/null
@@ -0,0 +1,56 @@
+import "mekanoobjectpolygonal"
+
+class MekanoPolygon
+{
+   List<MekanoPoint> m_Points { };
+
+public:
+   property MekanoObjectPolygonal polygonal { set { value.addPolygon(this); } }
+
+   property List<MekanoPoint> points { get { return m_Points; } }
+
+   void addPoint(MekanoPoint point)
+   {
+      m_Points.Add(point);
+   }
+
+   bool ::crosses(Vector2D c, Vector2D p1, Vector2D p2)
+   {
+      if(c.y>Min(p1.y, p2.y) && c.y<Max(p1.y, p2.y) && c.x <= Max(p1.x, p2.x))
+      {
+         float xinters=(c.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x;
+         return (p1.x==p2.x || c.x<xinters);
+      }
+      return false;
+   }
+
+   bool isInside(Vector2D v)
+   {
+      if(m_Points.count > 1)
+      {
+         int c = 0;
+         MekanoPoint point1, point2 = null, firstpoint = null;
+         bool first = true;
+
+         for(p : m_Points)
+         {
+            if(first)
+            {
+               firstpoint = point2 = p;
+               first = false;
+            }
+            else
+            {
+               point1 = point2;
+               point2 = p;
+               if(crosses(v, point1.localPosition, point2.localPosition))
+                  c++;
+            }
+         }
+         if(crosses(v, point2.localPosition, firstpoint.localPosition))
+            c++;
+         return c & 1;
+      }
+      return false;
+   }
+}
diff --git a/samples/guiAndGfx/mekano/mekanopolygonalbox.ec b/samples/guiAndGfx/mekano/mekanopolygonalbox.ec
new file mode 100644 (file)
index 0000000..ec6a76f
--- /dev/null
@@ -0,0 +1,80 @@
+import "mekanoobjectpolygonal"
+
+enum ObjBoxPointType { topPoint = 1, bottomPoint = 2 };
+
+class MekanoPolygonalBox : MekanoObjectPolygonal
+{
+private:
+   float m_fSize;
+
+public:
+   property int size
+   {
+      get { return (int)m_fSize; }
+      set
+      {
+         MekanoPolygon polygon { this };
+
+         MekanoPoint { this, type = center };
+         polygon.addPoint(MekanoPoint { this, type = vertex, -value/2+2, -value/2+2 });
+         polygon.addPoint(MekanoPoint { this, type = vertex, 0, -value/2 }); // 1
+         polygon.addPoint(MekanoPoint { this, type = vertex, value/2-2, -value/2+2 });
+         polygon.addPoint(MekanoPoint { this, type = vertex, value/2-2, value/2-2 });
+         polygon.addPoint(MekanoPoint { this, type = vertex, 0, value/2 }); // 4
+         polygon.addPoint(MekanoPoint { this, type = vertex, -value/2+2, value/2-2 });
+         m_fSize = value;
+
+         computeBoundingRadius();
+      }
+   }
+
+   void applyForce(Vector2D v, MekanoPoint pt)
+   {
+      m_AppliedForce.add(m_AppliedForce, v);
+      m_fAppliedTorque += v.dotProduct(pt.localPosition.normal);
+   }
+
+   void step(Time dt)
+   {
+      MekanoObjectPolygonal::step(dt);
+
+      // Translation
+      m_Acceleration.divide(m_AppliedForce, m_fMass);
+
+      m_Speed.x+=m_Acceleration.x*(float)dt;
+      m_Speed.y+=m_Acceleration.y*(float)dt;
+
+      m_Position.x+=m_Speed.x*(float)dt;
+      m_Position.y+=m_Speed.y*(float)dt;
+
+      // Rotation
+      m_fAngularAcceleration=m_fAppliedTorque/m_fInertiaMoment;
+      m_fAngularSpeed+=m_fAngularAcceleration*(float)dt;
+
+      // FIXME: Air friction here?
+      m_fAngularSpeed-=m_fAngularFriction*m_fAngularSpeed;
+
+      rotation += +m_fAngularSpeed*(float)dt;
+      computePoints();
+   }
+
+   MekanoPoint getCertainPoint(ObjBoxPointType type)
+   {
+      List<MekanoPolygon> polygons = this.polygons;
+      if(polygons.count)
+      {
+         MekanoPolygon polygon = polygons[0];
+         int index = 0, lookup = type == topPoint ? 1 : 4;
+         for(p : polygon.points; index++ == lookup)
+            return p;
+      }
+      return null;
+   }
+
+   void draw(MekanoDisplay display)
+   {
+      MekanoObjectPolygonal::draw(display);
+      if(attributes.highlighted)
+         display.drawCrossHair(position, 10);
+   }
+}
diff --git a/samples/guiAndGfx/mekano/mekanosimulation.ec b/samples/guiAndGfx/mekano/mekanosimulation.ec
new file mode 100644 (file)
index 0000000..8bc4adc
--- /dev/null
@@ -0,0 +1,75 @@
+import "mekanodisplay"
+
+class MekanoSimulation
+{
+private:
+   List<MekanoObject> m_Objects { };
+
+   float m_fTimeMultiplier;
+   Time m_LastTime;
+
+public:
+   timeMultiplier = 1.0f;
+   m_LastTime = GetTime();
+
+   property float timeMultiplier
+   {
+      get { return m_fTimeMultiplier; }
+      set { m_fTimeMultiplier=value; }
+   }
+
+   property List<MekanoObject> objectList
+   {
+      get { return m_Objects; }
+   }
+
+   property Time elapsedSeconds
+   {
+      get
+      {
+         Time time = GetTime(), diff=time - m_LastTime;
+         m_LastTime = time;
+         return diff;
+      }
+   }
+
+   void render(MekanoDisplay display)
+   {
+      for(o : m_Objects)
+         o.draw(display);
+   }
+
+   void step(Time dt)
+   {
+      for(o : m_Objects; !o.attributes.steady)
+         o.step(dt);
+   }
+
+   void stepDelta()
+   {
+      step(elapsedSeconds * m_fTimeMultiplier);
+   }
+
+   void exertForces()
+   {
+      for(o : m_Objects)
+         o.exertForces(this);
+   }
+
+   void resetForces()
+   {
+      for(o : m_Objects)
+         o.resetForces();
+   }
+
+   void addObject(MekanoObject object)
+   {
+      incref object;
+      m_Objects.Add(object);
+   }
+
+   void empty()
+   {
+      m_Objects.Free();
+   }
+}
diff --git a/samples/guiAndGfx/mekano/mekanownd.ec b/samples/guiAndGfx/mekano/mekanownd.ec
new file mode 100644 (file)
index 0000000..e2c553b
--- /dev/null
@@ -0,0 +1,161 @@
+import "mekanoobjectfixed"
+import "mekanoobjectspring"
+import "mekanopolygonalbox"
+import "mekanoobjectgravity"
+
+class MekanoWnd : Window
+{
+private:
+   Point mouse;
+   MekanoDisplay display { };
+   MekanoSimulation simulation { timeMultiplier = 4.0f };
+
+   MekanoObjectFixed fix;
+   fix = { simulation, position = { 300, 50 } };
+   MekanoPolygonalBox pbox;
+   pbox = { simulation, size = 20, mass = 10, inertiaMoment = 200 };
+   MekanoObjectSpring spring;
+   spring = { simulation, length = 60, restitution = 30 };
+
+   MekanoObjectGravity gravity { gravity = { 0, 10 } };
+
+   MekanoPolygonalBox lastbox;
+   bool selected, dragging, buttondown;
+
+public:
+
+   caption = "Mekano";
+   hasMaximize = true, hasMinimize = true, hasClose = true;
+   borderStyle = sizable;
+   clientSize = { 640, 480 };
+   background = slateGray;
+
+   BitmapResource bg { ":ecere.bmp", window = this };
+
+   MekanoWnd()
+   {
+      RandomSeed((uint)(((uint64)(GetTime() * 1000)) & MAXDWORD));
+      pbox.position = { GetRandom(0,500), 110 };
+      spring.link(MekanoPoint { fix, type = center }, pbox.getCertainPoint(topPoint));
+      lastbox = pbox;
+   }
+
+   ~MekanoWnd()
+   {
+      simulation.empty();
+   }
+
+   Timer timer
+   {
+      this, started = true;
+
+      bool DelayExpired()
+      {
+         simulation.resetForces();
+
+         simulation.exertForces();
+         simulation.stepDelta();
+
+         if(!buttondown) dragging = false;
+
+         for(object : simulation.objectList)
+         {
+            ObjectAttributes attributes=object.attributes;
+            if(attributes.selected)
+               if(dragging)
+                  object.position = { mouse.x, mouse.y };
+
+            attributes.selected = false;
+            attributes.steady = false;
+
+            if(object.isInside({ mouse.x, mouse.y }))
+            {
+               attributes.highlighted = true;
+               if(buttondown)
+               {
+                  dragging=true;
+                  attributes.selected = true;
+                  attributes.steady = true;
+               }
+            }
+            else
+            {
+               attributes.selected = false;
+               attributes.highlighted = false;
+            }
+            object.attributes = attributes;
+         }
+         Update(null);
+
+         return true;
+      }
+   };
+
+   void OnRedraw(Surface surface)
+   {
+      //surface.Tile(bg.bitmap, 0, 0, clientSize.w, clientSize.h);
+      display.surface = surface;
+      simulation.render(display);
+   }
+
+   bool OnRightButtonDown(int x, int y, Modifiers mods)
+   {
+      /*
+      fix = MekanoObjectFixed { simulation, position = { x, y } };
+      spring = { simulation, length = 30; restitution = 2; };
+
+      spring.link(MekanoPoint { fix, type = center }, pbox.centerPoint);
+      */
+
+      //int c; for(c = 0; c<400; c++) {
+      pbox =
+      {
+         simulation,
+         size = 20,
+         position = { x, y },
+         mass = 10
+      };
+
+      spring =
+      {
+         simulation,
+         length = 40,
+         restitution = 10
+      };
+
+      spring.link(lastbox.getCertainPoint(bottomPoint), pbox.getCertainPoint(topPoint));
+      lastbox=pbox;
+      //}
+      return true;
+   }
+
+   bool OnLeftButtonUp(int x, int y, Modifiers mods)
+   {
+      buttondown=false;
+      ReleaseCapture();
+      return true;
+   }
+
+   bool OnLeftButtonDown(int x, int y, Modifiers mods)
+   {
+      buttondown=true;
+      Capture();
+      return true;
+   }
+
+   bool OnMouseMove(int x, int y, Modifiers mods)
+   {
+      mouse = { x, y };
+      return true;
+   }
+
+   bool OnKeyDown(Key key, unichar ch)
+   {
+      switch(key)
+      {
+         case escape: Destroy(0); return false;
+         case f:      ((GuiApplication)__thisModule.application).fullScreen ^= true; return false;
+      }
+      return true;
+   }
+}
diff --git a/samples/guiAndGfx/mekano/vector.ec b/samples/guiAndGfx/mekano/vector.ec
new file mode 100644 (file)
index 0000000..6223f38
--- /dev/null
@@ -0,0 +1,15 @@
+import IMPORT_STATIC "ecere"
+
+struct Vector2D : Pointf
+{
+   float dotProduct(Vector2D v)            { return x*v.x + y*v.y; }
+   void scale(Vector2D v, float s)         { this = { v.x*s, v.y*s }; }
+   void divide(Vector2D v, float s)        { this = { v.x/s, v.y/s }; }
+   void add(Vector2D a, Vector2D v)        { this = { a.x+v.x, a.y+v.y }; }
+   void subtract(Vector2D a, Vector2D v)   { this = { a.x-v.x, a.y-v.y }; }
+   void normalize()                        { float norm = length; this = norm ? { x / norm, y / norm } : { 0, 0 }; }
+
+   property float length                   { get { return (float)sqrt(x*x+y*y); } }
+   property Vector2D normal                { get { value = { -y, x }; value.normalize(); } }
+   property Vector2D unit                  { get { value = this; value.normalize(); } }
+};