Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a 'Shape' class to the 'display' package #239

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/flambe/display/Graphics.hx
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,7 @@ interface Graphics

/** Draws a colored rectangle at the given region. */
function fillRect (color :Int, x :Float, y :Float, width :Float, height :Float) :Void;

/** Draws a line using the given specifications. */
function drawLine (color :Int, xStart :Float, yStart :Float, xEnd :Float, yEnd :Float, width :Float, roundedCap :Bool) :Void;
}
156 changes: 156 additions & 0 deletions src/flambe/display/Shape.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//
// Flambe - Rapid game development
// https://github.com/aduros/flambe/blob/master/LICENSE.txt

package flambe.display;

import flambe.display.Shape.LineCaps;
import flambe.display.Sprite;
import flambe.math.Point;
import flambe.math.FMath;
import flambe.util.Assert;

enum LineCaps
{
None;
Rounded;
}

/**
* A user defined shape (line, rectangle, polygon) that is assembled by adding
* various primitives together. Can be transformed like any Sprite object.
*/
class Shape extends Sprite
{
public var lineWidth(default, default) :Float;
public var lineCap(default, default) :LineCaps;
public var strokeColor(default, default) :Int;
public var fillColor(default, default) :Int;
public var penCoordinate(default, null) :Point;

private var _segments :Array<Segment>;


public function new()
{
super();

lineWidth = 1.0;
lineCap = None;
strokeColor = 0x000000;
fillColor = 0xFFFFFF;
penCoordinate = new Point();

_segments = new Array<Segment>();
}

public function lineStyle(width :Float, color :Int, cap :LineCaps) : Void
{
lineWidth = width;
strokeColor = color;
lineCap = cap;
}

public function fillStyle(color :Int) : Void
{
fillColor = color;
}

public function moveTo(x :Float, y :Float) : Void
{
penCoordinate.set(x, y);
}

public function lineTo(x :Float, y :Float) : Void
{
var startPoint :Point = new Point(penCoordinate.x, penCoordinate.y);
penCoordinate.set(x, y);

trace(startPoint);

var index = _segments.length;
_segments[index] = new Segment(startPoint, penCoordinate, lineWidth, lineCap, strokeColor);
}

public function curveTo(anchorX :Float, anchorY :Float, x :Float, y :Float) : Void
{
// Determine how much percision
var iPercInterval = 0.1; // 0.1 == 10 vertices

var i :Float = 0.0;
var xa, ya, xb, yb;
while (i < 1.0) {
// Compute anchor/path
xa = FMath.lerp(penCoordinate.x, anchorX, i);
ya = FMath.lerp(penCoordinate.y, anchorY, i);
xb = FMath.lerp(anchorX, x, i );
yb = FMath.lerp(anchorY, y, i );

// Find position along the anchor/path
lineTo(FMath.lerp(xa, xb, i), FMath.lerp(ya, yb, i));

i += iPercInterval;
}
}

public function drawCircle(x :Float, y :Float, radius :Float) : Void
{
var numWedges :Int = Std.int(radius / 2);
if (numWedges < 12) numWedges = 12;

var wedgeAngle :Float = (2.0 * Math.PI) / numWedges;

moveTo(x + radius, y);
var theta :Float = 0.0;
for (i in 0...numWedges)
{
theta = i * wedgeAngle;
lineTo(x + radius * Math.cos(theta), y + radius * Math.sin(theta));
}
}

public function drawEllipse(x :Float, y :Float, width :Float, height :Float, ?rotation :Float) : Void
{
}

public function drawRect(x :Float, y :Float, width :Float, height :Float) : Void
{
moveTo(x, y);
lineTo(x + width, y);
lineTo(x + width, y + height);
lineTo(x, y + height);
lineTo(x, y);
}

public function clear() : Void
{
_segments = new Array<Segment>();
}

override public function draw (g :Graphics)
{
for (seg in _segments) {
g.drawLine(seg.color, seg.startPt.x, seg.startPt.y, seg.endPt.x, seg.endPt.y, seg.width, seg.cap == Rounded);
}
}
}

