Category Archives: motion

Matrix Zoom and Pan

Actionscript:
  1. [SWF(width=600, height=600, frameRate=30)]
  2. var sw:Number = stage.stageWidth;
  3. var sh:Number = stage.stageHeight;
  4.  
  5. var s:Shape = Shape(addChild(new Shape()));
  6.  
  7. var scale:Number = 1;
  8. var scaleDest:Number = 1;
  9. var down:Boolean = false;
  10. var dx:Number = 0, dy:Number = 0, time:Number = 0;
  11.  
  12. buttonMode = true;
  13.  
  14. addInstructions();
  15. vectorTexture();
  16.  
  17. stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
  18. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  19. addEventListener(Event.ENTER_FRAME, onLoop);
  20.  
  21. function addInstructions():void{
  22.     var instruct:Sprite = Sprite(addChild(new Sprite()));
  23.     with (instruct.graphics) beginFill(0x666666), drawRect(0,0,270, 30);
  24.     instruct.x = instruct.y = 20;
  25.     var txt:TextField = TextField(instruct.addChild(new TextField()));
  26.     txt.defaultTextFormat = new TextFormat("Verdana", 11);
  27.     txt.x = txt.y = 5;
  28.     txt.selectable = false;
  29.     with (txt) textColor = 0xFFFFFF, autoSize = "left", text = "Click and hold to zoom, move mouse to pan";
  30. }
  31.  
  32. function vectorTexture():void{
  33.     var cols:Vector.<uint> = Vector.<uint>([0xFFFFFF, 0x000000]);
  34.     var rnd:Vector.<Number> = new Vector.<Number>(6, true);
  35.    
  36.     for(var i:int = 0 ; i<50; i++){
  37.         with(s.graphics){
  38.             lineStyle(Math.random() * 50 + 2, cols[int(Math.random()*cols.length)]);
  39.             drawCircle(Math.random() * sw, Math.random() * sh, 10 + Math.random() * Math.random() * 400);
  40.         }
  41.     }
  42.     s.graphics.lineStyle(20, 0xCCCCCC);
  43.     s.graphics.drawRect(0, 0,sw, sh);
  44. }
  45.  
  46. function onDown(evt:MouseEvent):void{ down = true; }
  47. function onUp(evt:MouseEvent):void{ down = false; }
  48. function onLoop(evt:Event):void {
  49.    if (down){
  50.      scaleDest *= 1.05;  
  51.      time = 0;
  52.    }else{
  53.      time++;
  54.      // zoom out after 30 iterations
  55.      if (time == 30){
  56.          scaleDest = 1;  
  57.      }
  58.    }
  59.    scale += (scaleDest - scale) / 4;
  60.    if (scale> 10) scale = scaleDest = 10;
  61.    
  62.    dx += (mouseX - dx) / 4;
  63.    dy += (mouseY - dy) / 4;
  64.    if (dx <0) dx = 0;
  65.    if (dy <0) dy = 0;
  66.    if (dx> sw) dx = sw;
  67.    if (dy> sh) dy = sh;
  68.    
  69.    // matrix zoom/pan
  70.    var m:Matrix = s.transform.matrix;
  71.    m.identity();
  72.    m.translate(-dx,-dy);
  73.    m.scale(scale, scale);
  74.    m.translate(dx,dy);
  75.    s.transform.matrix = m;
  76. }

I haven't been by the computer much these last two weeks - been traveling. Going back to nyc tomorrow so I'll go back to posting once a day.

This snippet uses a transformation matrix to zoom in and pan a Sprite instance. For demo purposes I filled the sprite with a few circles - but you'd likely be using this with a vector image of a map, a floor plan or some other graphic that warrants zooming and panning.

Back around flash 7 (I think) before the Matrix class was introduced we used to have to use MovieClip nesting to achieve this effect.


Have a look at the swf...


Also posted in Graphics, matrix | Tagged , , | Leave a comment

globalToLocal() in 3D

