Saturday, February 25, 2012

Creating a Flex AIR Annotator app: Part 6

In this part we will add the line drawing functionality.

Last time we did pen drawing, this time let's do line drawing.

Because we have to preview the line before the mouse is actually released, we need to have another Group object similar to drawSprite - tempSprite. Put it underneath drawSprite (so that it actually displays on top of drawSprite), also make their container drawGroup a Group object (I changed it to Box last time, but figured it would be better this way).

<mx:Canvas width="100%" height="100%" horizontalScrollPolicy="on" verticalScrollPolicy="on" rollOver="canvasOver();" rollOut="canvasOut();">
<mx:Box backgroundColor="#eeeeee" width="100%" height="100%" verticalAlign="middle" horizontalAlign="center" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Box id="drawArea" backgroundColor="#ffffff" width="300" height="200" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:ViewStack id="stack" change="stackChange();">
<s:NavigatorContent>
<custom:BigButton icon="@Embed('../lib/folder.png')" subtext="Import picture" toolTip="Open file" enabled="true" buttonMode="true" bWidth="300" bHeight="200" click="importPicture();" />
</s:NavigatorContent>
<s:NavigatorContent>
<mx:Image id="imgHolder" />
<s:Group id="drawGroup">
<s:Group id="drawSprite" />
<s:Group id="tempSprite" />
</s:Group>
<mx:Box id="drawHitArea" alpha="0" backgroundColor="#000000" mouseDown="drawDown();" mouseMove="drawMove();"/>
</s:NavigatorContent>
</mx:ViewStack>
</mx:Box>
</mx:Box>
</mx:Canvas>

Declare 4 variables - anchorX, anchorY, lastX and lastY.

private var anchorX:Number;
private var anchorY:Number;
private var lastX:Number;
private var lastY:Number;

Now go to drawDown() and add a conditional which checks if the line tool is selected. If it is, set drawMode to line, moveTo() mouse coordinates on the tempSprite object and set anchorX and anchorY to mouse coordinates. These will be the values that will remain the same until the user releases the mouse.

private function drawDown():void {
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 0) {
drawMode = "pen";
drawSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 3) {

drawMode = "line";
tempSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
}

Now go to drawMove() and add a conditional checking if drawMode is set to line. If true, clear tempSprite's graphic data and draw a line going from anchor positions to current mouse position. Also, set lastX and lastY to current mouse position.

private function drawMove():void {
if (drawMode == "pen") {
drawSprite.graphics.lineStyle(lineThickness, lineColor);
drawSprite.graphics.lineTo(drawArea.mouseX, drawArea.mouseY);
}
if (drawMode == "line") {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
tempSprite.graphics.moveTo(anchorX, anchorY);
lastX = drawArea.mouseX;
lastY = drawArea.mouseY;
tempSprite.graphics.lineTo(lastX, lastY);
}
}

When the mouse is finally released, the drawUp function is called. Here, we check if drawMode was line, and if true clear tempSprite graphics and add the line to drawSprite using anchor and last coordinate values.

private function drawUp(evt:MouseEvent):void {
if (drawMode == "line") {
tempSprite.graphics.clear();
drawSprite.graphics.lineStyle(lineThickness, lineColor);
drawSprite.graphics.moveTo(anchorX, anchorY);
drawSprite.graphics.lineTo(lastX, lastY);
}
drawMode = "";
}

And that's it! The line drawing works!

Full code:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
   xmlns:s="library://ns.adobe.com/flex/spark"
   xmlns:mx="library://ns.adobe.com/flex/mx" 
   xmlns:custom="*"
   showStatusBar="false"
   creationComplete="init();" minWidth="700" minHeight="400"
   width="700" height="500">

