// forked from paq's [Box2D] Spring
package
{
import Box2D.Common.b2Color;
import Box2D.Common.Math.b2Math;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import com.actionsnippet.qbox.objects.CircleObject;
import com.actionsnippet.qbox.QuickBox2D;
import com.actionsnippet.qbox.QuickObject;
import flash.display.Graphics;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import net.hires.debug.Stats;
/**
* @see http://personal.boristhebrave.com/project/controller-grab-pack
* @author paq89
*/
[SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="60")]
public class Main extends Sprite
{
private var _canvas:MovieClip;
private var _line:Shape;
private var _qbox:QuickBox2D;
private var _springs:Array;
private var _circles:Array;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// background
var g:Graphics = graphics;
g.beginFill(0xFFFFFF, 1);
g.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
// canvas
_canvas = new MovieClip();
addChild(_canvas);
// line
_line = new Shape();
addChild(_line);
// quickbox2d
_qbox = new QuickBox2D(_canvas);
_qbox.mouseDrag();
_qbox.start();
_qbox.createStageWalls();
_circles = [];
_springs = [];
var prev:QuickObject;
for (var i:int = 0; i < 5; i++)
{
var circle:QuickObject = _qbox.addCircle( { x:1 + Math.random() * (stage.stageWidth - 60) / 30,
y:1 + Math.random() * (stage.stageHeight - 60) / 30} );
_circles.push(circle);
if (prev)
{
var spring:b2SpringController = new b2SpringController();
spring.SetBody1(prev.body);
spring.SetBody2(circle.body);
spring.length = 1;
spring.damping = 0;
spring.k = 10;
_springs.push(spring);
}
prev = circle;
}
addEventListener(Event.ENTER_FRAME, loop);
addChild(new Stats);
}
private function loop(e:Event):void
{
_line.graphics.clear();
var len:int = _springs.length;
for (var i:int = 0; i < len; i++)
{
_springs[i].Step(_qbox.timeStep);
springDraw(_springs[i]);
}
}
private function springDraw(spring:b2SpringController):void
{
//Number of bends the spring makes
var kinks:Number = 10;
//Stub at either end that is unkinked
var stub:Number = 0.05;
//Kink width, as fraction of length
var kinkWidth:Number = 0.05;
//Set kink width s.t. kinks are at right angles at natural length
kinkWidth = (1-2*stub)/kinks/2;
//Color of spring
var color:Number = 0x333333;
var world1:b2Vec2 = spring.m_body1.GetWorldPoint(spring.m_anchor1);
var world2:b2Vec2 = spring.m_body2.GetWorldPoint(spring.m_anchor2);
var d:b2Vec2 = b2Math.SubtractVV(world2, world1);
var dlen:Number = d.Normalize();
var nX:Number = d.y * kinkWidth * spring.length * 4;
var nY:Number = -d.x * kinkWidth * spring.length * 4;
var p2:b2Vec2 = new b2Vec2(world1.x * (1 - stub) + world2.x * stub, world1.y * (1 - stub) + world2.y * stub);
drawSegment(world1,p2,color);
var prevp:b2Vec2 = p2;
for (var i:int = 0; i <= kinks; i++)
{
var alt:Number = i % 2 == 0 ? -1:1;
var pi:b2Vec2 = new b2Vec2(
world1.x + d.x * dlen * (stub + (1 - 2 * stub) * i / kinks) + nX * alt,
world1.y + d.y * dlen * (stub + (1 - 2 * stub) * i / kinks) + nY * alt
);
drawSegment(prevp, pi, color);
prevp = pi;
}
pi = new b2Vec2(world2.x * (1 - stub) + world1.x * stub, world2.y * (1 - stub) + world1.y * stub);
drawSegment(prevp,pi,color);
drawSegment(pi,world2,color);
}
private function drawSegment(p1:b2Vec2, p2:b2Vec2, color:Number):void
{
var g:Graphics = _line.graphics;
g.lineStyle(1, color, 1);
g.moveTo(p1.x * 30, p1.y * 30);
g.lineTo(p2.x * 30, p2.y * 30);
}
}
}
/*
* Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
import Box2D.Dynamics.*;
import Box2D.Common.Math.*;
import Box2D.Common.*;
/// Base class for controllers. Controllers are a convience for encapsulating common
/// per-step functionality
internal class b2Controller
{
public var m_next:b2Controller;
public var m_prev:b2Controller;
public function GetNext():b2Controller{return m_next;}
public function GetPrev():b2Controller{return m_prev;}
public virtual function Step(timestep:Number):void {}
public virtual function Draw(debugDraw:b2DebugDraw):void {}
}
/// Base class for controllers. Controllers are a convience for encapsulating common
/// per-step functionality
internal class b2PairController extends b2Controller
{
public var m_body1:b2Body;
public var m_body2:b2Body;
/// Get the anchor point on body1 in world coordinates.
public virtual function GetAnchor1():b2Vec2{return null};
/// Get the anchor point on body1 in world coordinates.
public virtual function SetAnchor1(v:b2Vec2):void{};
/// Get the anchor point on body2 in world coordinates.
public virtual function GetAnchor2():b2Vec2{return null};
/// Set the anchor point on body1 in world coordinates.
public virtual function SetAnchor2(v:b2Vec2):void{};
/// Get the first body attached to this joint.
public function GetBody1():b2Body
{
return m_body1;
}
/// Get the first body attached to this joint.
public function SetBody1(b:b2Body):void
{
m_body1 = b;
}
/// Get the second body attached to this joint.
public function GetBody2():b2Body
{
return m_body2;
}
/// Get the second body attached to this joint.
public function SetBody2(b:b2Body):void
{
m_body2 = b;
}
}
/// Applies Hooke forces between a pair of bodies
internal class b2SpringController extends b2PairController
{
/// Spring constant
public var k:Number = 1;
/// Damping constant
public var damping:Number = 0;
/// Natural length of spring
public var length:Number = 0;
public var m_anchor1:b2Vec2 = new b2Vec2();
public var m_anchor2:b2Vec2 = new b2Vec2();
/// Get the anchor point on body1 in world coordinates.
public override function GetAnchor1():b2Vec2{
return m_body1.GetWorldPoint(m_anchor1);
}
/// Get the anchor point on body1 in world coordinates.
public override function SetAnchor1(v:b2Vec2):void{
m_anchor1.SetV(m_body1.GetLocalPoint(v));
}
/// Get the anchor point on body2 in world coordinates.
public override function GetAnchor2():b2Vec2{
return m_body2.GetWorldPoint(m_anchor2);
}
/// Set the anchor point on body1 in world coordinates.
public override function SetAnchor2(v:b2Vec2):void{
m_anchor2.SetV(m_body2.GetLocalPoint(v));
}
public override function Step(timestep:Number):void{
//Skip if asleep
if(m_body1.IsSleeping() && m_body2.IsSleeping())
return;
var world1:b2Vec2 = m_body1.GetWorldPoint(m_anchor1);
var world2:b2Vec2 = m_body2.GetWorldPoint(m_anchor2);
var d:b2Vec2 = b2Math.SubtractVV(world2,world1);
var dlen:Number = d.Normalize();
//Spring force
var springForce:b2Vec2 = d.Copy();
springForce.Multiply( (dlen - length) * k );
m_body1.ApplyForce(springForce, world1);
m_body2.ApplyForce(springForce.Negative(), world2);
//Damping
if(damping!=0){
var v1:b2Vec2 = m_body1.GetLinearVelocityFromWorldPoint(world1);
var v2:b2Vec2 = m_body2.GetLinearVelocityFromWorldPoint(world2);
var v:b2Vec2 = b2Math.SubtractVV(v2,v1);
var dampingForce:b2Vec2 = d.Copy();
dampingForce.Multiply(b2Math.b2Dot(v,d) * damping);
m_body1.ApplyForce(dampingForce, world1);
m_body2.ApplyForce(dampingForce.Negative(), world2);
}
}
/// Draws a spring in a typical zig-zagging fashion
public override function Draw(debugDraw:b2DebugDraw):void
{
//Number of bends the spring makes
var kinks:Number = 10;
//Stub at either end that is unkinked
var stub:Number = 0.05;
//Kink width, as fraction of length
var kinkWidth:Number = 0.05;
//Set kink width s.t. kinks are at right angles at natural length
kinkWidth = (1-2*stub)/kinks/2;
//Color of spring
var color:b2Color = new b2Color(0xB8/255,0x86/255,0x0B/255);
var world1:b2Vec2 = m_body1.GetWorldPoint(m_anchor1);
var world2:b2Vec2 = m_body2.GetWorldPoint(m_anchor2);
var d:b2Vec2 = b2Math.SubtractVV(world2,world1);
var dlen:Number = d.Normalize();
var nX:Number=d.y*kinkWidth*length;
var nY:Number=-d.x*kinkWidth*length;
var p2:b2Vec2 = new b2Vec2(world1.x*(1-stub)+world2.x*stub,world1.y*(1-stub)+world2.y*stub);
debugDraw.DrawSegment(world1,p2,color);
var prevp:b2Vec2=p2;
for(var i:int=0;i<=kinks;i++){
var alt:Number = i%2==0?-1:1;
var pi:b2Vec2 = new b2Vec2(
world1.x+d.x*dlen*(stub+(1-2*stub)*i/kinks)+nX*alt,
world1.y+d.y*dlen*(stub+(1-2*stub)*i/kinks)+nY*alt
);
debugDraw.DrawSegment(prevp,pi,color);
prevp=pi;
}
pi = new b2Vec2(world2.x*(1-stub)+world1.x*stub,world2.y*(1-stub)+world1.y*stub);
debugDraw.DrawSegment(prevp,pi,color);
debugDraw.DrawSegment(pi,world2,color);
}
}