private class Segment
{
public var startPt(default, null) :Point;
public var endPt(default, null) :Point;
public var width(default, null) :Float;
public var cap(default, null) :LineCaps;
public var color(default, null) :Int;

public function new(startPoint :Point, endPoint :Point, lineWidth :Float, lineCap :LineCaps, clr :Int)
{
startPt = new Point(startPoint.x, startPoint.y);
endPt = new Point(endPoint.x, endPoint.y);

width = lineWidth;
cap = lineCap;

color = clr;
}
}
5 changes: 5 additions & 0 deletions src/flambe/math/FMath.hx
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,9 @@ class FMath
else if (value > 0) 1
else 0;
}

public static function lerp (n1 :Float, n2 :Float, percent :Float) :Float
{
return n1 + ((n2 - n1) * percent);
}
}
5 changes: 5 additions & 0 deletions src/flambe/platform/OverdrawGraphics.hx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ class OverdrawGraphics
drawRegion(x, y, width, height);
}

public function drawLine (color :Int, xStart :Float, yStart :Float, xEnd :Float, yEnd :Float, width :Float, roundedCap :Bool)
{
_impl.drawLine(color, xStart, yStart, xEnd, yEnd, width, roundedCap);
}

public function willRender ()
{
_impl.willRender();
Expand Down
181 changes: 181 additions & 0 deletions src/flambe/platform/flash/Stage3DGraphics.hx
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,57 @@ class Stage3DGraphics
data[++offset] = a;
}

public function drawLine (color :Int, xStart :Float, yStart :Float, xEnd :Float, yEnd :Float, width :Float, roundedCap :Bool) :Void
{
var state = getTopState();
if (state.emptyScissor()) {
return;
}

var pos = transformQuadForLine(xStart, yStart, xEnd, yEnd, width);
var r = (color & 0xff0000) / 0xff0000;
var g = (color & 0x00ff00) / 0x00ff00;
var b = (color & 0x0000ff) / 0x0000ff;
var a = state.alpha;

var offset = _batcher.prepareFillRect(_renderTarget, state.blendMode, state.getScissor());
var data = _batcher.data;

data[ offset] = pos[0];
data[++offset] = pos[1];
data[++offset] = r;
data[++offset] = g;
data[++offset] = b;
data[++offset] = a;

data[++offset] = pos[3];
data[++offset] = pos[4];
data[++offset] = r;
data[++offset] = g;
data[++offset] = b;
data[++offset] = a;

data[++offset] = pos[6];
data[++offset] = pos[7];
data[++offset] = r;
data[++offset] = g;
data[++offset] = b;
data[++offset] = a;

data[++offset] = pos[9];
data[++offset] = pos[10];
data[++offset] = r;
data[++offset] = g;
data[++offset] = b;
data[++offset] = a;

if (roundedCap)
{
drawLineCap(true, xStart, yStart, width, r, g, b, a);
drawLineCap(false, xEnd, yEnd, width, r, g, b, a);
}
}

public function multiplyAlpha (factor :Float)
{
getTopState().alpha *= factor;
Expand Down Expand Up @@ -339,6 +390,134 @@ class Stage3DGraphics
return pos;
}

