--- /dev/null
+import "mekanownd"
+
+MekanoWnd mekano { displayDriver = "OpenGL"; };
--- /dev/null
+{
+ "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"
+ ]
+}
--- /dev/null
+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;
+ }
+ }
+}
--- /dev/null
+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();
+ }
+}
--- /dev/null
+import "mekanoobject"
+
+define CROSSHAIRWIDTH = 10;
+
+class MekanoObjectFixed : MekanoObject
+{
+public:
+ void draw(MekanoDisplay display)
+ {
+ display.drawCrossHair(m_Position, CROSSHAIRWIDTH);
+ }
+};
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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;
+ }
+};
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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();
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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(); } }
+};