Actionscript:
  1. [SWF(frameRate=60, backgroundColor=0x000000, width=500, height=500)]
  2. stage.quality = "medium";
  3. var frame:Sprite = Sprite(addChild(new Sprite()));
  4. with (frame.graphics) beginFill(0xCCCCCC), drawRect(-200, -200, 400, 400), endFill();
  5. frame.x = stage.stageWidth / 2;
  6. frame.y = stage.stageHeight / 2;
  7. frame.z = 100;
  8.  
  9. var canvas:Shape = Shape(frame.addChild(new Shape()));
  10. var msk:Shape = Shape(frame.addChild(new Shape()));
  11. with (msk.graphics) beginFill(0x00FF00), drawRect(-200, -200, 400, 400), endFill();
  12. canvas.mask = msk
  13.  
  14. var txt:TextField = TextField(addChild(new TextField()));
  15. txt.defaultTextFormat = new TextFormat("_sans", 12);
  16. txt.x = txt.y = 10;
  17. txt.textColor = 0xFFFFFF, txt.autoSize="left", txt.text = "Draw on the 3D plane...";
  18.  
  19. stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
  20. stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
  21. function onDown(evt:MouseEvent):void{
  22.     canvas.graphics.lineStyle(4, 0x000000);
  23.     var pnt:Point = frame.globalToLocal(new Point(mouseX, mouseY));
  24.     canvas.graphics.moveTo(pnt.x, pnt.y);
  25.     addEventListener(Event.ENTER_FRAME, onDraw);
  26. }
  27. function onUp(evt:MouseEvent):void{
  28.     removeEventListener(Event.ENTER_FRAME, onDraw);
  29. }
  30.  
  31. var t:Number = 0;
  32. addEventListener(Event.ENTER_FRAME, onLoop);
  33. function onLoop(evt:Event):void {
  34.     frame.rotationY = 35 * Math.sin(t);
  35.     frame.rotationX = 35 * Math.cos(t);
  36.     t+=0.02;
  37. }
  38.  
  39. function onDraw(evt:Event):void {
  40.         var pnt:Point = frame.globalToLocal(new Point(mouseX, mouseY));
  41.     canvas.graphics.lineTo(pnt.x, pnt.y);
  42. }

This demo shows that globalToLocal() works with 3D - saving us the trouble of doing some extra math if we want to draw on 3D display objects...

Have a look at the swf....


Was made aware of this trick by watching a video that kevinSuttle sent me via twitter. The video is an interview with Chris Nuuja (one of the flash player engineers).

Also posted in 3D, DisplayObject, Graphics, misc | Tagged , , | 3 Comments

QuickBox2D Unicycler