private function transformQuadForLine(xStart :Float, yStart :Float, xEnd :Float, yEnd :Float, width :Float) :Vector<Float>
{
var halfWidth = width * 0.5;
var pos = _scratchQuadVector;

pos[2] = 0;
pos[5] = 0;
pos[8] = 0;
pos[11] = 0;

// Case for vertical line
if(xStart == xEnd) {
pos[0] = xStart - halfWidth;
pos[1] = yStart;
pos[3] = xStart + halfWidth;
pos[4] = yStart;

pos[6] = xEnd + halfWidth;
pos[7] = yEnd;
pos[9] = xEnd - halfWidth;
pos[10] = yEnd;

_startTheta = (yStart > yEnd) ? Math.PI : 0.0;
}
// Case for horizontal line
else if(yStart == yEnd) {
pos[0] = xStart;
pos[1] = yStart - halfWidth;
pos[3] = xStart;
pos[4] = yStart + halfWidth;

pos[6] = xEnd;
pos[7] = yEnd + halfWidth;
pos[9] = xEnd;
pos[10] = yEnd - halfWidth;

_startTheta = (xStart > xEnd) ? 0.5*Math.PI : -0.5*Math.PI;
}
// Final case for any line with slope
else {
var slopePerp = (xStart - xEnd) / (yEnd - yStart);
var xOffset = Math.sqrt((halfWidth * halfWidth) / (1.0 + (slopePerp * slopePerp)));

pos[0] = xStart - xOffset;
pos[1] = slopePerp * (pos[0] - xStart) + yStart;
pos[3] = xStart + xOffset;
pos[4] = slopePerp * (pos[3] - xStart) + yStart;

pos[6] = xEnd + xOffset;
pos[7] = slopePerp * (pos[6] - xEnd) + yEnd;
pos[9] = xEnd - xOffset;
pos[10] = slopePerp * (pos[9] - xEnd) + yEnd;

_startTheta = Math.atan(slopePerp);
if(yStart > yEnd) {
_startTheta += Math.PI;
}
}

getTopState().matrix.transformVectors(pos, pos);
return pos;
}

private function drawLineCap(startCap :Bool, xCtr :Float, yCtr :Float, width :Float, red :Float, green :Float, blue :Float, alpha :Float)
{
var halfWidth = width * 0.5;
var numWedgeForCap :Int = Std.int(width / 4);
if (numWedgeForCap < 6) numWedgeForCap = 6;

var wedgeAngle = (2.0 * Math.PI) / (numWedgeForCap * 2);
wedgeAngle *= (startCap ? -1.0 : 1.0);

for (i in 0...numWedgeForCap)
{
var pos = _scratchQuadVector;
pos[0] = xCtr;
pos[1] = yCtr;

var theta = _startTheta;
theta += i * wedgeAngle;

pos[3] = xCtr + halfWidth*Math.cos(theta);
pos[4] = yCtr + halfWidth*Math.sin(theta);

theta += wedgeAngle;

pos[6] = xCtr + halfWidth*Math.cos(theta);
pos[7] = yCtr + halfWidth*Math.sin(theta);

pos[9] = xCtr;
pos[10] = yCtr;

var state = getTopState();
state.matrix.transformVectors(pos, pos);

var offset = _batcher.prepareFillRect(_renderTarget, state.blendMode, state.getScissor());
var data = _batcher.data;

data[ offset] = pos[0];
data[++offset] = pos[1];
data[++offset] = red;
data[++offset] = green;
data[++offset] = blue;
data[++offset] = alpha;

data[++offset] = pos[3];
data[++offset] = pos[4];
data[++offset] = red;
data[++offset] = green;
data[++offset] = blue;
data[++offset] = alpha;

data[++offset] = pos[6];
data[++offset] = pos[7];
data[++offset] = red;
data[++offset] = green;
data[++offset] = blue;
data[++offset] = alpha;

data[++offset] = pos[9];
data[++offset] = pos[10];
data[++offset] = red;
data[++offset] = green;
data[++offset] = blue;
data[++offset] = alpha;
}
}

private static var _scratchMatrix3D = new Matrix3D();
private static var _scratchClipVector = new Vector<Float>(2*3, true);
private static var _scratchQuadVector = new Vector<Float>(4*3, true);
Expand All @@ -348,6 +527,8 @@ class Stage3DGraphics
return v;
})();

private var _startTheta :Float; // Used for drawing rounded line caps

private var _batcher :Stage3DBatcher;
private var _renderTarget :Stage3DTextureRoot;

Expand Down
Loading