<fx:Script>
<![CDATA[
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.FocusDirection;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.filters.DropShadowFilter;
import flash.geom.Matrix;
import flash.net.FileFilter;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import mx.controls.Alert;
import flash.filesystem.FileMode;

private var bitmapData:BitmapData;
private var content:*;
[Bindable]
private var buttonsEnabled:Boolean = false;
[Bindable]
private var lineThickness:int = 5;
[Bindable]
private var lineColor:uint = 0xff0000;

[Embed(source="../lib/cursor_cross.png")]
private var crossCursor:Class;

private var drawMode:String = "";
private var anchorX:Number;
private var anchorY:Number;
private var lastX:Number;
private var lastY:Number;

private function init():void{
drawArea.filters = [new DropShadowFilter(4, 60, 0, 0.7, 10, 10, 1, 3)];
addEventListener(MouseEvent.MOUSE_UP, drawUp);
}

private function canvasOver():void {
if (toolbarStack.selectedIndex == 2) {
cursorManager.setCursor(crossCursor, 2, -10, -10);
}
}

private function canvasOut():void {
cursorManager.removeAllCursors();
}

private function drawDown():void {
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 0) {
drawMode = "pen";
drawSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 3) {

drawMode = "line";
tempSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
}

private function drawUp(evt:MouseEvent):void {
if (drawMode == "line") {
tempSprite.graphics.clear();
drawSprite.graphics.lineStyle(lineThickness, lineColor);
drawSprite.graphics.moveTo(anchorX, anchorY);
drawSprite.graphics.lineTo(lastX, lastY);
}
drawMode = "";
}

private function drawMove():void {
if (drawMode == "pen") {
drawSprite.graphics.lineStyle(lineThickness, lineColor);
drawSprite.graphics.lineTo(drawArea.mouseX, drawArea.mouseY);
}
if (drawMode == "line") {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
tempSprite.graphics.moveTo(anchorX, anchorY);
lastX = drawArea.mouseX;
lastY = drawArea.mouseY;
tempSprite.graphics.lineTo(lastX, lastY);
}
}

private function importPicture():void {
var file:File = new File();
var imageFilter:FileFilter = new FileFilter("Images", "*.jpg;*jpeg;*.gif;*.png");
file.browseForOpen("Import picture", [imageFilter]);
file.addEventListener(Event.SELECT, fileSelect);
}

private function fileSelect(evt:Event):void {
var file:File = evt.target as File;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load(new URLRequest(file.url));
}

private function loadComplete(evt:Event):void {
content = evt.target.content;
bitmapData = new BitmapData(content.width, content.height, false);
bitmapData.draw(content, new Matrix(), null, null, null, true);
stackChange(); // needed if selectedIndex was already 1 when the function was called
stack.selectedIndex = 1;
}

private function stackChange():void {
if (stack.selectedIndex == 1) {
imgHolder.source = new Bitmap(bitmapData);
drawArea.width = content.width;
drawArea.height = content.height;
drawGroup.width = content.width;
drawGroup.height = content.height;
buttonsEnabled = true;
}
}

private function toolbarChange():void {
if (toolbarStack.selectedIndex == 2) {
updateLineExample();
}
}

private function updateLineExample():void{
lineExample.graphics.clear();
lineExample.graphics.lineStyle(lineThickness, lineColor);
lineExample.graphics.moveTo(10, 10);
lineExample.graphics.lineTo(30, 10);

drawHitArea.width = content.width - lineThickness;
drawHitArea.height = content.height - lineThickness;
drawHitArea.x = lineThickness / 2;
drawHitArea.y = lineThickness / 2;
}
]]>
</fx:Script>

<fx:Declarations>
<mx:ArrayCollection id="toolbarToggle">
<fx:Object icon="@Embed('../lib/ic_pen.png')" />
<fx:Object icon="@Embed('../lib/ic_ellipse.png')"/>
<fx:Object icon="@Embed('../lib/ic_rect.png')"/>
<fx:Object icon="@Embed('../lib/ic_line.png')"/>
</mx:ArrayCollection>
</fx:Declarations>

<s:VGroup width="100%" height="100%" gap="0">
<mx:HBox backgroundColor="#ccccdd" width="100%" height="52">
<custom:IconButton icon="@Embed('../lib/new.png')" toolTip="New" enabled="{buttonsEnabled}" buttonMode="true" click="importPicture();" />
<custom:IconButton icon="@Embed('../lib/move.png')" toolTip="Move" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=1;" />
<custom:IconButton icon="@Embed('../lib/draw.png')" toolTip="Draw" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=2;" />
<custom:IconButton icon="@Embed('../lib/bubble.png')" toolTip="Annotation" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=3;" />
<custom:IconButton icon="@Embed('../lib/cut.png')" toolTip="Cut" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=4;" />
<custom:IconButton icon="@Embed('../lib/save.png')" toolTip="Save" enabled="{buttonsEnabled}" buttonMode="true" />
<mx:ViewStack id="toolbarStack" width="100%" height="100%" change="toolbarChange();">
<s:NavigatorContent>

</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Move</s:Label>
<s:Label>Click and hold your mouse on annotations to move them.</s:Label>
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Draw</s:Label>
<s:HGroup>
<mx:ToggleButtonBar dataProvider="{toolbarToggle}" width="100" id="drawTools" />
<mx:ColorPicker selectedColor="@{lineColor}" change="updateLineExample();" />
<mx:Slider minimum="1" maximum="15" change="updateLineExample();" value="@{lineThickness}" width="80" liveDragging="true"/>
<s:SpriteVisualElement width="40" height="20" id="lineExample"/>
<s:Button click="drawSprite.graphics.clear();" label="Clear all" />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Annotation</s:Label>
<s:HGroup>
<s:TextInput width="180" text="Insert text here" />
<mx:ColorPicker />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Cut</s:Label>
<s:HGroup>
<s:Label>Select an area to crop.</s:Label>
<s:Button label="Crop selected area" />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
</mx:ViewStack>
</mx:HBox>
<mx:Canvas width="100%" height="100%" horizontalScrollPolicy="on" verticalScrollPolicy="on" rollOver="canvasOver();" rollOut="canvasOut();">
<mx:Box backgroundColor="#eeeeee" width="100%" height="100%" verticalAlign="middle" horizontalAlign="center" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Box id="drawArea" backgroundColor="#ffffff" width="300" height="200" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:ViewStack id="stack" change="stackChange();">
<s:NavigatorContent>
<custom:BigButton icon="@Embed('../lib/folder.png')" subtext="Import picture" toolTip="Open file" enabled="true" buttonMode="true" bWidth="300" bHeight="200" click="importPicture();" />
</s:NavigatorContent>
<s:NavigatorContent>
<mx:Image id="imgHolder" />
<s:Group id="drawGroup">
<s:Group id="drawSprite" />
<s:Group id="tempSprite" />
</s:Group>
<mx:Box id="drawHitArea" alpha="0" backgroundColor="#000000" mouseDown="drawDown();" mouseMove="drawMove();"/>
</s:NavigatorContent>
</mx:ViewStack>
</mx:Box>
</mx:Box>
</mx:Canvas>
</s:VGroup>

</s:WindowedApplication>

Thanks for reading!

No comments:

Post a Comment