/**
* Copyright russ ( http://wonderfl.net/user/russ )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/2thJ
*/
package
{
import flash.display.*;
import flash.events.Event;
import flash.text.*;
import flash.utils.*;
public class LineDrawingTest2 extends Sprite {
private var _Logger:TextField = new TextField();
// CHANGE NUMBER OF DATA POINTS HERE!!
// CHANGE NUMBER OF DATA POINTS HERE!!
// CHANGE NUMBER OF DATA POINTS HERE!!
// CHANGE NUMBER OF DATA POINTS HERE!!
private const NUM_LINES_TO_DRAW:int = 30000;
private var _Result:Bitmap = new Bitmap();
private var _SpiralData:Vector.<Vector.<Number>>;
private var _SinData:Vector.<Vector.<Number>>;
private var _TestSequence:Array;
public function LineDrawingTest2() {
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
_Logger.autoSize = TextFieldAutoSize.LEFT;
addChild(_Logger);
addChild(_Result);
_Result.x = 300;
//Generate the data arrays...
_SpiralData = _GenerateSpiral(NUM_LINES_TO_DRAW, 2, 500); // (30000, 2, 500) matches original LineDrawingTest
var amplitude:Number = 200;
_SinData = _GenerateSine(1000, amplitude, amplitude, 1.0/1000, 100);
_TestSequence = [
[_TestEFLA, ["Spiral - EFLA: ", _SpiralData, ]],
[_TestLineTo, ["Spiral - lineTo(LOW): ", StageQuality.LOW, _SpiralData]],
[_TestLineTo, ["Spiral - lineTo(MEDIUM): ", StageQuality.MEDIUM, _SpiralData]],
[_TestLineTo, ["Spiral - lineTo(HIGH): ", StageQuality.HIGH, _SpiralData]],
[_TestLineTo, ["Spiral - lineTo(BEST): ", StageQuality.BEST, _SpiralData]],
[_TestDrawPath, ["Spiral - drawPath(LOW): ", StageQuality.LOW, _SpiralData]],
[_TestDrawPath, ["Spiral - drawPath(MEDIUM): ", StageQuality.MEDIUM, _SpiralData]],
[_TestDrawPath, ["Spiral - drawPath(HIGH): ", StageQuality.HIGH, _SpiralData]],
[_TestDrawPath, ["Spiral - drawPath(BEST): ", StageQuality.BEST, _SpiralData]],
// [_TestDrawPath, ["Sin - drawPath(LOW): ", StageQuality.LOW, _SinData]],
];
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void {
var curTest:Array;
var func:Function;
var funcArgs:Array;
curTest = _TestSequence.shift();
if(_TestSequence.length == 0) {
stage.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
func = curTest[0] as Function;
funcArgs = curTest[1] as Array;
func.apply(this, funcArgs);
}
private function _GenerateSine(Duration:Number, Amplitude:Number, Offset:Number, Frequency:Number, NumPts:uint):Vector.<Vector.<Number>> {
var step:Number = Duration/(NumPts - 1);
var i:uint;
var x:Number;
var y:Number;
var xData:Vector.<Number> = new Vector.<Number>();
var yData:Vector.<Number> = new Vector.<Number>();
var pi:Number = Math.PI;
x = 0;
for(i = 0; i < NumPts; ++i) {
y = Amplitude * Math.sin(2*pi*Frequency*x) + Offset;
xData.push(x);
yData.push(y);
x += step;
}
var ret:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
ret.push(xData);
ret.push(yData);
return ret;
}
private function _GenerateSpiral(NumLines:uint, dv:Number, Size:uint):Vector.<Vector.<Number>> {
// returns two Number vectors, the x array and y array
var numOrbits:uint = Size / (2*dv);
var linesCreated:uint = 0;
var xData:Vector.<Number> = new Vector.<Number>();
var yData:Vector.<Number> = new Vector.<Number>();
var dx:uint = 0;
var dy:uint = 0;
var i:uint;
while(linesCreated < NumLines) {
dx = dy = 0;
for(i = 0; i < numOrbits; ++i) {
//top left....
xData.push(dx);
yData.push(dy);
//top right...
xData.push(Size - dx);
yData.push(dy);
//bottom right...
xData.push(Size - dx);
yData.push(Size - dy);
// bottom left...
dx += dv;
xData.push(dx);
yData.push(Size - dy);
//set up top left again...
dy += dv;
//exit check...
linesCreated += 4;
if (linesCreated >= NumLines) {
break;
}
}
}
var ret:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
ret.push(xData);
ret.push(yData);
return ret;
}
private function _TestEFLA(LogMsg:String, Data:Vector.<Vector.<Number>>):void {
var startTime_ms:uint;
var numPts:uint = Data[0].length;
var i:uint;
var bmd:BitmapData = new BitmapData(1000, 1000, false, 0xffffff);
var xData:Vector.<Number> = Data[0];
var yData:Vector.<Number> = Data[1];
var lineColor:uint = 0x0000ff;
// the extra vector indexing in the loop below is not quite fair to the EFLA approach, but it is definitely
// a realistic situation for line drawing...
startTime_ms = getTimer();
for(i = 1; i < numPts; ++i) {
EFLA(xData[i - 1], yData[i - 1], xData[i], yData[i], lineColor, bmd);
}
log(LogMsg + (getTimer() - startTime_ms) + "ms -- " + NUM_LINES_TO_DRAW + " lines");
_Result.bitmapData = bmd;
}
private function _TestLineTo(LogMsg:String, StageQuality:String, Data:Vector.<Vector.<Number>>):void {
var startTime_ms:uint;
var numPts:uint = Data[0].length;
var i:uint;
var bmd:BitmapData = new BitmapData(1000, 1000, false, 0xffffff);
var xData:Vector.<Number> = Data[0];
var yData:Vector.<Number> = Data[1];
var lineColor:uint = 0x00ff00;
var shape:Shape = new Shape();
var graphics:Graphics = shape.graphics;
stage.quality = StageQuality;
graphics.lineStyle(0, lineColor, 1);
graphics.moveTo(xData[0], yData[0]);
for(i = 1; i < numPts; ++i) {
graphics.lineTo(xData[i], yData[i]);
}
//Run (and time) the lineTo rendering...
startTime_ms = getTimer();
bmd.draw(shape);
log(LogMsg + (getTimer() - startTime_ms) + " ms -- " + NUM_LINES_TO_DRAW + " lines");
_Result.bitmapData = bmd;
}
private function _TestDrawPath(LogMsg:String, StageQuality:String, Data:Vector.<Vector.<Number>>):void {
var startTime_ms:uint;
var numPts:uint = Data[0].length;
var i:uint;
var bmd:BitmapData = new BitmapData(1000, 1000, false, 0xffffff);
var xData:Vector.<Number> = Data[0];
var yData:Vector.<Number> = Data[1];
var lineColor:uint = 0xff0000;
var shape:Shape = new Shape();
var graphics:Graphics = shape.graphics;
// make the required drawPath vectors
var commands:Vector.<int> = new Vector.<int>();
var dataPts:Vector.<Number> = new Vector.<Number>();
stage.quality = StageQuality;
graphics.lineStyle(0, lineColor, 1);
startTime_ms = getTimer();
//Generate the drawing commands...
commands.push(GraphicsPathCommand.MOVE_TO);
dataPts.push(xData[0]); // moveto x
dataPts.push(yData[0]); // moveto y
for(i = 1; i < numPts; ++i) {
commands.push(GraphicsPathCommand.LINE_TO);
dataPts.push(xData[i]); // lineto x
dataPts.push(yData[i]); // lineto y
}
// Execute drawPath (which just queues up the commands)...
shape.graphics.drawPath(commands, dataPts);
//log("full drawPath pt generation time: " + (getTimer() - beforeTime) + "ms");
//Now actually run (and time) the drawPath rendering...
startTime_ms = getTimer();
bmd.draw(shape);
log(LogMsg + (getTimer() - startTime_ms) + " ms -- " + NUM_LINES_TO_DRAW + " lines");
_Result.bitmapData = bmd;
}
private function log(msg:*):void {
_Logger.appendText(msg + "\n");
}
// EFLA == "Extremely Fast Line Algorithm"
// - algorithm author is Po-Han Lin (original version: http://www.edepot.com/algorithm.html)
// - AS3 port by Simo Santavirta (http://www.simppa.fi/blog/?p=521)
// - further tweaks by Jackson Dunstan (http://jacksondunstan.com/articles/506)
private function EFLA(x:int, y:int, x2:int, y2:int, color:uint, bmd:BitmapData): void {
var shortLen:int = y2-y;
var longLen:int = x2-x;
if ((shortLen ^ (shortLen >> 31)) - (shortLen >> 31) > (longLen ^ (longLen >> 31)) - (longLen >> 31)) {
shortLen ^= longLen;
longLen ^= shortLen;
shortLen ^= longLen;
var yLonger:Boolean = true;
}
else {
yLonger = false;
}
var inc:int = longLen < 0 ? -1 : 1;
var multDiff:Number = longLen == 0 ? shortLen : shortLen / longLen;
if (yLonger) {
for (var i:int = 0; i != longLen; i += inc) {
bmd.setPixel(x + i*multDiff, y+i, color);
}
}
else {
for (i = 0; i != longLen; i += inc) {
bmd.setPixel(x+i, y+i*multDiff, color);
}
}
}
}
}