Actionscript:
  1. import com.actionsnippet.qbox.*;
  2. import Box2D.Common.Math.*;
  3. import Box2D.Dynamics.Joints.*;
  4. [SWF (backgroundColor=0x000000, width=700, height=600, frameRate=60)]
  5. var sim:QuickBox2D = new QuickBox2D(this, {debug:false,gravityY:10, renderJoints:false});
  6. sim.setDefault({fillColor:0xCC0000,lineAlpha:0});
  7. sim.createStageWalls();
  8.  
  9. for (var i:int = 0; i<20; i++){
  10.     sim.addCircle({x:1+i * 0.8, y:1, radius:0.1+Math.random()*0.1, restitution:0.3});
  11. }
  12.  
  13. sim.setDefault({fillColor:0xEFEFEF, fillAlpha:0.5, lineColor:0xFFFFFF, groupIndex:-2});
  14. var ancA:QuickObject = sim.addCircle({x:20, y:12, radius:0.5, density:0, skin:"none"});
  15. var ancB:QuickObject = sim.addCircle({x:20, y:2, radius:0.5, density:0, skin:"none"});
  16. var head:QuickObject = sim.addCircle({x:0, y:-0.6, radius:0.5});
  17. var neck:QuickObject = sim.addBox({x:0, y:1, width:0.5, height:2});
  18. var spindle:QuickObject = sim.addBox({x:0, y:3, width:0.1, height:2})
  19. spindle.userData.visible = false;
  20. var top:QuickObject = sim.addGroup({objects:[head, neck, spindle], x:10, y:5, fixedRotation:true});
  21. var upperLegA:QuickObject = sim.addBox({x:10, y:7.5, width:0.25, height:1.5});
  22. var lowerLegA:QuickObject = sim.addBox({x:10, y:9, width:0.25, height:1.5});
  23. var wheel:QuickObject = sim.addCircle({x:10, y:9, radius:1});
  24. var upperLegB:QuickObject = sim.addBox({x:10, y:7.5, width:0.25, height:1.5});
  25. var lowerLegB:QuickObject = sim.addBox({x:10, y:9, width:0.25, height:1.5});
  26. var upperArmA:QuickObject = sim.addBox({x:10.7, y:5.25, width:1.5, height:0.25});
  27. var lowerArmA:QuickObject = sim.addBox({x:11.8, y:5.25, width:1.2, height:0.25});
  28. var upperArmB:QuickObject = sim.addBox({x:9.5, y:5.25, width:1.5, height:0.25});
  29. var lowerArmB:QuickObject = sim.addBox({x:8.4, y:5.25, width:1.2, height:0.25});
  30. var anchor:b2Vec2 = new b2Vec2();
  31. function connect(a:QuickObject, b:QuickObject, lower:Number, upper:Number, offX:Number=0, offY:Number = 0):QuickObject{
  32.      var min:Number = Math.min(a.y, b.y);
  33.      var max:Number = Math.max(a.y, b.y);
  34.      anchor.y = min + (max - min) * 0.5 + offY;
  35.      min = Math.min(a.x, b.x);
  36.      max = Math.max(a.x, b.x);
  37.      anchor.x = min + (max - min) * 0.5 + offX;
  38.      return sim.addJoint({type:"revolute", a:a.body, b:b.body, x1:anchor.x, y1:anchor.y, lowerAngle:lower, upperAngle:upper});
  39. }
  40. sim.addJoint({type:"revolute", a:wheel.body, b:top.body, x1:wheel.x, y1:wheel.y});
  41. sim.addJoint({type:"revolute", a:lowerLegA.body, b:wheel.body, x1:lowerLegA.x, y1:lowerLegA.y + 0.7});
  42. sim.setDefault({enableLimit:true});
  43. connect(top, upperArmA, 0, 3, -.3, .1);
  44. connect(upperArmA, lowerArmA, -2.5, -1, 0, 0);
  45. connect(top, upperArmB, -3, 0, .3, .1);
  46. connect(upperArmB, lowerArmB, -2.5, -1, 0, 0);
  47. sim.addJoint({type:"distance", a:lowerArmA.body, b:ancA.body, frequencyHz:0.3});
  48. sim.addJoint({type:"distance", a:lowerArmB.body, b:ancB.body, frequencyHz:0.3});
  49. sim.setDefault({enableLimit:true});
  50. connect(top, upperLegA, -6.28, 6, 0, 0.8);
  51. connect(upperLegA, lowerLegA, .1, 6);
  52. wheel.angle = Math.PI;
  53. connect(top, upperLegB, -6.28, 6, 0, 0.8);
  54. connect(upperLegB, lowerLegB, .1, 6);
  55. sim.setDefault({enableLimit:false, type:"revolute"});
  56. sim.addJoint({a:lowerLegB.body, b:wheel.body, x1:lowerLegB.x, y1:lowerLegB.y + 0.7});
  57.  
  58. sim.start();
  59. sim.mouseDrag();
  60.  
  61. var sd:Number = 5, s:Number = 0, t:Number = 0;
  62. addEventListener(Event.ENTER_FRAME, function():void{
  63.     top.angle = Math.cos(t += 0.05) * 0.2;
  64.     if (top.x> 19) sd = -5
  65.     if (top.x <5) sd = 5;
  66.     s += (sd - s) / 8;
  67.     wheel.body.SetAngularVelocity(s);
  68. });

This chunk of code uses QuickBox2D to create a simple looking character riding a unicycle...

Have a look at the swf...

Also posted in Box2D, QuickBox2D | Tagged , , , , | 9 Comments

QuickBox2D Floating Walker

