Skip to content

Instantly share code, notes, and snippets.

@kukulski
Last active December 10, 2015 17:08
Show Gist options
  • Select an option

  • Save kukulski/4465533 to your computer and use it in GitHub Desktop.

Select an option

Save kukulski/4465533 to your computer and use it in GitHub Desktop.
PanZoom logic -- logic to maintain expected 1:1 correspondence with dragged content as it is simultaneously dragged and scaled
/**
I'm building a plugin for Illustrator enable the expected behavior for multitouch pan-zoom,
and needed to get the geometry right for the CoreImage draw API, (for fast feedback)
so here's a tiny as3 implementation that confirms the correct behavior
*/
package {
import flash.geom.Point;
import flash.geom.Rectangle;
public class RectScaler {
private var offset:Point;
private var offsetNow:Point = new Point;
private var scaleNow:Number;
public function startDrag(downLocation:Point):void {
offset = new Point(-downLocation.x, -downLocation.y);
}
public function setPanZoom(cursor:Point, scale:Number):void {
offsetNow.x = offset.x * scale + cursor.x;
offsetNow.y = offset.y * scale + cursor.y;
scaleNow = scale;
}
public function transformRect(r:Rectangle):Rectangle {
return new Rectangle(
r.x*scaleNow + offsetNow.x,
r.y*scaleNow + offsetNow.y,
r.width*scaleNow,
r.height*scaleNow);
}
}
}
class RectTransformer {
public:
void startDrag(const AIRealPoint &startPosition, NSRect bounds) {
centeredAroundCursor = bounds;
centeredAroundCursor.origin.x -= startPosition.h;
centeredAroundCursor.origin.y -= startPosition.v;
}
NSRect panZoom(float scale, const AIRealPoint &newCenter) {
NSRect scaledAroundCursor = scaleAll(centeredAroundCursor,scale);
scaledAroundCursor.origin.x += newCenter.h;
scaledAroundCursor.origin.y += newCenter.v;
return scaledAroundCursor;
}
private:
NSRect scaleAll(const NSRect &r, float scale) {
return {{r.origin.x * scale, r.origin.y * scale},
{r.size.width * scale, r.size.height * scale}};
}
NSRect centeredAroundCursor;
};
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
public class TestScaleRects extends Sprite
{
private var rects:Vector.<Rectangle> = new <Rectangle>[
new Rectangle(100,100,250,140),
new Rectangle(0,100,250,140),
new Rectangle(50,50,250,140),
new Rectangle(100,150,250,140)];
private var downPoint:Point;
private var currentPoint:Point = new Point;
private var scaler:RectScaler = new RectScaler;
private var theta:Number = 0;
public function TestScaleRects()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
addEventListener(Event.ENTER_FRAME, redrawEveryFrame);
}
protected function redrawEveryFrame(event:Event):void
{
theta += .05;
var scale:Number = .5 + .25 * Math.sin(theta);
graphics.clear();
if(downPoint) scaler.setPanZoom(currentPoint, scale);
for(var i:int =0; i < rects.length; i++) {
var drawRect:Rectangle = downPoint ? scaler.transformRect(rects[i]) : rects[i];
/* this is not how you use the flash display list.
we do this to mirror the usage within a drag-loop in a Cocoa app */
graphics.lineStyle(3*scale,0);
graphics.drawRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height);
}
}
protected function onUp(event:MouseEvent):void
{
downPoint = null;
}
protected function onMove(event:MouseEvent):void
{
currentPoint.x = event.stageX;
currentPoint.y = event.stageY;
}
protected function onDown(event:MouseEvent):void
{
downPoint = new Point(event.stageX, event.stageY);
scaler.startDrag(downPoint);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment