Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 605 lines (566 sloc) 17.9 KB
package net.flashpunk.utils
{
import flash.display.*;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import net.flashpunk.Entity;
import net.flashpunk.FP;
import net.flashpunk.Graphic;
import net.flashpunk.graphics.Text;
/**
* Static class with access to miscellaneous drawing functions.
* These functions are not meant to replace Graphic components
* for Entities, but rather to help with testing and debugging.
*/
public class Draw
{
/**
* The blending mode used by Draw functions. This will not
* apply to Draw.line() or Draw.circle(), but will apply
* to Draw.linePlus() and Draw.circlePlus().
*/
public static var blend:String;
/**
* Sets the drawing target for Draw functions.
* @param target The buffer to draw to.
* @param camera The camera offset (use null for none).
* @param blend The blend mode to use.
*/
public static function setTarget(target:BitmapData, camera:Point = null, blend:String = null):void
{
_target = target;
_camera = camera ? camera : FP.zero;
Draw.blend = blend;
}
/**
* Resets the drawing target to the default. The same as calling Draw.setTarget(FP.buffer, FP.camera).
*/
public static function resetTarget():void
{
_target = FP.buffer;
_camera = FP.camera;
Draw.blend = null;
}
/**
* Draws a pixelated, non-antialiased line.
* @param x1 Starting x position.
* @param y1 Starting y position.
* @param x2 Ending x position.
* @param y2 Ending y position.
* @param color Color of the line.
* @param overwriteAlpha Alpha value written to these pixels: does NOT do blending. If you want to draw a semi-transparent line over some other content, you will have to either: A) use Draw.linePlus() or B) if non-antialiasing is important, render with Draw.line() to an intermediate buffer with transparency and then render that intermediate buffer.
*/
public static function line(x1:int, y1:int, x2:int, y2:int, color:uint = 0xFFFFFF, overwriteAlpha:Number = 1.0):void
{
color = (uint(overwriteAlpha * 0xFF) << 24) | (color & 0xFFFFFF);
// get the drawing positions
x1 -= _camera.x;
y1 -= _camera.y;
x2 -= _camera.x;
y2 -= _camera.y;
// get the drawing difference
var screen:BitmapData = _target,
X:Number = Math.abs(x2 - x1),
Y:Number = Math.abs(y2 - y1),
xx:int,
yy:int;
// draw a single pixel
if (X == 0)
{
if (Y == 0)
{
screen.setPixel32(x1, y1, color);
return;
}
// draw a straight vertical line
yy = y2 > y1 ? 1 : -1;
while (y1 != y2)
{
screen.setPixel32(x1, y1, color);
y1 += yy;
}
screen.setPixel32(x2, y2, color);
return;
}
if (Y == 0)
{
// draw a straight horizontal line
xx = x2 > x1 ? 1 : -1;
while (x1 != x2)
{
screen.setPixel32(x1, y1, color);
x1 += xx;
}
screen.setPixel32(x2, y2, color);
return;
}
xx = x2 > x1 ? 1 : -1;
yy = y2 > y1 ? 1 : -1;
var c:Number = 0,
slope:Number;
if (X > Y)
{
slope = Y / X;
c = .5;
while (x1 != x2)
{
screen.setPixel32(x1, y1, color);
x1 += xx;
c += slope;
if (c >= 1)
{
y1 += yy;
c -= 1;
}
}
screen.setPixel32(x2, y2, color);
}
else
{
slope = X / Y;
c = .5;
while (y1 != y2)
{
screen.setPixel32(x1, y1, color);
y1 += yy;
c += slope;
if (c >= 1)
{
x1 += xx;
c -= 1;
}
}
screen.setPixel32(x2, y2, color);
}
}
/**
* Draws a smooth, antialiased line with optional alpha and thickness.
* @param x1 Starting x position.
* @param y1 Starting y position.
* @param x2 Ending x position.
* @param y2 Ending y position.
* @param color Color of the line.
* @param alpha Alpha of the line.
* @param thick The thickness of the line.
*/
public static function linePlus(x1:Number, y1:Number, x2:Number, y2:Number, color:uint = 0xFF000000, alpha:Number = 1, thick:Number = 1):void
{
graphicsClear();
_graphics.lineStyle(thick, color, alpha, false, LineScaleMode.NONE);
_graphics.moveTo(x1 - _camera.x, y1 - _camera.y);
_graphics.lineTo(x2 - _camera.x, y2 - _camera.y);
targetDraw();
}
/**
* Draws a filled rectangle.
* @param x X position of the rectangle.
* @param y Y position of the rectangle.
* @param width Width of the rectangle.
* @param height Height of the rectangle.
* @param color Color of the rectangle.
* @param alpha Alpha of the rectangle.
* @param overwrite If the color/alpha provided should replace the existing data rather than blend.
*/
public static function rect(x:Number, y:Number, width:Number, height:Number, color:uint = 0xFFFFFF, alpha:Number = 1, overwrite:Boolean = false):void
{
if (! overwrite && (alpha < 1 || blend)) {
graphicsClear();
_graphics.beginFill(color & 0xFFFFFF, alpha);
_graphics.drawRect(x - _camera.x, y - _camera.y, width, height);
targetDraw();
return;
}
color = (uint(alpha * 0xFF) << 24) | (color & 0xFFFFFF);
_rect.x = x - _camera.x;
_rect.y = y - _camera.y;
_rect.width = width;
_rect.height = height;
_target.fillRect(_rect, color);
}
/**
* Draws a rectangle.
* @param x X position of the rectangle.
* @param y Y position of the rectangle.
* @param width Width of the rectangle.
* @param height Height of the rectangle.
* @param color Color of the rectangle.
* @param alpha Alpha of the rectangle.
* @param fill If the rectangle should be filled with the color (true) or just an outline (false).
* @param thick How thick the outline should be (only applicable when fill = false).
* @param radius Round rectangle corners by this amount.
*/
public static function rectPlus(x:Number, y:Number, width:Number, height:Number, color:uint = 0xFFFFFF, alpha:Number = 1, fill:Boolean = true, thick:Number = 1, radius:Number = 0):void
{
if (color > 0xFFFFFF) color = 0xFFFFFF & color;
graphicsClear();
if (fill) {
_graphics.beginFill(color, alpha);
} else {
_graphics.lineStyle(thick, color, alpha, false, LineScaleMode.NORMAL, null, JointStyle.MITER);
}
if (radius <= 0) {
_graphics.drawRect(x - _camera.x, y - _camera.y, width, height);
} else {
_graphics.drawRoundRect(x - _camera.x, y - _camera.y, width, height, radius);
}
targetDraw();
}
/**
* Draws a non-filled, pixelated circle.
* @param x Center x position.
* @param y Center y position.
* @param radius Radius of the circle.
* @param color Color of the circle.
*/
public static function circle(x:int, y:int, radius:int, color:uint = 0xFFFFFF):void
{
if (color < 0xFF000000) color = 0xFF000000 | color;
x -= _camera.x;
y -= _camera.y;
var f:int = 1 - radius,
fx:int = 1,
fy:int = -2 * radius,
xx:int = 0,
yy:int = radius;
_target.setPixel32(x, y + radius, color);
_target.setPixel32(x, y - radius, color);
_target.setPixel32(x + radius, y, color);
_target.setPixel32(x - radius, y, color);
while (xx < yy)
{
if (f >= 0)
{
yy --;
fy += 2;
f += fy;
}
xx ++;
fx += 2;
f += fx;
_target.setPixel32(x + xx, y + yy, color);
_target.setPixel32(x - xx, y + yy, color);
_target.setPixel32(x + xx, y - yy, color);
_target.setPixel32(x - xx, y - yy, color);
_target.setPixel32(x + yy, y + xx, color);
_target.setPixel32(x - yy, y + xx, color);
_target.setPixel32(x + yy, y - xx, color);
_target.setPixel32(x - yy, y - xx, color);
}
}
/**
* Draws a circle to the screen.
* @param x X position of the circle's center.
* @param y Y position of the circle's center.
* @param radius Radius of the circle.
* @param color Color of the circle.
* @param alpha Alpha of the circle.
* @param fill If the circle should be filled with the color (true) or just an outline (false).
* @param thick How thick the outline should be (only applicable when fill = false).
*/
public static function circlePlus(x:Number, y:Number, radius:Number, color:uint = 0xFFFFFF, alpha:Number = 1, fill:Boolean = true, thick:Number = 1):void
{
graphicsClear();
if (fill)
{
_graphics.beginFill(color & 0xFFFFFF, alpha);
_graphics.drawCircle(x - _camera.x, y - _camera.y, radius);
_graphics.endFill();
}
else
{
_graphics.lineStyle(thick, color & 0xFFFFFF, alpha);
_graphics.drawCircle(x - _camera.x, y - _camera.y, radius);
}
targetDraw();
}
/**
* Draws an ellipse to the screen.
* @param x X position of the ellipse's center.
* @param y Y position of the ellipse's center.
* @param width Width of the ellipse.
* @param height Height of the ellipse.
* @param color Color of the ellipse.
* @param alpha Alpha of the ellipse.
* @param fill If the ellipse should be filled with the color (true) or just an outline (false).
* @param thick How thick the outline should be (only applicable when fill = false).
* @param angle What angle (in degrees) the ellipse should be rotated.
*/
public static function ellipse(x:Number, y:Number, width:Number, height:Number, color:uint = 0xFFFFFF, alpha:Number = 1, fill:Boolean = true, thick:Number = 1, angle:Number = 0):void
{
_graphics.clear();
if (fill)
{
_graphics.beginFill(color & 0xFFFFFF, alpha);
_graphics.drawEllipse(-width / 2, -height / 2, width, height);
_graphics.endFill();
}
else
{
_graphics.lineStyle(thick, color & 0xFFFFFF, alpha);
_graphics.drawEllipse(-width / 2, -height / 2, width, height);
}
var m:Matrix = new Matrix();
m.rotate(angle * FP.RAD);
m.translate(x - _camera.x, y - _camera.y);
_target.draw(FP.sprite, m, null, blend);
}
/**
* Draws the Entity's hitbox.
* @param e The Entity whose hitbox is to be drawn.
* @param outline If just the hitbox's outline should be drawn.
* @param color Color of the hitbox.
* @param alpha Alpha of the hitbox.
*/
public static function hitbox(e:Entity, outline:Boolean = true, color:uint = 0xFFFFFF, alpha:Number = 1):void
{
if (outline)
{
if (color < 0xFF000000) color = 0xFF000000 | color;
var x:int = e.x - e.originX - _camera.x,
y:int = e.y - e.originY - _camera.y;
_rect.x = x;
_rect.y = y;
_rect.width = e.width;
_rect.height = 1;
_target.fillRect(_rect, color);
_rect.y += e.height - 1;
_target.fillRect(_rect, color);
_rect.y = y;
_rect.width = 1;
_rect.height = e.height;
_target.fillRect(_rect, color);
_rect.x += e.width - 1;
_target.fillRect(_rect, color);
return;
}
if (alpha >= 1 && !blend)
{
if (color < 0xFF000000) color = 0xFF000000 | color;
_rect.x = e.x - e.originX - _camera.x;
_rect.y = e.y - e.originY - _camera.y;
_rect.width = e.width;
_rect.height = e.height;
_target.fillRect(_rect, color);
return;
}
if (color > 0xFFFFFF) color = 0xFFFFFF & color;
_graphics.clear();
_graphics.beginFill(color, alpha);
_graphics.drawRect(e.x - e.originX - _camera.x, e.y - e.originY - _camera.y, e.width, e.height);
_target.draw(FP.sprite, null, null, blend);
}
/** rostok
* sets pixel 32
*/
public static function setPixel32(x:int, y:int, color:uint = 0xFFFFFF):void
{
_target.setPixel32(x - _camera.x, y - _camera.y, color);
}
/** rostok
* gets pixel 32
*/
public static function getPixel32(x:int, y:int):uint
{
return _target.getPixel32(x - _camera.x, y - _camera.y);
}
/** rostok
* sets pixel
*/
public static function setPixel(x:int, y:int, color:uint = 0xFFFFFF):void
{
_target.setPixel(x - _camera.x, y - _camera.y, color);
}
/** rostok
* gets pixel
*/
public static function getPixel(x:int, y:int):uint
{
return _target.getPixel(x - _camera.x, y - _camera.y);
}
/** rostok
* draws capsule
*/
public static function drawCapsule(p1x:Number,p1y:Number,p2x:Number,p2y:Number,r:Number,c:int=0xFFFFFF):void
{
startDelayedTargetDraw();
Draw.circle(p1x, p1y, r, c);
Draw.circle(p2x, p2y, r, c);
var dx:Number = p1x - p2x;
var dy:Number = p1y - p2y;
var l:Number = Math.sqrt(dx*dx+dy*dy);
if (l > 0) {
dx *= r/l;
dy *= r/l;
Draw.line(p1x - dy, p1y + dx, p2x - dy, p2y + dx, c);
Draw.line(p1x + dy, p1y - dx, p2x + dy, p2y - dx, c);
}
endDelayedTargetDraw();
}
/** rostok
* draw arrow from p1 to p2
*/
public static function arrow(p1x:Number, p1y:Number, p2x:Number, p2y:Number, c:int = 0xFFFFFF, h:Number = 8, w:Number = 6):void
{
startDelayedTargetDraw();
Draw.line(p1x, p1y, p2x, p2y, c);
var p:Point = new Point(p1x - p2x, p1y - p2y);
p.normalize(h);
var q:Point = new Point(-p.y, p.x);
q.normalize(w/2);
Draw.line(p2x, p2y, p2x + p.x + q.x, p2y + p.y + q.y, c);
Draw.line(p2x, p2y, p2x + p.x - q.x, p2y + p.y - q.y, c);
endDelayedTargetDraw();
}
/**
* Draws a quadratic curve.
* @param x1 X start.
* @param y1 Y start.
* @param x2 X control point, used to determine the curve.
* @param y2 Y control point, used to determine the curve.
* @param x3 X finish.
* @param y3 Y finish.
* @param color Color of the curve
* @param alpha Alpha transparency.
*/
public static function curve(x1:Number, y1:Number, x2:Number, y2:Number, x3:Number, y3:Number, color:uint = 0, alpha:Number = 1, thick:Number = 1):void
{
graphicsClear();
_graphics.lineStyle(thick, color & 0xFFFFFF, alpha);
_graphics.moveTo(x1 - _camera.x, y1 - _camera.y);
_graphics.curveTo(x2 - _camera.x, y2 - _camera.y, x3 - _camera.x, y3 - _camera.y);
targetDraw();
}
/**
* Draws a graphic object.
* @param g The Graphic to draw.
* @param x X position.
* @param y Y position.
*/
public static function graphic(g:Graphic, x:int = 0, y:int = 0):void
{
if (g.visible)
{
if (g.relative)
{
FP.point.x = x;
FP.point.y = y;
}
else FP.point.x = FP.point.y = 0;
FP.point2.x = _camera.x;
FP.point2.y = _camera.y;
g.render(_target, FP.point, FP.point2);
}
}
/**
* Draws an Entity object.
* @param e The Entity to draw.
* @param x X position.
* @param y Y position.
* @param addEntityPosition Adds the Entity's x and y position to the target position.
*/
public static function entity(e:Entity, x:int = 0, y:int = 0, addEntityPosition:Boolean = false):void
{
if (e.visible && e.graphic)
{
if (addEntityPosition) graphic(e.graphic, x + e.x, y + e.y);
else graphic(e.graphic, x, y);
}
}
/**
* Draws text.
* @param text The text to render.
* @param x X position.
* @param y Y position.
* @param options Options (see Text constructor).
*/
public static function text (text:String, x:Number = 0, y:Number = 0, options:Object = null):void
{
var textGfx:Text = new Text(text, x, y, options);
textGfx.render(_target, FP.zero, _camera);
}
// rostok
/**
* simple dashed rectangle
* @param x X position of the rectangle.
* @param y Y position of the rectangle.
* @param width Width of the rectangle.
* @param height Height of the rectangle.
* @param color Color of the rectangle.
* @param step dashed line step
*/
public static function rectDashed(x:Number, y:Number, width:Number, height:Number, color:uint = 0xFFFFFF, step:Number = 16, alpha:Number = 1):void
{
startDelayedTargetDraw();
for (var k:Number = x - height; k < x + width; k += step)
{
var sx:Number = k;
var sy:Number = y;
var ex:Number = k+height;
var ey:Number = y+height;
var b:Number;
b = x - sx;
if (b > 0) { sx += b; sy += b; }
b = ex - x - width;
if (b > 0) { ex -= b; ey -= b; }
Draw.linePlus(sx, sy, ex, ey, color, alpha);
}
Draw.rectPlus(x, y, width, height, color, alpha, false);
endDelayedTargetDraw();
}
public static function rectHorifilled(x:Number, y:Number, width:Number, height:Number, color:uint = 0xFFFFFF, step:Number = 16, alpha:Number=1):void
{
startDelayedTargetDraw();
Draw.rectPlus(x, y, width, height, color, alpha, false);
width += x;
for (var k:Number = y + step; k < y + height; k += step)
Draw.linePlus(x, k, width, k, color, alpha);
//Draw.line(x + 1, k, x + width - 1, k, color);
endDelayedTargetDraw();
}
public static function rectVerifilled(x:Number, y:Number, width:Number, height:Number, color:uint = 0xFFFFFF, step:Number = 16, alpha:Number=1 ):void
{
startDelayedTargetDraw();
Draw.rectPlus(x, y, width, height, color, alpha, false);
height += y;
for (var k:Number = x + step; k < x + width; k += step)
Draw.linePlus(k, y, k, height, color, alpha);
//Draw.line(k, y + 1, k, y + height - 1, color);
endDelayedTargetDraw();
}
// rostok
/**
* clears graphics sprite only if no delayedTargetDraw is in place
*/
private static function graphicsClear():void { if (!_delayedTargetDraw) _graphics.clear(); }
/**
* renders graphics sprite to target only if no delayedTargetDraw is in place
*/
private static function targetDraw():void { if (!_delayedTargetDraw) _target.draw(FP.sprite, null, null, blend); }
/**
* holds state of delayedTargetDraw, false by default
*/
private static var _delayedTargetDraw:Boolean = false;
/**
* starts the delayed target draw, clears the _graphics
*/
private static function startDelayedTargetDraw():void
{
_graphics.clear();
_delayedTargetDraw = true;
}
/**
* ends delayedTargetDraw by drawing graphics to target
*/
private static function endDelayedTargetDraw():void
{
_delayedTargetDraw = false;
_target.draw(FP.sprite, null, null, blend);
}
// Drawing information.
/** @private */ private static var _target:BitmapData;
/** @private */ private static var _camera:Point;
/** @private */ private static var _graphics:Graphics = FP.sprite.graphics;
/** @private */ private static var _rect:Rectangle = FP.rect;
}
}
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.