Actionscript:
  1. import com.actionsnippet.qbox.*;
  2. import Box2D.Common.Math.*;
  3. import Box2D.Dynamics.Joints.*;
  4.  
  5. [SWF (backgroundColor=0x000000, width=700, height=600, frameRate=60)]
  6.  
  7. var sim:QuickBox2D = new QuickBox2D(this, {gravityY:.2});
  8.  
  9. sim.setDefault({fillColor:0xEFEFEF, fillAlpha:0.5, lineColor:0xFFFFFF});
  10.  
  11. sim.createStageWalls();
  12.  
  13. for (var i:int = 0; i<20; i++){
  14.     sim.addCircle({x:1+i, y:1, radius:0.2, restitution:1});
  15. }
  16.  
  17. var head:QuickObject = sim.addCircle({x:0, y:0, radius:0.5});
  18. var neck:QuickObject = sim.addBox({x:0, y:1, width:0.5, height:2});
  19. var top:QuickObject = sim.addGroup({objects:[head, neck], x:10, y:5, fixedRotation:true});
  20.  
  21. var upperLegA:QuickObject = sim.addBox({x:10, y:7.5, width:0.25, height:1.5,groupIndex:-2});
  22. var lowerLegA:QuickObject = sim.addBox({x:10, y:9, width:0.25, height:1.5,groupIndex:-2});
  23.  
  24. var upperLegB:QuickObject = sim.addBox({x:10, y:7.5, width:0.25, height:1.5,groupIndex:-2});
  25. var lowerLegB:QuickObject = sim.addBox({x:10, y:9, width:0.25, height:1.5,groupIndex:-2});
  26.  
  27. var anchor:b2Vec2 = new b2Vec2();
  28. function connect(a:QuickObject, b:QuickObject, lower:Number, upper:Number, offX:Number=0, offY:Number = 0):QuickObject{
  29.      var min:Number = Math.min(a.y, b.y);
  30.      var max:Number = Math.max(a.y, b.y);
  31.      anchor.y = min + (max - min) * 0.5 + offY;
  32.      min = Math.min(a.x, b.x);
  33.      max = Math.max(a.x, b.x);
  34.      anchor.x = min + (max - min) * 0.5 + offX;
  35.      return sim.addJoint({a:a.body, b:b.body, x1:anchor.x, y1:anchor.y, lowerAngle:lower, upperAngle:upper});
  36. }
  37.  
  38. sim.setDefault({type:"revolute", collideConnected:false, enableLimit:true, enableMotor:true, maxMotorTorque:250, lineColor:0xFFFFFF});
  39.  
  40. var upperLegAJoint:QuickObject = connect(top, upperLegA, -1, 1, 0, 0.8);
  41. var upperLegBJoint:QuickObject = connect(top, upperLegB, -1, 1, 0, 0.8);
  42.  
  43. var lowerLegAJoint:QuickObject = connect(upperLegA, lowerLegA, -1, 1);
  44. var lowerLegBJoint:QuickObject = connect(upperLegB, lowerLegB, -1, 1);
  45.  
  46. sim.start();
  47. sim.mouseDrag();
  48.  
  49.  
  50. var j:b2RevoluteJoint;
  51. var t:Number = -Math.PI * 0.5;
  52. addEventListener(Event.ENTER_FRAME, onLoop);
  53. function onLoop(evt:Event):void {
  54.    
  55.     var s1:Number = 1*Math.sin(t);
  56.     var s2:Number = 1*Math.sin(t+Math.PI);
  57.     t+= 0.05;
  58.    
  59.     top.body.SetAngularVelocity(s1 * 0.25);
  60.    
  61.     j =  upperLegAJoint.joint as b2RevoluteJoint;
  62.     j.SetMotorSpeed(s1);
  63.    
  64.     j =  upperLegBJoint.joint as b2RevoluteJoint;
  65.     j.SetMotorSpeed(s2);
  66.      
  67.     j = lowerLegAJoint.joint as b2RevoluteJoint;
  68.     j.SetMotorSpeed(-s2);
  69.    
  70.     j = lowerLegBJoint.joint as b2RevoluteJoint;
  71.     j.SetMotorSpeed(-s1);
  72. }

Experimenting with primitive robot designs... this was just an improvised design - it looks like it may make sense to break out a pencil and some paper - or my wacom... to get something interesting...


Have a look at the swf...


Also posted in Box2D, QuickBox2D | Tagged , , , , | 1 Comment