├── libs
├── graphics.fla
├── graphics.swc
└── starling.swc
├── src
├── assets
│ ├── roi.png
│ ├── bricks_pow2.jpg
│ └── line_pattern.png
├── demos
│ ├── DemoHoles.as
│ ├── DemoDashedLine.as
│ ├── DemoCloneInstances.as
│ ├── DemoSVG.as
│ ├── DemoLineStyles.as
│ ├── DemoPixiAdvanced.as
│ ├── DemoPixiSimple.as
│ ├── DemoGraphicsData.as
│ ├── DemoAnimatedArc.as
│ ├── DemoPixiDynamic.as
│ ├── DemoTextureFill.as
│ └── DemoPieChart.as
├── StarlingRoot.as
└── Boot.as
├── draw_lib
├── bin-release
│ └── draw_lib.swc
├── src
│ └── com
│ │ └── roipeker
│ │ └── starling
│ │ └── draw
│ │ ├── utils
│ │ ├── earcut
│ │ │ └── Node.as
│ │ ├── GraphicCurves.as
│ │ ├── lines
│ │ │ ├── Pnt.as
│ │ │ ├── LineStrokePixi.as
│ │ │ └── ComplexLineStroke.as
│ │ ├── QuadraticUtils.as
│ │ ├── BezierUtils.as
│ │ ├── ArcUtils.as
│ │ ├── svg
│ │ │ ├── SVGColor.as
│ │ │ └── DPathParser.as
│ │ └── GraphUtils.as
│ │ ├── math
│ │ └── shapes
│ │ │ ├── AbsShape.as
│ │ │ ├── StarPoly.as
│ │ │ ├── Circle.as
│ │ │ ├── Ellipse.as
│ │ │ ├── ShapeType.as
│ │ │ ├── MeshShape.as
│ │ │ ├── Poly.as
│ │ │ ├── RoundRect.as
│ │ │ └── Rect.as
│ │ ├── builders
│ │ ├── AbsShapeBuilder.as
│ │ ├── RectBuilder.as
│ │ ├── MeshBuilder.as
│ │ ├── PolyBuilder.as
│ │ ├── CircleBuilder.as
│ │ ├── GraphicsDataBuild.as
│ │ └── RoundRectBuilder.as
│ │ ├── AbsMesh.as
│ │ ├── styles
│ │ ├── FillStyle.as
│ │ ├── LineStyle.as
│ │ └── GradientFillStyle.as
│ │ ├── GraphData.as
│ │ └── GraphGeom.as
└── draw_lib.iml
├── .gitignore
├── .idea
├── encodings.xml
├── vcs.xml
├── flexCompiler.xml
├── modules.xml
├── misc.xml
└── runConfigurations
│ └── demos.xml
├── LICENSE
├── draw demos.iml
├── README.md
└── app.xml
/libs/graphics.fla:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roipeker/draw-lib/HEAD/libs/graphics.fla
--------------------------------------------------------------------------------
/libs/graphics.swc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roipeker/draw-lib/HEAD/libs/graphics.swc
--------------------------------------------------------------------------------
/libs/starling.swc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roipeker/draw-lib/HEAD/libs/starling.swc
--------------------------------------------------------------------------------
/src/assets/roi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roipeker/draw-lib/HEAD/src/assets/roi.png
--------------------------------------------------------------------------------
/src/assets/bricks_pow2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roipeker/draw-lib/HEAD/src/assets/bricks_pow2.jpg
--------------------------------------------------------------------------------
/src/assets/line_pattern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roipeker/draw-lib/HEAD/src/assets/line_pattern.png
--------------------------------------------------------------------------------
/draw_lib/bin-release/draw_lib.swc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roipeker/draw-lib/HEAD/draw_lib/bin-release/draw_lib.swc
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Actionscript template
2 | bin/
3 | dev-assets/
4 | .idea/workspace.xml
5 | .idea/tasks.xml
6 | # Other files and folders
7 | .settings/
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/flexCompiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/earcut/Node.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 03/01/2019.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.utils.earcut {
8 | public class Node {
9 |
10 | public var i:int;
11 | public var x:Number;
12 | public var y:Number;
13 | public var prev:Node = null;
14 | public var next:Node = null;
15 | public var z:uint = 0;
16 | public var prevZ:Node;
17 | public var nextZ:Node;
18 | public var steiner:Boolean = false;
19 |
20 | public function Node($i:int, $x:Number, $y:Number) {
21 | i = $i;
22 | x = $x;
23 | y = $y;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/math/shapes/AbsShape.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.math.shapes {
8 | import starling.utils.StringUtil;
9 |
10 | public class AbsShape {
11 |
12 | public var holes:Array;
13 | protected var _type:ShapeType;
14 |
15 | public function AbsShape(type:ShapeType) {
16 | _type = type;
17 | holes = [];
18 | }
19 |
20 | public function get type():ShapeType {
21 | return _type;
22 | }
23 |
24 | // todo: add return pool...
25 |
26 | public function reset():void {
27 | holes.length = 0;
28 | }
29 |
30 | public function toString():String {
31 | return StringUtil.format("[AbsShape type={0}]", _type);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Roi Peker
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/demos/DemoHoles.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import starling.display.Sprite;
11 | import starling.events.Event;
12 |
13 | public class DemoHoles extends Sprite {
14 |
15 | public function DemoHoles() {
16 | addEventListener(Event.ADDED_TO_STAGE, init);
17 | }
18 |
19 | private function init(event:Event):void {
20 | stage.color = 0x0;
21 |
22 | var g:Draw = new Draw();
23 | addChild(g);
24 | g.x = 200;
25 | g.y = 200;
26 |
27 |
28 | g.beginFill(0xff0000, .6);
29 | g.drawCircle(50, 50, 70);
30 | g.endFill();
31 |
32 | g.beginFill(0x00ff00, .7);
33 | g.drawCircle(20, 20, 90);
34 | g.endFill();
35 |
36 | g.beginHole();
37 | g.drawCircle(40, 40, 12);
38 | g.drawCircle(-10, -10, 20);
39 | g.drawRect(-30, -40, 10, 10);
40 | g.endHole();
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/StarlingRoot.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package {
8 | import demos.*;
9 |
10 | import starling.core.Starling;
11 | import starling.display.Sprite;
12 | import starling.events.Event;
13 |
14 | public class StarlingRoot extends Sprite {
15 |
16 | public function StarlingRoot() {
17 | addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
18 | Starling.current.showStats = true;
19 | }
20 |
21 | private function onAddedToStage(event:Event):void {
22 |
23 | // just uncomment 1 line at a time to test.
24 |
25 | // addChild(new DemoPixiSimple());
26 | // addChild(new DemoPixiAdvanced());
27 | // addChild(new DemoPixiDynamic());
28 | // addChild(new DemoAnimatedArc());
29 | // addChild(new DemoDashedLine());
30 | // addChild(new DemoCloneInstances());
31 | // addChild(new DemoHoles());
32 | // addChild(new DemoSVG());
33 | // addChild(new DemoPieChart());
34 | // addChild(new DemoGraphicsData());
35 | // addChild(new DemoLineStyles());
36 | addChild(new DemoTextureFill());
37 | }
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/demos/DemoDashedLine.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import starling.core.Starling;
11 | import starling.display.Sprite;
12 | import starling.events.Event;
13 |
14 | public class DemoDashedLine extends Sprite {
15 |
16 | public function DemoDashedLine() {
17 | addEventListener(Event.ADDED_TO_STAGE, init);
18 | }
19 |
20 | private function init(event:Event):void {
21 | const d:Draw = new Draw();
22 | d.x = 100;
23 | d.y = 100;
24 | addChild(d);
25 |
26 | const tweenObj:Object = {linePercent: 0};
27 |
28 | const polygon:Array = [
29 | 10, 10,
30 | 50, 15,
31 | 60, 60,
32 | 20, 40
33 | ];
34 |
35 | Starling.juggler.tween(tweenObj, 1, {linePercent: 1, repeatCount: 0, onUpdate: drawit});
36 |
37 | function drawit():void {
38 | d.clear();
39 | d.lineStyle(2, 0xff0000 );
40 | d.beginFill(0x00ff00, .5);
41 | d.dashedPolygon(polygon, 5, 3, tweenObj.linePercent);
42 | d.drawCircle(100,100,40);
43 | }
44 |
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/math/shapes/StarPoly.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.math.shapes {
8 | import com.roipeker.starling.draw.utils.GraphUtils;
9 |
10 | import starling.utils.StringUtil;
11 |
12 | public class StarPoly extends Poly {
13 |
14 | // TODO: add pool.
15 |
16 | public function StarPoly(x:Number, y:Number, points:int, radius:Number, innerRadius:Number = 0, rotation:Number = 0) {
17 | innerRadius = innerRadius || radius / 2;
18 | var startAngle:Number = (-1 * Math.PI / 2) + rotation;
19 | var len:int = points * 2;
20 | var delta:Number = GraphUtils.PI2 / len;
21 | var polygon:Array = [];
22 | for (var i:int = 0; i < len; i++) {
23 | const r:Number = i % 2 ? innerRadius : radius;
24 | const a:Number = (i * delta) + startAngle;
25 | polygon.push(
26 | x + (r * Math.cos(a)),
27 | y + (r * Math.sin(a))
28 | );
29 | }
30 | super(polygon);
31 | }
32 |
33 | override public function toString():String {
34 | return StringUtil.format("[StarPoly #points={0}]", points.length);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/demos/DemoCloneInstances.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import starling.display.Sprite;
11 | import starling.events.Event;
12 |
13 | public class DemoCloneInstances extends Sprite {
14 |
15 | public function DemoCloneInstances() {
16 | addEventListener(Event.ADDED_TO_STAGE, init );
17 | }
18 |
19 | private function init(event:Event):void {
20 | const graph1:Draw = new Draw();
21 | addChild(graph1);
22 |
23 | graph1.beginFill(0x00ff00, .2);
24 | graph1.drawCircle(20, 20, 20);
25 | graph1.drawRoundRect(40,40,90, 60, 12);
26 | graph1.endFill();
27 |
28 | // geometry is SHARED with graph1
29 | /*var graph2:Draw = graph1.clone();
30 | addChild(graph2);
31 | graph2.x = 0;
32 | graph2.y = 200;
33 | // this will affect graph1 geometry as well.
34 | graph2.lineStyle(10, 0xff0000, .5);
35 | graph2.drawRect(100,100,50,50);*/
36 |
37 | var graph2:Draw = new Draw();
38 | addChild(graph2);
39 | graph2.x = 0;
40 | graph2.y = 200;
41 | graph2.copyFrom(graph1);
42 |
43 |
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/math/shapes/Circle.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.math.shapes {
8 | import starling.utils.StringUtil;
9 |
10 | public class Circle extends AbsShape {
11 |
12 | // TODO: add pool.
13 |
14 | public var x:Number;
15 | public var y:Number;
16 | public var radius:Number;
17 |
18 | public function Circle(x:Number = 0, y:Number = 0, radius:Number = 0) {
19 | this.x = x;
20 | this.y = y;
21 | this.radius = radius;
22 | super(ShapeType.CIRC);
23 | }
24 |
25 | public function clone():Circle {
26 | return new Circle(x, y, radius);
27 | }
28 |
29 | public function contains(x:Number, y:Number):Boolean {
30 | if (radius <= 0) return false;
31 | const r2:Number = radius * radius;
32 | const dx:Number = x - this.x;
33 | const dy:Number = y - this.y;
34 | return dx * dx + dy * dy <= r2;
35 | }
36 |
37 | public function getBounds():Rect {
38 | return new Rect(x - radius, y - radius, radius * 2, radius * 2);
39 | }
40 |
41 | override public function toString():String {
42 | return StringUtil.format("[Circle x={0}, y={1}, radius={2}]", x, y, radius);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/builders/AbsShapeBuilder.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.builders {
8 | import com.roipeker.starling.draw.GraphData;
9 | import com.roipeker.starling.draw.GraphGeom;
10 | import com.roipeker.starling.draw.math.shapes.ShapeType;
11 |
12 | import flash.utils.Dictionary;
13 |
14 | public class AbsShapeBuilder {
15 |
16 |
17 | private static var _map:Dictionary;
18 |
19 | private static function init():void {
20 | if (_map) return;
21 | _map = new Dictionary(false);
22 | _map[ShapeType.RECT] = new RectBuilder();
23 | _map[ShapeType.RRECT] = new RoundRectBuilder();
24 | _map[ShapeType.ELIP] = _map[ShapeType.CIRC] = new CircleBuilder();
25 | _map[ShapeType.POLY] = new PolyBuilder();
26 | _map[ShapeType.TRI] = new MeshBuilder();
27 | }
28 |
29 | public static function get(type:ShapeType):AbsShapeBuilder {
30 | if (!_map) init();
31 | return _map[type];
32 | }
33 |
34 | /**
35 | * Abstract class.
36 | */
37 | public function AbsShapeBuilder() {
38 | }
39 |
40 | public function build(shapeData:GraphData):void {
41 | }
42 |
43 | public function triangulate(shapeData:GraphData, geometry:GraphGeom):void {
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/math/shapes/Ellipse.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.math.shapes {
8 | import starling.utils.StringUtil;
9 |
10 | public class Ellipse extends AbsShape {
11 |
12 | // TODO: add pool.
13 |
14 | public var x:Number;
15 | public var y:Number;
16 | public var halfW:Number;
17 | public var halfH:Number;
18 |
19 | public function Ellipse(x:Number = 0, y:Number = 0, halfW:Number = 0, halfH:Number = 0) {
20 | this.x = x;
21 | this.y = y;
22 | this.halfW = halfW;
23 | this.halfH = halfH;
24 | super(ShapeType.ELIP);
25 | }
26 |
27 | public function clone():Ellipse {
28 | return new Ellipse(x, y, halfW, halfH);
29 | }
30 |
31 | public function contains(x:Number, y:Number):Boolean {
32 | if (halfW <= 0 || halfH <= 0) return false;
33 | var normx:Number = (x - this.x) / halfW;
34 | var normy:Number = (x - this.y) / halfH;
35 | return normx * normx + normy * normy <= 1;
36 | }
37 |
38 | public function getBounds():Rect {
39 | return new Rect(x - halfW, y - halfH, halfW, halfH);
40 | }
41 |
42 | override public function toString():String {
43 | return StringUtil.format("[Ellipse x={0}, y={1}, half_width={2}, half_height={3}]", x, y, halfW, halfH);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/builders/RectBuilder.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.builders {
8 | import com.roipeker.starling.draw.GraphData;
9 | import com.roipeker.starling.draw.GraphGeom;
10 | import com.roipeker.starling.draw.math.shapes.Rect;
11 |
12 | public class RectBuilder extends AbsShapeBuilder {
13 |
14 | public function RectBuilder() {
15 | super();
16 | }
17 |
18 | override public function build(shapeData:GraphData):void {
19 | const rect:Rect = shapeData.shape as Rect;
20 | shapeData.points.length = 0;
21 | shapeData.points.push(
22 | rect.x, rect.y,
23 | rect.x + rect.w, rect.y,
24 | rect.x + rect.w, rect.y + rect.h,
25 | rect.x, rect.y + rect.h,
26 | rect.x, rect.y
27 | );
28 | }
29 |
30 | override public function triangulate(shapeData:GraphData, geometry:GraphGeom):void {
31 | const points:Array = shapeData.points;
32 | const vertPos:int = geometry.points.length >> 1;
33 | geometry.points.push(
34 | points[0], points[1],
35 | points[2], points[3],
36 | points[6], points[7],
37 | points[4], points[5]
38 | );
39 | geometry.indices.push(
40 | vertPos, vertPos + 1, vertPos + 2,
41 | vertPos + 1, vertPos + 2, vertPos + 3
42 | );
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/draw_lib/draw_lib.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/AbsMesh.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 31/12/2018.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw {
8 | import starling.display.Mesh;
9 | import starling.geom.Polygon;
10 | import starling.rendering.IndexData;
11 | import starling.rendering.VertexData;
12 | import starling.styles.MeshStyle;
13 |
14 | public class AbsMesh extends Mesh {
15 |
16 | protected var _poly:Polygon;
17 | protected var _color:int = 0xffffff;
18 |
19 | public function AbsMesh(vdata:VertexData = null, idata:IndexData = null, style:MeshStyle = null) {
20 | if (!vdata) vdata = new VertexData();
21 | if (!idata) idata = new IndexData();
22 | _poly = new Polygon();
23 | super(vdata, idata, style);
24 | }
25 |
26 | public function processVerticesList(vertices:Array, color:int = -1):void {
27 |
28 | var vdata:VertexData = vertexData;
29 | var idata:IndexData = indexData;
30 |
31 | vdata.clear();
32 | idata.clear();
33 |
34 | if (!_poly) {
35 | _poly = new Polygon( vertices )
36 | } else {
37 | _poly.numVertices = 0;
38 | _poly.addVertices.apply(null, vertices);
39 | }
40 | _poly.triangulate(idata);
41 | _poly.copyToVertexData(vdata);
42 | if (color > -1) {
43 | _color = color;
44 | vdata.colorize('color', _color);
45 | }
46 | setIndexDataChanged();
47 | }
48 |
49 | public function getVertex():VertexData {
50 | return vertexData;
51 | }
52 |
53 | public function getIndex():IndexData {
54 | return indexData;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/math/shapes/ShapeType.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.math.shapes {
8 | import starling.utils.StringUtil;
9 |
10 | public class ShapeType {
11 |
12 | public static const CIRC:ShapeType = new ShapeType("CIRC");
13 | public static const ELIP:ShapeType = new ShapeType("ELIP");
14 | public static const RECT:ShapeType = new ShapeType("RECT");
15 | public static const RRECT:ShapeType = new ShapeType("RRECT");
16 | public static const POLY:ShapeType = new ShapeType("POLY");
17 | public static const TRI:ShapeType = new ShapeType("TRI");
18 |
19 | public static function equals(shapeA:ShapeType, shapeB:ShapeType):Boolean {
20 | return shapeA == shapeB;
21 | }
22 |
23 | private var _type:String;
24 |
25 | public function ShapeType(type:String = null) {
26 | _type = type;
27 | }
28 |
29 | public function toString():String {
30 | return StringUtil.format("[ShapeType type={0}]", _type);
31 | }
32 |
33 | public function isCircle():Boolean {
34 | return this == ShapeType.CIRC;
35 | }
36 |
37 | public function isEllipse():Boolean {
38 | return this == ShapeType.ELIP;
39 | }
40 |
41 | public function isPolygon():Boolean {
42 | return this == ShapeType.POLY;
43 | }
44 |
45 | public function isRectangle():Boolean {
46 | return this == ShapeType.RECT;
47 | }
48 |
49 | public function isRoundRectangle():Boolean {
50 | return this == ShapeType.RRECT;
51 | }
52 |
53 | public function isTri():Boolean {
54 | return this == ShapeType.TRI;
55 | }
56 |
57 | public function get type():String {
58 | return _type;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/styles/FillStyle.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.styles {
8 | import flash.geom.Matrix;
9 |
10 | import starling.textures.Texture;
11 |
12 | import starling.utils.StringUtil;
13 |
14 | public class FillStyle {
15 |
16 | // TODO: add pool
17 |
18 | public var color:uint;
19 | public var alpha:Number;
20 | public var visible:Boolean;
21 | public var gradient:GradientFillStyle;
22 | public var matrix:Matrix;
23 |
24 | public var texture:Texture;
25 | // only valid for pow of 2 textures.
26 | public var textureRepeat:Boolean;
27 |
28 | public function FillStyle() {
29 | gradient = new GradientFillStyle();
30 | reset();
31 | }
32 |
33 | public function reset():void {
34 | // defaults.
35 | gradient.reset();
36 | color = 0xFFFFFF;
37 | alpha = 1;
38 | visible = false;
39 | textureRepeat=false;
40 | matrix = null ;
41 | texture = null; // might be a default WHITE texture
42 | }
43 |
44 | public function clone():FillStyle {
45 | const fill:FillStyle = new FillStyle();
46 | fill.color = color;
47 | fill.alpha = alpha;
48 | fill.texture = texture ;
49 | fill.matrix = matrix ;
50 | fill.textureRepeat=textureRepeat;
51 | fill.visible = visible;
52 | fill.gradient.copyFrom(gradient);
53 | return fill;
54 | }
55 |
56 | public function toString():String {
57 | return StringUtil.format("[FillStyle color=0x{0}, alpha={1}, visible={2}, gradient={3}, texture={4}, textureRepeat={5}]",
58 | color.toString(16).toUpperCase(), alpha, visible, gradient, texture,textureRepeat);
59 | }
60 |
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/GraphicCurves.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 04/01/2019.
4 | //
5 | // ported from PixiJS
6 | // https://github.com/pixijs/pixi.js
7 | //
8 | // =================================================================================================
9 |
10 | package com.roipeker.starling.draw.utils {
11 |
12 | /**
13 | * Graphics curves resolution settings. If `adaptive` flag is set to `true`,
14 | * the resolution is calculated based on the curve's length to ensure better visual quality.
15 | * Adaptive draw works with `bezierCurveTo` and `quadraticCurveTo`.
16 | *
17 | * @static
18 | * @constant
19 | * @memberof PIXI
20 | * @name GRAPHICS_CURVES
21 | * @type {object}
22 | * @property {boolean} adaptive=false - flag indicating if the resolution should be adaptive
23 | * @property {number} maxLength=10 - maximal length of a single segment of the curve (if adaptive = false, ignored)
24 | * @property {number} minSegments=8 - minimal number of segments in the curve (if adaptive = false, ignored)
25 | * @property {number} maxSegments=2048 - maximal number of segments in the curve (if adaptive = false, ignored)
26 | */
27 | public class GraphicCurves {
28 | public function GraphicCurves() {
29 | }
30 |
31 | public static var adaptive:Boolean = false;
32 | public static var maxLength:Number = 10;
33 | public static var minSegments:uint = 8;
34 | public static var maxSegments:uint = 2048;
35 |
36 | public static function segmentsCount(length:Number, defaultSegments:uint = 20):uint {
37 | if (adaptive) {
38 | return defaultSegments;
39 | }
40 | var result:uint = Math.ceil(length / maxLength);
41 | if (result < minSegments) {
42 | result = minSegments;
43 | } else if (result > maxSegments) {
44 | result = maxSegments;
45 | }
46 | return result;
47 | }
48 |
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/demos.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/styles/LineStyle.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.styles {
8 | import flash.display.CapsStyle;
9 | import flash.display.JointStyle;
10 |
11 | import starling.utils.StringUtil;
12 |
13 | public class LineStyle extends FillStyle {
14 |
15 | // TODO: add pool
16 |
17 | public var width:Number;
18 | public var alignment:Number;
19 |
20 | // -- extra props.
21 | public var joint:String; // JoinStyle
22 | public var caps:String; // CapsStyle
23 | public var miterLimit:Number;
24 |
25 | public function LineStyle() {
26 | reset();
27 | }
28 |
29 | override public function clone():FillStyle {
30 | const fill:LineStyle = new LineStyle();
31 | fill.texture = texture;
32 | fill.textureRepeat = textureRepeat;
33 | fill.matrix = matrix;
34 | fill.width = width ;
35 | fill.alignment = alignment;
36 | fill.joint= joint;
37 | fill.caps= caps;
38 | fill.miterLimit= miterLimit;
39 | fill.color = color ;
40 | fill.alpha= alpha;
41 | fill.visible= visible;
42 | fill.gradient.copyFrom(gradient);
43 | return fill ;
44 | }
45 |
46 | override public function reset():void {
47 | super.reset();
48 | color = 0x000000;
49 | width = 0;
50 | alignment = 0.5;
51 | miterLimit = 10;
52 | joint = JointStyle.MITER;
53 | caps = CapsStyle.SQUARE;
54 | }
55 |
56 | override public function toString():String {
57 | return StringUtil.format("[LineStyle width={0}, alignment={1}, color=0x{2}, alpha={3}, visible={4}, joint={5}, caps={6}, gradient={7}, texture={8}]",
58 | width, alignment, color.toString(16).toUpperCase(), alpha, visible, joint, caps, gradient, texture);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/styles/GradientFillStyle.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.styles {
8 | import starling.utils.StringUtil;
9 |
10 | public class GradientFillStyle {
11 |
12 | // basic gradient with 2 colors.
13 | public var color1:uint;
14 | public var color2:uint;
15 | public var alpha1:Number;
16 | public var alpha2:Number;
17 | public var angle:Number;
18 | public var visible:Boolean;
19 |
20 | public function GradientFillStyle() {
21 | color1 = 0xFFFFFF;
22 | color2 = 0x000000;
23 | alpha1 = 1;
24 | alpha2 = 1;
25 | angle = 0;
26 | visible = false;
27 | }
28 |
29 | public function reset():void {
30 | color1 = 0xFFFFFF;
31 | color2 = 0x000000;
32 | alpha1 = 1;
33 | alpha2 = 1;
34 | angle = 0;
35 | visible = false;
36 | }
37 |
38 | public function clone():GradientFillStyle {
39 | var fill:GradientFillStyle = new GradientFillStyle();
40 | fill.color1 = color1;
41 | fill.color2 = color2;
42 | fill.alpha1 = alpha1;
43 | fill.alpha2 = alpha2;
44 | fill.angle = angle;
45 | fill.visible = visible;
46 | return fill;
47 | }
48 |
49 | public function copyFrom(gradient:GradientFillStyle):void {
50 | color1 = gradient.color1;
51 | color2 = gradient.color2;
52 | alpha1 = gradient.alpha1;
53 | alpha2 = gradient.alpha2;
54 | angle = gradient.angle;
55 | visible = gradient.visible;
56 | }
57 |
58 | public function toString():String {
59 | return StringUtil.format("[GradientFillStyle colors=0x{0}-0x{1}, alphas={2}-{3}, angle={4}, visible={5}]",
60 | color1.toString(16).toUpperCase(), color2.toString(16).toUpperCase(), alpha1, alpha2, angle, visible);
61 | }
62 |
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/lines/Pnt.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 10/01/2019.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.utils.lines {
8 | public class Pnt {
9 |
10 | public var x:Number;
11 | public var y:Number;
12 |
13 | public function key():String {
14 | return x + 'x' + y;
15 | }
16 |
17 | public function Pnt(x:Number = 0, y:Number = 0) {
18 | this.x = x;
19 | this.y = y;
20 | }
21 |
22 | public function scalarMult(f:Number):Pnt {
23 | this.x *= f;
24 | this.y *= f;
25 | return this;
26 | }
27 |
28 | public function perpendicular():Pnt {
29 | var x:Number = this.x;
30 | this.x = -this.y;
31 | this.y = x;
32 | return this;
33 | }
34 |
35 | public function toString():String {
36 | return 'Pnt: x=' + x + ' y=' + y;
37 | }
38 |
39 | public function invert():Pnt {
40 | this.x = -this.x;
41 | this.y = -this.y;
42 | return this;
43 | }
44 |
45 | [Inline]
46 | public final function length():Number {
47 | return Math.sqrt(this.x * this.x + this.y * this.y);
48 | }
49 |
50 | public function normalize():Pnt {
51 | var mod:Number = this.length();
52 | this.x /= mod;
53 | this.y /= mod;
54 | return this;
55 | }
56 |
57 | [Inline]
58 | public final function angle():Number {
59 | return this.y / this.x;
60 | }
61 |
62 | [Inline]
63 | public static function Angle(p0:Pnt, p1:Pnt):Number {
64 | return Math.atan2(p1.x - p0.x, p1.y - p0.y);
65 | }
66 |
67 | public static function Add(p0:Pnt, p1:Pnt):Pnt {
68 | return new Pnt(p0.x + p1.x, p0.y + p1.y);
69 | }
70 |
71 | public static function Sub(p1:Pnt, p0:Pnt):Pnt {
72 | return new Pnt(p1.x - p0.x, p1.y - p0.y);
73 | }
74 |
75 | public static function Middle(p0:Pnt, p1:Pnt):Pnt {
76 | return Add(p0, p1).scalarMult(.5);
77 | }
78 |
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/GraphData.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // ported from PixiJS
6 | // https://github.com/pixijs/pixi.js
7 | //
8 | // =================================================================================================
9 |
10 | package com.roipeker.starling.draw {
11 | import com.roipeker.starling.draw.math.shapes.AbsShape;
12 | import com.roipeker.starling.draw.math.shapes.ShapeType;
13 | import com.roipeker.starling.draw.styles.FillStyle;
14 | import com.roipeker.starling.draw.styles.LineStyle;
15 |
16 | import flash.geom.Matrix;
17 |
18 | import starling.utils.StringUtil;
19 |
20 | public class GraphData {
21 |
22 | // TODO: add pool
23 |
24 | public var shape:AbsShape;
25 | public var fillStyle:FillStyle;
26 | public var lineStyle:LineStyle;
27 | public var matrix:Matrix;
28 | public var type:ShapeType;
29 | public var points:Array;
30 | public var holes:Array;
31 |
32 | public function GraphData(shape:AbsShape, fillStyle:FillStyle = null, lineStyle:LineStyle = null, matrix:Matrix = null) {
33 | this.shape = shape;
34 | this.fillStyle = fillStyle;
35 | this.lineStyle = lineStyle;
36 | this.matrix = matrix;
37 | type = shape.type;
38 | points = [];
39 | holes = [];
40 | }
41 |
42 | public function reset():void {
43 | // TODO: when add pool to holes, check if contains graphData.
44 | /*for (var i:int = 0, ilen:int = holes.length; i < ilen; i++) {
45 | holes[i].reset();
46 | }*/
47 | // return pool
48 | fillStyle.reset();
49 | lineStyle.reset();
50 | shape.reset();
51 | points.length = 0;
52 | holes.length = 0;
53 | }
54 |
55 | public function clone():GraphData {
56 | return new GraphData(shape, fillStyle, lineStyle, matrix);
57 | }
58 |
59 | public function dispose():void {
60 | reset();
61 | shape = null;
62 | holes = null;
63 | points = null;
64 | lineStyle = null;
65 | fillStyle = null;
66 | matrix = null;
67 | }
68 |
69 | public function toString():String {
70 | return StringUtil.format("[GraphData type={0}\nfill_style={1}\nline_style={2}\nshape={3}]",
71 | type, fillStyle, lineStyle, shape);
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/builders/MeshBuilder.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.builders {
8 | import com.roipeker.starling.draw.GraphData;
9 | import com.roipeker.starling.draw.GraphGeom;
10 | import com.roipeker.starling.draw.math.shapes.MeshShape;
11 |
12 | public class MeshBuilder extends AbsShapeBuilder {
13 |
14 | public function MeshBuilder() {
15 | super();
16 | }
17 |
18 | override public function build(shapeData:GraphData):void {
19 | const shape:MeshShape = shapeData.shape as MeshShape;
20 | // vectors vs array.
21 | const len:int = shape.vertices.length;
22 | for (var i:int = 0; i < len; i++) {
23 | shapeData.points[i] = shape.vertices[i];
24 | }
25 | }
26 |
27 | override public function triangulate(shapeData:GraphData, geometry:GraphGeom):void {
28 |
29 | // TODO: doesnt work well with lineStyle, only fills.
30 |
31 | const shapePoints:Vector. = MeshShape(shapeData.shape).vertices;
32 | const shapeIndices:Vector. = MeshShape(shapeData.shape).indices;
33 |
34 | const verts:Array = geometry.points;
35 | const indices:Array = geometry.indices;
36 |
37 | // vectors vs array.
38 | for (var i:int = 0, ilen:int = shapePoints.length; i < ilen; i++) {
39 | verts[verts.length] = shapePoints[i];
40 | }
41 |
42 | if (shapeIndices && shapeIndices.length) {
43 | ilen = shapeIndices.length;
44 | for (i = 0; i < ilen; i++) {
45 | indices[indices.length] = shapeIndices[i];
46 | }
47 | } else {
48 | // validate amount of vertices...
49 | var numPoints:int = shapePoints.length / 2;
50 | if (numPoints % 3 != 0) {
51 | trace("[MeshBuilder] Error in triangulation, must be divisible by 3");
52 | } else {
53 | const vertPos:int = geometry.points.length >> 1;
54 | for (i = 0; i < numPoints; i += 3) {
55 | indices.push(
56 | vertPos + i, vertPos + 1 + i, vertPos + 2 + i
57 | );
58 | }
59 | }
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/math/shapes/MeshShape.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 03/01/2019.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.math.shapes {
8 | import starling.utils.StringUtil;
9 |
10 | // TODO: doesn't work well with lineStyle.
11 |
12 | public class MeshShape extends AbsShape {
13 |
14 | /*private static const _pool:Vector. = new Vector.();
15 | public static function put(obj:MeshShape):void {
16 | if (obj) {
17 | obj.indices = null;
18 | obj.vertices = null;
19 | obj.uvs = null;
20 | _pool[_pool.length] = obj;
21 | }
22 | }
23 |
24 | public static function get(vertices:Vector., indices:Vector. = null, uvs:Array = null):MeshShape {
25 | if (_pool.length == 0) return new MeshShape(vertices, indices, uvs);
26 | var obj:MeshShape = _pool.pop();
27 | obj.vertices = vertices;
28 | obj.indices = indices;
29 | obj.uvs = uvs;
30 | return obj;
31 | }*/
32 |
33 | /*public static function get(vertices:Array, indices:Array, uvs:Array):MeshShape {
34 | return new MeshShape(vertices, indices, uvs);
35 | }*/
36 |
37 | public static function get empty():MeshShape {
38 | return new MeshShape();
39 | }
40 |
41 | public var vertices:Vector.;
42 | public var indices:Vector.;
43 | public var uvs:Array;
44 |
45 | /**
46 | * @param vertices
47 | * @param indices
48 | * @param uvs
49 | */
50 | public function MeshShape(vertices:Vector. = null, indices:Vector. = null, uvs:Array = null) {
51 | this.vertices = vertices;
52 | this.indices = indices;
53 | this.uvs = uvs;
54 | super(ShapeType.TRI);
55 | }
56 |
57 | public function clone():MeshShape {
58 | return new MeshShape(vertices, indices, uvs);
59 | }
60 |
61 | public function copyFrom(rect:MeshShape):MeshShape {
62 | vertices = rect.vertices;
63 | indices = rect.indices;
64 | uvs = rect.uvs;
65 | return this;
66 | }
67 |
68 | override public function toString():String {
69 | return StringUtil.format(
70 | '[ MeshShape vertices={0}, indices={1}, uvs={2} ]', vertices, indices, uvs);
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/builders/PolyBuilder.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.builders {
8 | import com.roipeker.starling.draw.GraphData;
9 | import com.roipeker.starling.draw.GraphGeom;
10 | import com.roipeker.starling.draw.math.shapes.Poly;
11 | import com.roipeker.starling.draw.utils.earcut.Earcut;
12 |
13 | public class PolyBuilder extends AbsShapeBuilder {
14 |
15 | public function PolyBuilder() {
16 | super();
17 | }
18 |
19 | override public function build(shapeData:GraphData):void {
20 | const points:Array = Poly(shapeData.shape).points;
21 | for (var i:int = 0, ilen:int = points.length; i < ilen; i++) {
22 | shapeData.points[i] = points[i];
23 | }
24 | }
25 |
26 | override public function triangulate(shapeData:GraphData, geometry:GraphGeom):void {
27 | var points:Array = shapeData.points;
28 | if (points.length >= 6) {
29 | const verts:Array = geometry.points;
30 | const indices:Array = geometry.indices;
31 | var ilen:int;
32 | var holes:Array = [];
33 |
34 | ilen = shapeData.holes.length;
35 | if (ilen) {
36 | for (var i:int = 0; i < ilen; i++) {
37 | // holes.push(points.length / 2);
38 | holes[holes.length] = points.length / 2;
39 | points = points.concat(shapeData.holes[i].points);
40 | }
41 | }
42 |
43 | var triangles:Array = Earcut.earcut(points, holes );
44 | if (!triangles) return;
45 |
46 | const vertPos:int = verts.length >> 1;
47 |
48 | ilen = triangles.length;
49 | for (i = 0; i < ilen; i += 3) {
50 | /*indices.push(
51 | triangles[i] + vertPos,
52 | triangles[i + 1] + vertPos,
53 | triangles[i + 2] + vertPos
54 | );*/
55 | indices[indices.length] = triangles[i] + vertPos;
56 | indices[indices.length] = triangles[i + 1] + vertPos;
57 | indices[indices.length] = triangles[i + 2] + vertPos;
58 | }
59 |
60 | ilen = points.length;
61 | for (i = 0; i < ilen; i++) {
62 | verts[verts.length] = points[i];
63 | }
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/math/shapes/Poly.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.math.shapes {
8 | import flash.geom.Point;
9 |
10 | import starling.utils.StringUtil;
11 |
12 | public class Poly extends AbsShape {
13 |
14 | public var closed:Boolean;
15 | public var points:Array;
16 |
17 | public function Poly(points:Array = null) {
18 | this.points = [];
19 | super(ShapeType.POLY);
20 | if (points != null) {
21 | setup(points);
22 | }
23 | }
24 |
25 | override public function reset():void {
26 | super.reset();
27 | points.length = 0;
28 | }
29 |
30 | public function setup(points:Array):void {
31 | if (points[0] is Array) {
32 | points = points[0];
33 | } else if (points[0] is Point) {
34 | var p:Array = [];
35 | for (var i:int = 0, ilen:int = points.length; i < ilen; i++) {
36 | p[p.length] = points[i].x;
37 | p[p.length] = points[i].y;
38 | }
39 | points = p;
40 | }
41 | closed = true;
42 | this.points = points;
43 | close();
44 | }
45 |
46 | public function clone():Poly {
47 | return new Poly(this.points);
48 | }
49 |
50 | public function close():void {
51 | closed = true;
52 | const arr:Array = points;
53 | if (arr[0] != arr[arr.length - 2] || arr[1] != arr[arr.length - 1]) {
54 | arr[arr.length] = arr[0];
55 | arr[arr.length] = arr[1];
56 | // arr.push(arr[0], arr[1]);
57 | }
58 | }
59 |
60 | public function contains(px:Number, py:Number):Boolean {
61 | var inside:Boolean = false;
62 | const arr:Array = points;
63 | const len:int = arr.length >> 1;
64 | var j:int = len - 1;
65 | for (var i:int = 0; i < len; i++) {
66 | var xi:Number = arr[i * 2];
67 | var yi:Number = arr[(i * 2) + 1];
68 | var xj:Number = arr[j * 2];
69 | var yj:Number = arr[(j * 2) + 1];
70 | const intersect:Boolean = ((yi > py) !== (yj > py)) && (px < ((xj - xi) * ((py - yi) / (yj - yi))) + xi);
71 | if (intersect) {
72 | inside = !inside;
73 | }
74 | }
75 | return inside;
76 | }
77 |
78 | override public function toString():String {
79 | return StringUtil.format("[Poly #points={0}]", this.points.length >> 1);
80 | }
81 |
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/demos/DemoSVG.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.svg.SVGDraw;
9 |
10 | import flash.geom.Rectangle;
11 |
12 | import starling.display.Sprite;
13 | import starling.events.Event;
14 | import starling.utils.RectangleUtil;
15 |
16 | public class DemoSVG extends Sprite {
17 |
18 | public function DemoSVG() {
19 | addEventListener(Event.ADDED_TO_STAGE, init);
20 | }
21 |
22 | private function init(event:Event):void {
23 |
24 | stage.color = 0x333333;
25 |
26 | var svg:SVGDraw = new SVGDraw();
27 | addChild(svg);
28 |
29 | // DrawLib has a limitation when it draws polygons, as it doesn't support winding.
30 | // So when you have "holes" inside shapes, you can fill them with a color, and define in the
31 | // or set the property svg::holeColor=0xff0000.
32 |
33 | // svg.parse(svg_samples.icoYesXML); // the CHECK has a fill color that is defined as hole, check the SVG.
34 | // svg.parse(svg_samples.pencilXML);
35 | // svg.parse(svg_samples.materialIcoCheck);
36 | // svg.parse(svg_samples.gmailIcon);
37 | // svg.parse(svg_samples.icoErrorXml);
38 | // svg.parse(svg_samples.tiger);
39 | // svg.parse(svg_samples.android);
40 | // svg.parse(svg_samples.fish);
41 | // svg.parse(svg_samples.modzilla1);
42 | // svg.parse(svg_samples.apple);
43 | // svg.parse(svg_samples.face);
44 | // svg.parse(svg_samples.google);
45 |
46 | // svg.parse(svg_samples.naturalDisaster1);
47 | // svg.parse(svg_samples.naturalDisaster2);
48 | svg.parse(svg_samples.emergency1);
49 |
50 | // buggy
51 | // svg.parse(svg_samples.gmaps);
52 | // svg.parse(svg_samples.gphotos);
53 |
54 | // center pivot, adjust size, center...
55 | svg.validate();
56 | svg.alignPivot();
57 |
58 | const max:Rectangle = new Rectangle(0, 0, stage.stageWidth / 2, stage.stageHeight / 2);
59 |
60 | // match the biggest dimensions
61 | var out:Rectangle = RectangleUtil.fit(svg.bounds, max);
62 | // trace(out);
63 |
64 | svg.width = out.width;
65 | svg.height = out.height;
66 | // svg.scaleY = svg.scaleX;
67 | svg.x = stage.stageWidth / 2;
68 | svg.y = stage.stageHeight / 2;
69 |
70 | /*svg.width = stage.stageWidth / 2;
71 | svg.scaleY = svg.scaleX;
72 | svg.x = stage.stageWidth / 2;
73 | svg.y = stage.stageHeight / 2;*/
74 |
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/draw demos.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/Boot.as:
--------------------------------------------------------------------------------
1 | package {
2 |
3 | import flash.display.Bitmap;
4 | import flash.display.BitmapData;
5 | import flash.display.Loader;
6 | import flash.display.Sprite;
7 | import flash.display.StageAlign;
8 | import flash.display.StageQuality;
9 | import flash.display.StageScaleMode;
10 | import flash.events.Event;
11 | import flash.filesystem.File;
12 | import flash.filesystem.FileMode;
13 | import flash.filesystem.FileStream;
14 | import flash.geom.Rectangle;
15 | import flash.utils.ByteArray;
16 |
17 | import starling.core.Starling;
18 |
19 | [SWF(width="800", height="600", backgroundColor="#FFFFFF", frameRate="60")]
20 | public class Boot extends Sprite {
21 |
22 | private var starling:Starling;
23 |
24 | public function Boot() {
25 |
26 | init2();
27 |
28 | // loaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);
29 | }
30 |
31 | private function init2():void {
32 | var path:String = '/Users/rodrigo/dev/flutter/mine/flutter_scrapper/assets/images/wild.png';
33 | var f:File = new File(path);
34 | var fs:FileStream = new FileStream();
35 | var ba:ByteArray = new ByteArray();
36 | fs.open(f,FileMode.READ);
37 | fs.readBytes(ba);
38 | fs.close();
39 |
40 | var loader:Loader= new Loader();
41 | loader.contentLoaderInfo.addEventListener("complete", function(e){
42 |
43 |
44 | var bd:BitmapData = Bitmap(loader.content).bitmapData;
45 | trace( bd.width>>1, bd.height>>1 );
46 | // var px:uint = bd.getPixel(bd.width>>1, bd.height>>1);
47 | var px:uint = bd.getPixel32(473, 856);
48 |
49 | trace('iag loaded', px, '-', px.toString(16));
50 |
51 | });
52 | loader.loadBytes(ba);
53 | }
54 |
55 | private function onLoaderComplete(event:Event):void {
56 | stage.scaleMode = StageScaleMode.NO_SCALE;
57 | stage.align = StageAlign.TOP_LEFT;
58 | stage.quality = StageQuality.HIGH;
59 |
60 | starling = new Starling(StarlingRoot, stage);
61 | starling.antiAliasing = 2;
62 | starling.supportBrowserZoom = false;
63 | starling.supportHighResolutions = true;
64 | // starling.skipUnchangedFrames = true;
65 | starling.simulateMultitouch = false;
66 | starling.start();
67 |
68 | stage.addEventListener(Event.RESIZE, onStageResize);
69 | }
70 |
71 | private function onStageResize(evt:Event):void {
72 | if (starling) {
73 | starling.stage.stageWidth = stage.stageWidth;
74 | starling.stage.stageHeight = stage.stageHeight;
75 | const viewport:Rectangle = starling.viewPort;
76 | viewport.width = stage.stageWidth;
77 | viewport.height = stage.stageHeight;
78 | starling.viewPort = viewport;
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # draw-lib
2 |
3 | Draw tries to mimic [AS3 Graphics API](https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Graphics.html) but is powered by [Starling](https://gamua.com/starling/). Was heavily ported from the Graphics implementation in PixiJS.
4 | And, although I do not recommend it for production use, the code wasn't tested enough and is unoptimized, it seems to work fine for simple vector shapes in my testings.
5 |
6 | -------------
7 |
8 | #### Donation
9 | Support **draw-lib** via [Paypal](https://www.paypal.me/roipeker/)
10 |
11 | [](https://www.paypal.me/roipeker/)
12 |
13 |
14 |
15 | ##### IDE Software provided by JetBrains
16 | [](https://www.jetbrains.com)
17 |
18 | -------------
19 |
20 |
21 | ## Getting Started
22 |
23 | The code in this repo contains a *IntellijIdea* project with 2 projects modules:
24 | * draw_demos
25 | * draw_lib
26 |
27 | You can get a precompiled binary for Draw inside [_/draw_lib/bin-release/draw_lib.swc_](https://github.com/roipeker/draw-lib/tree/master/draw_lib/bin-release/draw_lib.swc), but the Intellij project uses Build Configurations dependencies to run the samples.
28 |
29 | ### Prerequisites
30 |
31 | Intellij and some version of [AdobeSDK](https://www.adobe.com/devnet/air/air-sdk-download.html) compatible with Starling v2.5.1
32 |
33 | ### API and demos.
34 |
35 | Considere the API very similar to the AS3 Graphics one.
36 |
37 | I didn't have the time to write documentation (or code comments), but 90% of the code was ported from PixiJS, so check their [docs](http://pixijs.download/dev/docs/PIXI.Graphics.html).
38 |
39 | ## demos screenshots
40 |
41 | 
42 | 
43 | 
44 | 
45 | 
46 | 
47 | 
48 | 
49 | 
50 | 
51 |
52 |
53 | ## Contributing
54 |
55 | That's why I setup this repo! So, use pull request.
56 |
57 | You can also [buy me a coffee](https://www.paypal.me/roipeker/).
58 |
59 |
60 | ## Authors
61 |
62 | * **Rodrigo Lopez** - *Initial work* - [roipeker](https://github.com/roipeker)
63 |
64 | ## Acknowledgments
65 |
66 | * [Starling](https://forum.starling-framework.org/)
67 | * [PixiJS](https://www.pixijs.com/)
68 |
69 | ## License
70 |
71 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
72 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/math/shapes/RoundRect.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.math.shapes {
8 | import starling.utils.StringUtil;
9 |
10 | public class RoundRect extends AbsShape {
11 |
12 | public static function get empty():RoundRect {
13 | return new RoundRect();
14 | }
15 |
16 | public var x:Number;
17 | public var y:Number;
18 | public var w:Number;
19 | public var h:Number;
20 | public var tlr:Number;
21 | public var trr:Number;
22 | public var blr:Number;
23 | public var brr:Number;
24 |
25 | public function RoundRect(x:Number = 0, y:Number = 0, w:Number = 0, h:Number = 0, tlr:Number = 10, trr:Number = 10, blr:Number = 10, brr:Number = 10) {
26 | this.x = x;
27 | this.y = y;
28 | this.w = w;
29 | this.h = h;
30 | this.tlr = tlr;
31 | this.trr = trr;
32 | this.blr = blr;
33 | this.brr = brr;
34 | adjustRadius();
35 | super(ShapeType.RRECT);
36 | }
37 |
38 | private final function adjustRadius():void {
39 | if (trr < 0) trr = tlr;
40 | if (blr < 0) blr = tlr;
41 | if (brr < 0) brr = tlr;
42 | }
43 |
44 | public function clone():RoundRect {
45 | return new RoundRect(x, y, w, h, tlr, trr, blr, brr);
46 | }
47 |
48 | // not used.
49 | // TODO: add all rect corners checks.
50 | public function contains(px:Number, py:Number):Boolean {
51 | if (w <= 0 || h <= 0) return false;
52 | if (px >= x && px < x + w) {
53 | if (py >= y && py < y + h) {
54 | if ((py >= y + tlr && py <= y + h - tlr) ||
55 | (px >= x + tlr && px <= x + w - tlr)) {
56 | return true;
57 | }
58 | var dx:Number = px - (x + tlr);
59 | var dy:Number = py - (y + tlr);
60 | const radius2:Number = tlr * tlr;
61 | if ((dx * dx + dy * dy) <= radius2) return true;
62 | dx = px - (x + w - radius2);
63 | if ((dx * dx + dy * dy) <= radius2) return true;
64 | dy = py - (y + h - radius2);
65 | if ((dx * dx + dy * dy) <= radius2) return true;
66 | dx = px - (x + tlr);
67 | if ((dx * dx + dy * dy) <= radius2) return true;
68 | }
69 | }
70 | return false;
71 | }
72 |
73 | override public function toString():String {
74 | return StringUtil.format("[RoundRect x={0}, y={1}, halfW={2}, halfH={3}, radius={4}]", x, y, w, h, tlr);
75 | }
76 |
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/builders/CircleBuilder.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.builders {
8 | import com.roipeker.starling.draw.GraphData;
9 | import com.roipeker.starling.draw.GraphGeom;
10 | import com.roipeker.starling.draw.math.shapes.Circle;
11 | import com.roipeker.starling.draw.math.shapes.Ellipse;
12 |
13 | public class CircleBuilder extends AbsShapeBuilder {
14 |
15 | // Defines a global factor to reduce by the radius of width and height
16 | // Higher number = less segments.
17 | public static var segmentReduceFactor:Number = 2.5;
18 |
19 | public function CircleBuilder() {
20 | super();
21 | }
22 |
23 | override public function build(shapeData:GraphData):void {
24 | const points:Array = shapeData.points;
25 | var x:Number, y:Number, w:Number = 0, h:Number = 0;
26 | var totalSegments:int;
27 |
28 | if (shapeData.type.isCircle()) {
29 | const circleData:Circle = shapeData.shape as Circle;
30 | x = circleData.x;
31 | y = circleData.y;
32 | w = h = circleData.radius;
33 | // totalSegments = Math.floor(30 * Math.sqrt(halfW)) / segmentReduceFactor;
34 | } else if (shapeData.type.isEllipse()) {
35 | const ellipseData:Ellipse = shapeData.shape as Ellipse;
36 | x = ellipseData.x;
37 | y = ellipseData.y;
38 | w = ellipseData.halfW;
39 | h = ellipseData.halfH;
40 | // totalSegments = Math.floor(15 * Math.sqrt(halfW + halfH)) / segmentReduceFactor;
41 | }
42 |
43 | if (w == 0 || h == 0) return;
44 | totalSegments = 15 * Math.sqrt(w + h) | 0;
45 | totalSegments /= segmentReduceFactor;
46 | points.length = 0;
47 |
48 | const segment:Number = (Math.PI * 2) / totalSegments;
49 |
50 | for (var i:int = 0; i < totalSegments; i++) {
51 | const angle:Number = segment * i;
52 | points.push(
53 | x + (-Math.sin(angle) * w),
54 | y + (-Math.cos(angle) * h)
55 | );
56 | }
57 | points.push(points[0], points[1]);
58 | }
59 |
60 | override public function triangulate(shapeData:GraphData, geometry:GraphGeom):void {
61 | const points:Array = shapeData.points;
62 | const verts:Array = geometry.points;
63 | const indices:Array = geometry.indices;
64 | const center:int = verts.length >> 1;
65 | var vertPos:int = center;
66 |
67 | // Circle and Ellipse have x,y
68 | verts.push(shapeData.shape['x'], shapeData.shape['y']);
69 | for (var i:int = 0, ilen:int = points.length; i < ilen; i += 2) {
70 | verts.push(points[i], points[int(i + 1)]);
71 | indices.push(vertPos, center, ++vertPos);
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/demos/DemoLineStyles.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-16.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import flash.display.CapsStyle;
11 | import flash.display.JointStyle;
12 |
13 | import starling.display.Sprite;
14 | import starling.events.Event;
15 |
16 | public class DemoLineStyles extends Sprite {
17 |
18 | public function DemoLineStyles() {
19 | addEventListener(Event.ADDED_TO_STAGE, init);
20 | }
21 |
22 | private function init(event:Event):void {
23 | var draw:Draw = new Draw();
24 | addChild(draw);
25 |
26 | // when default to JoinStyle.MITTER, CapsStyle.SQUARE, it uses the optimized PixiJS lines routine, waaay less polygons.
27 | // ans also supports ::alignment to shift from 0 (line outside the shape), to 1 (line inside shape)... although there's no restrictions for the value.
28 | draw.lineStyle(20, 0x0, .5);
29 | draw.moveTo(490, 40);
30 | draw.lineTo(600, 200);
31 | draw.lineTo(640, 100);
32 |
33 |
34 | // How aligmnent works.....
35 | draw.lineStyle(10, 0xff0000, .25, 1);
36 | draw.beginFill(0x00ff00, 1);
37 | draw.drawCircle(50, 300, 40);
38 | draw.endFill();
39 |
40 | draw.lineStyle(10, 0xff0000, .25, .5);
41 | draw.beginFill(0x00ff00, 1);
42 | draw.drawCircle(150, 300, 40);
43 | draw.endFill();
44 |
45 | draw.lineStyle(10, 0xff0000, .25, 0);
46 | draw.beginFill(0x00ff00, 1);
47 | draw.drawCircle(250, 300, 40);
48 | draw.endFill();
49 |
50 | // with -1, the line offsets to the outside to the specified line width (thickness)
51 | draw.lineStyle(10, 0xff0000, .25, -1);
52 | draw.beginFill(0x00ff00, 1);
53 | draw.drawCircle(350, 300, 40);
54 | draw.endFill();
55 |
56 | /// ------
57 |
58 | // complex line style (joins and caps) uses a different "rendering" system...
59 |
60 | draw.lineStyle(12, 0xff0000, .75, 0, JointStyle.BEVEL, CapsStyle.ROUND);
61 | draw.beginFill(0x00ff00, .6);
62 | draw.drawRect(50, 50, 80, 80);
63 | draw.endFill();
64 |
65 | draw.lineStyle(20, 0xff0000, 1, 0.5, JointStyle.ROUND, CapsStyle.ROUND);
66 | draw.moveTo(120 + 60, 80);
67 | draw.lineTo(120 + 200, 200);
68 | draw.lineTo(120 + 300, 50);
69 | draw.lineTo(120 + 10, 200);
70 |
71 | draw.lineStyle(4, 0x00ff00, .7, 0.5, JointStyle.BEVEL, CapsStyle.NONE);
72 | draw.moveTo(120 + 60, 80);
73 | draw.lineTo(120 + 200, 200);
74 | draw.lineTo(120 + 300, 50);
75 | draw.lineTo(120 + 10, 200);
76 |
77 | // 2 color gradients in fills and lines (Thanks to @JohnBlackburne)
78 | draw.lineGradientStyle(5, 0xc21500, 0xffc500, Math.PI / 2, .4, .4, 0.5);
79 | draw.beginGradientFill(0xe4e4d9, 0x215f00, Math.PI, 1, 1);
80 | draw.drawRoundRectComplex(450, 280, 130, 90, 20, 12, 40, 20);
81 | draw.endFill();
82 |
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/QuadraticUtils.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 04/01/2019.
4 | //
5 | // ported from PixiJS
6 | // https://github.com/pixijs/pixi.js
7 | //
8 | // =================================================================================================
9 |
10 | package com.roipeker.starling.draw.utils {
11 | public class QuadraticUtils {
12 | public function QuadraticUtils() {
13 | }
14 |
15 | /**
16 | * Calculate length of quadratic curve
17 | * @see {@link http://www.malczak.linuxpl.com/blog/quadratic-bezier-curve-length/}
18 | * for the detailed explanation of math behind this.
19 | *
20 | * @private
21 | * @param {number} fromX - x-coordinate of curve start point
22 | * @param {number} fromY - y-coordinate of curve start point
23 | * @param {number} cpX - x-coordinate of curve control point
24 | * @param {number} cpY - y-coordinate of curve control point
25 | * @param {number} toX - x-coordinate of curve end point
26 | * @param {number} toY - y-coordinate of curve end point
27 | * @return {number} Length of quadratic curve
28 | */
29 | public static function quadraticBezierLength(p0x:Number, p0y:Number, p1x:Number, p1y:Number, p2x:Number, p2y:Number):Number {
30 | var ax:Number = p0x - 2 * p1x + p2x;
31 | var ay:Number = p0y - 2 * p1y + p2y;
32 | var bx:Number = 2 * p1x - 2 * p0x;
33 | var by:Number = 2 * p1y - 2 * p0y;
34 | var A:Number = 4 * (ax * ax + ay * ay);
35 | var B:Number = 4 * (ax * bx + ay * by);
36 | var C:Number = bx * bx + by * by;
37 | var Sabc:Number = 2 * Math.sqrt(A + B + C);
38 | var A_2:Number = Math.sqrt(A);
39 | var A_32:Number = 2 * A * A_2;
40 | var C_2:Number = 2 * Math.sqrt(C);
41 | var BA:Number = B / A_2;
42 | return (A_32 * Sabc + A_2 * B * (Sabc - C_2) + (4 * C * A - B * B) * Math.log((2 * A_2 + BA + Sabc) / (BA + C_2))) / (4 * A_32);
43 | }
44 |
45 | /**
46 | * Calculate the points for a quadratic bezier curve and then draws it.
47 | * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c
48 | *
49 | * @param {number} cpX - Control point x
50 | * @param {number} cpY - Control point y
51 | * @param {number} toX - Destination point x
52 | * @param {number} toY - Destination point y
53 | * @param {number[]} points - Points to add segments to.
54 | */
55 | public static function curveTo(cpX:Number, cpY:Number, toX:Number, toY:Number, points:Array):void {
56 | var fromX:Number = points[points.length - 2];
57 | var fromY:Number = points[points.length - 1];
58 | const n:uint = GraphicCurves.segmentsCount(
59 | QuadraticUtils.quadraticBezierLength(fromX, fromY, cpX, cpY, toX, toY)
60 | );
61 | var xa:Number = 0;
62 | var ya:Number = 0;
63 | var j:Number;
64 | for (var i:int = 1; i <= n; ++i) {
65 | j = i / n;
66 | xa = fromX + ((cpX - fromX) * j);
67 | ya = fromY + ((cpY - fromY) * j);
68 | points.push(xa + (((cpX + ((toX - cpX) * j)) - xa) * j),
69 | ya + (((cpY + ((toY - cpY) * j)) - ya) * j));
70 | }
71 | }
72 | }
73 | }
74 |
75 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/builders/GraphicsDataBuild.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.builders {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import flash.display.*;
11 | import flash.utils.Dictionary;
12 |
13 | public class GraphicsDataBuild {
14 | public function GraphicsDataBuild() {
15 | }
16 |
17 | // SPECIAL TO BUILD.
18 | public static var _graphCommandsMap:Dictionary;
19 |
20 | public static function initGraphMapClasses():void {
21 | if(_graphCommandsMap) return ;
22 | _graphCommandsMap = new Dictionary(false);
23 | _graphCommandsMap[GraphicsStroke] = _grapStroke;
24 | _graphCommandsMap[GraphicsPath] = _graphPath;
25 | _graphCommandsMap[GraphicsSolidFill] = _graphSolidFill;
26 | _graphCommandsMap[GraphicsEndFill] = _graphEndFill;
27 | _graphCommandsMap[GraphicsGradientFill] = _graphGradientFill;
28 | }
29 |
30 | private static function _grapStroke(obj:Draw, g:GraphicsStroke) {
31 | var o:Object = {
32 | width: g.thickness || 0,
33 | miterlimit: g.miterLimit,
34 | caps: CapsStyle.SQUARE,//g.caps,
35 | joints: JointStyle.MITER//g.joints
36 | };
37 | if (g.fill) {
38 | if (g.fill is GraphicsSolidFill) addSolid(g.fill as GraphicsSolidFill);
39 | }
40 |
41 | function addSolid(fill:GraphicsSolidFill):void {
42 | o.color = fill.color, o.alpha = fill.alpha;
43 | }
44 |
45 | obj.lineStyle(o.width, o.color, o.alpha, .5, o.joints, o.caps, o.miterlimit);
46 | }
47 |
48 | private static function _graphPath(obj:Draw, g:GraphicsPath) {
49 | obj.drawPath(g.commands, g.data);
50 | }
51 |
52 | private static function _graphSolidFill(obj:Draw, g:GraphicsSolidFill) {
53 | obj.beginFill(g.color, g.alpha);
54 | }
55 |
56 | private static function _graphGradientFill(obj:Draw, g:GraphicsGradientFill) {
57 | var angle:Number = 0;
58 | if (g.matrix) {
59 | const a:Number = g.matrix.a;
60 | const b:Number = g.matrix.b;
61 | const c:Number = g.matrix.c;
62 | const d:Number = g.matrix.d;
63 | var r:Number;
64 | if (a != 0 || b != 0) {
65 | r = Math.sqrt(a * a + b * b);
66 | angle = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
67 | } else if (c != 0 || d != 0) {
68 | r = Math.sqrt(c * c + d * d);
69 | angle = Math.PI / 2 - (d > 0 ? Math.acos(-c / r) : -Math.acos(c / r));
70 | }
71 | }
72 | obj.beginGradientFill(g.colors[1], g.colors[0], angle, g.alphas[0], g.alphas[1]);
73 | }
74 |
75 | private static function _graphEndFill(obj:Draw, g:GraphicsEndFill) {
76 | obj.endFill();
77 | }
78 |
79 | public function drawGraphicsData(graphicsData:Vector.):void {
80 | if (!_graphCommandsMap) initGraphMapClasses();
81 | for (var i:int = 0, ilen:int = graphicsData.length; i < ilen; i++) {
82 | var gd:IGraphicsData = graphicsData[i];
83 | var clase:Class = Object(gd).constructor;
84 | if (clase && _graphCommandsMap[clase]) {
85 | _graphCommandsMap[clase](this, gd);
86 | } else {
87 | trace("DrawGraphicsData unsupported command:", clase, JSON.stringify(gd));
88 | }
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/demos/DemoPixiAdvanced.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import starling.display.Sprite;
11 | import starling.events.Event;
12 |
13 | /**
14 | *
15 | * Ported from
16 | * https://pixijs.io/examples/#/graphics/advanced.js
17 | *
18 | * WARNING: texture fills/lineStyles not supported.
19 | *
20 | */
21 | public class DemoPixiAdvanced extends Sprite {
22 |
23 | public function DemoPixiAdvanced() {
24 | addEventListener(Event.ADDED_TO_STAGE, init);
25 | }
26 |
27 | private function init(event:Event):void {
28 |
29 | stage.color = 0x333333;
30 |
31 | // const sprite = PIXI.Sprite.from('examples/assets/bg_rotate.jpg');
32 |
33 | // // BEZIER CURVE ////
34 | // information: https://en.wikipedia.org/wiki/Bézier_curve
35 |
36 | const realPath:Draw = new Draw();
37 | realPath.lineStyle(2, 0xFFFFFF, 1);
38 | realPath.moveTo(0, 0);
39 | realPath.lineTo(100, 200);
40 | realPath.lineTo(200, 200);
41 | realPath.lineTo(240, 100);
42 |
43 | realPath.x = 50;
44 | realPath.y = 50;
45 |
46 | addChild(realPath);
47 |
48 |
49 | const bezier:Draw = new Draw();
50 | bezier.lineStyle(5, 0xAA0000, 1);
51 | bezier.bezierCurveTo(100, 200, 200, 200, 240, 100);
52 |
53 | bezier.x = 50;
54 | bezier.y = 50;
55 |
56 | addChild(bezier);
57 |
58 |
59 | // // BEZIER CURVE 2 ////
60 | const realPath2:Draw = new Draw();
61 | realPath2.lineStyle(2, 0xFFFFFF, 1);
62 | realPath2.moveTo(0, 0);
63 | realPath2.lineTo(0, -100);
64 | realPath2.lineTo(150, 150);
65 | realPath2.lineTo(240, 100);
66 |
67 | realPath2.x = 320;
68 | realPath2.y = 150;
69 |
70 | addChild(realPath2);
71 |
72 |
73 |
74 | const bezier2:Draw = new Draw();
75 | bezier2.lineStyle(10, 0xffffff);
76 | // bezier2.lineTextureStyle(10, sprite.texture);
77 | bezier2.bezierCurveTo(0, -100, 150, 150, 240, 100);
78 |
79 | bezier2.x = 320;
80 | bezier2.y = 150;
81 |
82 | addChild(bezier2);
83 |
84 | // // ARC ////
85 | const arc:Draw = new Draw();
86 | arc.lineStyle(5, 0xAA00BB, 1);
87 | arc.arc(600, 100, 50, Math.PI, 2 * Math.PI);
88 |
89 | addChild(arc);
90 |
91 | // // ARC 2 ////
92 | const arc2:Draw = new Draw();
93 | arc2.lineStyle(6, 0x3333DD, 1);
94 | arc2.arc(650, 270, 60, 2 * Math.PI, 3 * Math.PI / 2);
95 |
96 | addChild(arc2);
97 |
98 | // // ARC 3 ////
99 | const arc3:Draw = new Draw();
100 | arc3.lineStyle(20, 0xff0000);
101 | // arc3.lineTextureStyle(20, sprite.texture);
102 | arc3.arc(650, 420, 60, 2 * Math.PI, 2.5 * Math.PI / 2);
103 |
104 | addChild(arc3);
105 |
106 | // / Hole ////
107 | const rectAndHole:Draw = new Draw();
108 | rectAndHole.beginFill(0x00FF00);
109 | rectAndHole.drawRect(350, 350, 150, 150);
110 | rectAndHole.beginHole();
111 | rectAndHole.drawCircle(375, 375, 25);
112 | rectAndHole.drawCircle(425, 425, 25);
113 | rectAndHole.drawCircle(475, 475, 25);
114 | rectAndHole.endHole();
115 | rectAndHole.endFill();
116 |
117 | addChild(rectAndHole);
118 |
119 | // // Line Texture Style ////
120 | const beatifulRect:Draw = new Draw();
121 |
122 | // beatifulRect.lineTextureStyle(20, sprite.texture);
123 | beatifulRect.lineStyle(20, 0x00ff00);
124 | beatifulRect.beginFill(0xFF0000);
125 | beatifulRect.drawRect(80, 350, 150, 150);
126 | beatifulRect.endFill();
127 |
128 | addChild(beatifulRect);
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/demos/DemoPixiSimple.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import starling.display.Sprite;
11 | import starling.events.Event;
12 |
13 | /**
14 | * Ported from
15 | * https://pixijs.io/examples/#/graphics/simple.js
16 | */
17 | public class DemoPixiSimple extends Sprite {
18 |
19 | public function DemoPixiSimple() {
20 | addEventListener(Event.ADDED_TO_STAGE, init);
21 | }
22 |
23 | private function init(event:Event):void {
24 |
25 | stage.color = 0x333333 ;
26 |
27 | const graphics:Draw = new Draw();
28 | addChild(graphics);
29 |
30 | // Rectangle
31 | graphics.beginFill(0xDE3249);
32 | graphics.drawRect(50, 50, 100, 100);
33 | graphics.endFill();
34 |
35 | // Rectangle + line style 1
36 | graphics.lineStyle(2, 0xFEEB77, 1);
37 | graphics.beginFill(0x650A5A);
38 | graphics.drawRect(200, 50, 100, 100);
39 | graphics.endFill();
40 |
41 | // Rectangle + line style 2
42 | graphics.lineStyle(10, 0xFFBD01, 1);
43 | graphics.beginFill(0xC34288);
44 | graphics.drawRect(350, 50, 100, 100);
45 | graphics.endFill();
46 |
47 | // Rectangle 2
48 | graphics.lineStyle(2, 0xFFFFFF, 1);
49 | graphics.beginFill(0xAA4F08);
50 | graphics.drawRect(530, 50, 140, 100);
51 | graphics.endFill();
52 |
53 | // Circle
54 | graphics.lineStyle(0); // draw a circle, set the lineStyle to zero so the circle doesn't have an outline
55 | graphics.beginFill(0xDE3249, 1);
56 | graphics.drawCircle(100, 250, 50);
57 | graphics.endFill();
58 |
59 | // Circle + line style 1
60 | graphics.lineStyle(2, 0xFEEB77, 1);
61 | graphics.beginFill(0x650A5A, 1);
62 | graphics.drawCircle(250, 250, 50);
63 | graphics.endFill();
64 |
65 | // Circle + line style 2
66 | graphics.lineStyle(10, 0xFFBD01, 1);
67 | graphics.beginFill(0xC34288, 1);
68 | graphics.drawCircle(400, 250, 50);
69 | graphics.endFill();
70 |
71 | // Ellipse + line style 2
72 | graphics.lineStyle(2, 0xFFFFFF, 1);
73 | graphics.beginFill(0xAA4F08, 1);
74 | graphics.drawEllipse(600, 250, 80, 50);
75 | graphics.endFill();
76 |
77 | // draw a shape
78 | graphics.beginFill(0xFF3300);
79 | graphics.lineStyle(4, 0xffd900, 1);
80 | graphics.moveTo(50, 350);
81 | graphics.lineTo(250, 350);
82 | graphics.lineTo(100, 400);
83 | graphics.lineTo(50, 350);
84 | graphics.endFill();
85 |
86 | // draw a rounded rectangle
87 | graphics.lineStyle(2, 0xFF00FF, 1);
88 | graphics.beginFill(0x650A5A, 0.25);
89 | graphics.drawRoundRect(50, 440, 100, 100, 16);
90 | graphics.endFill();
91 |
92 | // draw star
93 | graphics.lineStyle(2, 0xFFFFFF);
94 | graphics.beginFill(0x35CC5A, 1);
95 | graphics.drawStar(360, 370, 5, 50);
96 | graphics.endFill();
97 |
98 | // draw star 2
99 | graphics.lineStyle(2, 0xFFFFFF);
100 | graphics.beginFill(0xFFCC5A, 1);
101 | graphics.drawStar(280, 510, 7, 50);
102 | graphics.endFill();
103 |
104 | // draw star 3
105 | graphics.lineStyle(4, 0xFFFFFF);
106 | graphics.beginFill(0x55335A, 1);
107 | graphics.drawStar(470, 450, 4, 50);
108 | graphics.endFill();
109 |
110 | // draw polygon
111 | const path:Array = [
112 | 600, 370,
113 | 700, 460,
114 | 780, 420,
115 | 730, 570,
116 | 590, 520
117 | ];
118 |
119 | graphics.lineStyle(0);
120 | graphics.beginFill(0x3500FA, 1);
121 | graphics.drawPolygon(path);
122 | graphics.endFill();
123 |
124 | addChild(graphics);
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/demos/DemoGraphicsData.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import flash.display.Graphics;
11 | import flash.display.MovieClip;
12 | import flash.display.Shape;
13 |
14 | import starling.animation.Transitions;
15 | import starling.core.Starling;
16 | import starling.display.Sprite;
17 | import starling.events.Event;
18 | import starling.events.Touch;
19 | import starling.events.TouchEvent;
20 | import starling.events.TouchPhase;
21 |
22 | public class DemoGraphicsData extends Sprite {
23 |
24 | private var isHovering:Boolean = false;
25 | private var ninja2:Draw;
26 |
27 | public function DemoGraphicsData() {
28 | addEventListener(Event.ADDED_TO_STAGE, init);
29 | }
30 |
31 | private function init(event:Event):void {
32 | stage.color = 0xffffffff;
33 |
34 | // old school, mimic graphics data.
35 |
36 | var ninja1:Draw = makeDrawFromMovieClip(new ninja_girl_idle());
37 | ninja1.x = ninja1.width / 2;
38 | ninja1.y = ninja1.height / 2;
39 |
40 |
41 | // as we can see with these MovieClips from the SWC,
42 | // Draw's drawing commands are a little buggy.
43 |
44 | var koala:Draw = makeDrawFromMovieClip(new koala_mc());
45 | koala.scale = .5;
46 | koala.x = stage.stageWidth - koala.width / 2;
47 | koala.y = koala.height / 2;
48 |
49 |
50 | var mole:Draw = makeDrawFromMovieClip(new mole_mc());
51 | mole.x = stage.stageWidth - mole.width / 2;
52 | mole.y = stage.stageHeight - mole.height / 2;
53 |
54 | var pinocchio:Draw = makeDrawFromMovieClip(new pinocchio_mc());
55 | pinocchio.scale = .5;
56 | pinocchio.x = pinocchio.width / 2;
57 | pinocchio.y = stage.stageHeight - pinocchio.height / 2;
58 |
59 | // centered, animated, interactive ninja
60 |
61 | ninja2 = new Draw();
62 | addChild(ninja2);
63 |
64 | // copy ninja1 geometry, (no need for parsing).
65 | ninja2.copyFrom(ninja1, true);
66 | ninja2.alignPivot();
67 | ninja2.scale = 0.1;
68 |
69 | ninja2.x = stage.stageWidth / 2;
70 | ninja2.y = stage.stageHeight / 2;
71 |
72 | // scale ninja
73 | Starling.juggler.tween(ninja2, 4, {scale: 6, reverse: true, repeatCount: 0, repeatDelay: 1});
74 |
75 | ninja2.rotation = -.3;
76 | Starling.juggler.tween(ninja2, .7, {
77 | rotation: .3,
78 | reverse: true,
79 | repeatCount: 0,
80 | transition: Transitions.EASE_IN
81 | });
82 |
83 | ninja2.touchable = true;
84 | ninja2.addEventListener(TouchEvent.TOUCH, handleNinjaTouch);
85 | }
86 |
87 | private function handleNinjaTouch(e:TouchEvent):void {
88 | var t:Touch = e.getTouch(ninja2);
89 | if (t && t.phase == TouchPhase.HOVER) {
90 | // HOVER
91 | hover(true);
92 | } else if (t == null) {
93 | // OUT
94 | hover(false);
95 | }
96 | }
97 |
98 | private function hover(flag:Boolean):void {
99 | if (isHovering == flag) return;
100 | isHovering = flag;
101 | Starling.juggler.tween(ninja2, .15,
102 | {alpha: isHovering ? 0.5 : 1, transition: Transitions.EASE_OUT});
103 | }
104 |
105 | private function makeDrawFromMovieClip(mc:MovieClip):Draw {
106 | var draw:Draw = new Draw();
107 | addChild(draw);
108 | draw.drawGraphicsData(getGraphics(mc).readGraphicsData());
109 | draw.validate();
110 | draw.alignPivot();
111 | // draw.touchGroup = true ;
112 | draw.touchable = false;
113 | return draw;
114 | }
115 |
116 | private function getGraphics(mc:MovieClip):Graphics {
117 | return Shape(mc.getChildAt(0)).graphics;
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/math/shapes/Rect.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.math.shapes {
8 | import starling.utils.MathUtil;
9 | import starling.utils.StringUtil;
10 |
11 | public class Rect extends AbsShape {
12 |
13 | // TODO: add pool.
14 |
15 | public static function get empty():Rect {
16 | return new Rect();
17 | }
18 |
19 | public var x:Number;
20 | public var y:Number;
21 | public var w:Number;
22 | public var h:Number;
23 |
24 | public function Rect(x:Number = 0, y:Number = 0, w:Number = 0, h:Number = 0) {
25 | this.x = x;
26 | this.y = y;
27 | this.w = w;
28 | this.h = h;
29 | super(ShapeType.RECT);
30 | }
31 |
32 | public function get left():Number {
33 | return x;
34 | }
35 |
36 | public function get right():Number {
37 | return x + w;
38 | }
39 |
40 | public function get top():Number {
41 | return y;
42 | }
43 |
44 | public function get bottom():Number {
45 | return y + h;
46 | }
47 |
48 | public function clone():Rect {
49 | return new Rect(x, y, w, h);
50 | }
51 |
52 | public function copyFrom(rect:Rect):Rect {
53 | x = rect.x;
54 | y = rect.y;
55 | w = rect.w;
56 | h = rect.h;
57 | return this;
58 | }
59 |
60 | public function copyTo(rect:Rect):Rect {
61 | rect.x = x;
62 | rect.y = y;
63 | rect.w = w;
64 | rect.h = h;
65 | return rect;
66 | }
67 |
68 | public function contains(x:Number, y:Number):Boolean {
69 | if (w <= 0 || h <= 0) {
70 | return false;
71 | }
72 | if (x >= this.x && x < this.x + w) {
73 | if (y >= this.y && y < this.y + h) {
74 | return true;
75 | }
76 | }
77 | return false;
78 | }
79 |
80 | public function pad(x:Number, y:Number = Number.NaN):void {
81 | x = x || 0;
82 | y = y || (y !== 0 ? x : 0);
83 | this.x -= x;
84 | this.y -= y;
85 | w += x * 2;
86 | h += y * 2;
87 | }
88 |
89 | public function fit(rect:Rect):void {
90 | if (x < rect.x) {
91 | w += x;
92 | if (w < 0) w = 0;
93 | x = rect.x;
94 | }
95 | if (y < rect.y) {
96 | h += y;
97 | if (h < 0) h = 0;
98 | y = rect.y;
99 | }
100 |
101 | if (x + w > rect.x + rect.w) {
102 | w = rect.w - x;
103 | if (w < 0) {
104 | w = 0;
105 | }
106 | }
107 |
108 | if (y + h > rect.y + rect.h) {
109 | h = rect.h - y;
110 | if (h < 0) {
111 | h = 0;
112 | }
113 | }
114 | }
115 |
116 | public function ceil(resolution:Number = 1, eps:Number = .001):void {
117 | const x2:Number = Math.ceil((x + w - eps) * resolution) / resolution;
118 | const y2:Number = Math.ceil((y + h - eps) * resolution) / resolution;
119 | x = (x + eps * resolution | 0) / resolution;
120 | y = (y + eps * resolution | 0) / resolution;
121 | w = x2 - x;
122 | h = y2 - y;
123 | }
124 |
125 | public function enlarge(rect:Rect):void {
126 | const min:Function = MathUtil.min;
127 | const max:Function = MathUtil.max;
128 | const x1:Number = min(x, rect.x);
129 | const x2:Number = max(x + w, rect.x + rect.w);
130 | const y1:Number = min(y, rect.y);
131 | const y2:Number = max(y + h, rect.y + rect.h);
132 | x = x1;
133 | w = x2 - x1;
134 | y = y2;
135 | h = y2 - y1;
136 | }
137 |
138 | override public function toString():String {
139 | return StringUtil.format("[Rect x={0}, y={1}, halfW={2}, halfH={3}]", x, y, w, h);
140 | }
141 |
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/demos/DemoAnimatedArc.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import starling.animation.Tween;
11 | import starling.core.Starling;
12 | import starling.display.Sprite;
13 | import starling.events.Event;
14 | import starling.events.Touch;
15 | import starling.events.TouchEvent;
16 | import starling.events.TouchPhase;
17 |
18 | public class DemoAnimatedArc extends Sprite {
19 |
20 | public function DemoAnimatedArc() {
21 | addEventListener(Event.ADDED_TO_STAGE, init);
22 | }
23 |
24 | private function init(event:Event):void {
25 | stage.color = 0xffffff ;
26 |
27 | const g:Draw = new Draw();
28 | addChild(g);
29 |
30 | // tween these properties
31 | const arcData:Object = {
32 | x: 150,
33 | y: 150,
34 | radius: 150 / 3,
35 | angle: 10,
36 | color: 0xff0000
37 | };
38 |
39 | g.addEventListener(TouchEvent.TOUCH, handleTouch);
40 |
41 | function handleTouch(e:TouchEvent):void {
42 | // when clicked.
43 | var t:Touch = e.getTouch(g, TouchPhase.BEGAN);
44 | if (t) {
45 | arcData.color = Math.random() * 0xffffff;
46 | if (tween.progress == 0 || tween.progress == 1) {
47 | drawArc();
48 | }
49 | }
50 | }
51 |
52 | const RAD:Number = Math.PI / 180;
53 | var tween:Tween;
54 | tween = new Tween(arcData, 1, 'linear');
55 | tween.reverse = true;
56 | tween.repeatCount = 0;
57 | tween.repeatDelay = .5;
58 | tween.onUpdate = drawArc;
59 | tween.animate('angle', 361);
60 | Starling.juggler.add(tween);
61 |
62 | function drawArc():void {
63 | g.clear()
64 | .lineStyle(20, arcData.color, 1)
65 | // .lineGradientStyle(30, 0xff0000, 0xff00ff, .3 )
66 | .arc(arcData.x, arcData.y, arcData.radius, 0, arcData.angle * RAD);
67 | }
68 |
69 | pacman();
70 | }
71 |
72 | private function pacman():void {
73 | var pacman:Draw = new Draw();
74 | addChild(pacman);
75 |
76 | pacman.x = 400;
77 | pacman.y = 200;
78 |
79 | var o:Object = {p: 0.1, b: 1};
80 |
81 |
82 | Starling.juggler.tween(o, .25, {p: 1, reverse: true, repeatCount: 0, onUpdate: drawPackman});
83 |
84 | drawPackman();
85 |
86 | blink();
87 |
88 | function blink() {
89 | Starling.juggler.tween(o, .12, {
90 | delay: .1 + Math.random(),
91 | b: 0,
92 | reverse: true,
93 | onComplete:blink,
94 | repeatCount: 2
95 | });
96 | }
97 |
98 | function drawPackman():void {
99 |
100 | const maxAngle:Number = Math.PI * 2 / 4;
101 |
102 | var angle:Number = o.p * maxAngle;
103 | var endAng:Number = Math.PI * 2 - angle / 2;
104 |
105 | pacman.clear()
106 | .lineStyle(4, 0x0, .5, 1)
107 | .beginFill(0xfdea6c, 1)
108 | .moveTo(0, 0)
109 | .arc(0, 0, 80, angle / 2, endAng)
110 | .lineTo(0, 0)
111 | .endFill()
112 |
113 | .lineStyle(0)
114 |
115 | .beginFill(0xf2d159, 1)
116 | .moveTo(0, 0)
117 | .arc(0, 0, 80, angle/2,Math.PI )
118 | .lineTo(0, 0)
119 | .endFill()
120 |
121 | .beginFill(0x4d4d4d, .95)
122 | .drawEllipse(10 - Math.cos(endAng) * 10, -30 + Math.sin(endAng) * 20, 12, 12 * o.b)
123 | .endFill()
124 |
125 | // .beginFill(0x462b57, .95)
126 | .lineStyle(4, 0x0, 1)
127 | .moveTo(Math.cos(endAng)*80,Math.sin(angle/2)*80)
128 | .lineTo(200,200);
129 | }
130 |
131 |
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/demos/DemoPixiDynamic.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import starling.display.Sprite;
11 | import starling.events.Event;
12 | import starling.events.Touch;
13 | import starling.events.TouchEvent;
14 | import starling.events.TouchPhase;
15 |
16 | /**
17 | * Ported from
18 | * https://pixijs.io/examples/#/graphics/dynamic.js
19 | */
20 | public class DemoPixiDynamic extends Sprite {
21 |
22 | public function DemoPixiDynamic() {
23 | addEventListener(Event.ADDED_TO_STAGE, init);
24 | }
25 |
26 | private function init(event:Event):void {
27 |
28 | stage.color = 0x333333;
29 | stage.color = 0x0;
30 |
31 | const graphics:Draw = new Draw();
32 | addChild(graphics);
33 |
34 | // set a fill and line style
35 | graphics.beginFill(0xFF3300);
36 | graphics.lineStyle(10, 0xffd900, 1);
37 |
38 | // draw a shape
39 | graphics.moveTo(50, 50);
40 | graphics.lineTo(250, 50);
41 | graphics.lineTo(100, 100);
42 | graphics.lineTo(250, 220);
43 | graphics.lineTo(50, 220);
44 | graphics.lineTo(50, 50);
45 | graphics.endFill();
46 |
47 | // set a fill and line style again
48 | graphics.lineStyle(10, 0xFF0000, 0.8);
49 | graphics.beginFill(0xFF700B, 1);
50 |
51 | // draw a second shape
52 | graphics.moveTo(210, 300);
53 | graphics.lineTo(450, 320);
54 | graphics.lineTo(570, 350);
55 | graphics.quadraticCurveTo(600, 0, 480, 100);
56 | graphics.lineTo(330, 120);
57 | graphics.lineTo(410, 200);
58 | graphics.lineTo(210, 300);
59 | graphics.endFill();
60 |
61 | // draw a rectangle
62 | graphics.lineStyle(2, 0x0000FF, 1);
63 | graphics.drawRect(50, 250, 100, 100);
64 |
65 | // draw a circle
66 | graphics.lineStyle(0);
67 | graphics.beginFill(0xFFFF0B, 0.5);
68 | graphics.drawCircle(470, 200, 100);
69 | graphics.endFill();
70 |
71 | graphics.lineStyle(20, 0x33FF00);
72 | graphics.moveTo(30, 30);
73 | graphics.lineTo(600, 300);
74 |
75 |
76 | // let's create a moving shape
77 | var thing:Draw = new Draw();
78 | addChild(thing);
79 | thing.x = 620 / 2;
80 | thing.y = 380 / 2;
81 |
82 | // avoid frame drops on mouse move.
83 | thing.touchable = false;
84 | graphics.touchable = false;
85 |
86 | var count:Number = 0;
87 |
88 | stage.addEventListener(TouchEvent.TOUCH, handleTouch);
89 |
90 | // run the render loop
91 | addEventListener(Event.ENTER_FRAME, animate);
92 |
93 | // Just click on the stage to draw random lines
94 | function handleTouch(e:TouchEvent) {
95 | var t:Touch = e.getTouch(stage, TouchPhase.BEGAN);
96 | if (t) onClick();
97 | }
98 |
99 | function onClick() {
100 | graphics.lineStyle(Math.random() * 30, Math.random() * 0xFFFFFF, 1);
101 | graphics.moveTo(Math.random() * 620, Math.random() * 380);
102 | graphics.bezierCurveTo(Math.random() * 620, Math.random() * 380,
103 | Math.random() * 620, Math.random() * 380,
104 | Math.random() * 620, Math.random() * 380);
105 | }
106 |
107 | function animate() {
108 | count += 0.1;
109 | thing.clear();
110 | thing.lineStyle(10, 0xff0000, 1);
111 | thing.beginFill(0xffFF00, 0.5);
112 |
113 | thing.moveTo(-120 + Math.sin(count) * 20, -100 + Math.cos(count) * 20);
114 | thing.lineTo(120 + Math.cos(count) * 20, -100 + Math.sin(count) * 20);
115 | thing.lineTo(120 + Math.sin(count) * 20, 100 + Math.cos(count) * 20);
116 | thing.lineTo(-120 + Math.cos(count) * 20, 100 + Math.sin(count) * 20);
117 | // thing.lineTo(-120 + Math.sin(count) * 20, -100 + Math.cos(count) * 20);
118 | thing.closePath();
119 |
120 | thing.rotation = count * 0.1;
121 | }
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/BezierUtils.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 04/01/2019.
4 | //
5 | // ported from PixiJS
6 | // https://github.com/pixijs/pixi.js
7 | //
8 | // =================================================================================================
9 |
10 | package com.roipeker.starling.draw.utils {
11 | public class BezierUtils {
12 | public function BezierUtils() {
13 | }
14 |
15 |
16 | /**
17 | * Utilities for bezier curves
18 | * @class
19 | * @private
20 | */
21 | /**
22 | * Calculate length of bezier curve.
23 | * Analytical solution is impossible, since it involves an integral that does not integrate in general.
24 | * Therefore numerical solution is used.
25 | *
26 | * @private
27 | * @param {number} fromX - Starting point x
28 | * @param {number} fromY - Starting point y
29 | * @param {number} cpX - Control point x
30 | * @param {number} cpY - Control point y
31 | * @param {number} cpX2 - Second Control point x
32 | * @param {number} cpY2 - Second Control point y
33 | * @param {number} toX - Destination point x
34 | * @param {number} toY - Destination point y
35 | * @return {number} Length of bezier curve
36 | */
37 | public static function curveLength(fromX:Number, fromY:Number, cpX:Number, cpY:Number, cpX2:Number, cpY2:Number, toX:Number, toY:Number):Number {
38 | const n:uint = 10;
39 | var result:Number = 0.0;
40 | var t:Number = 0.0;
41 | var t2:Number = 0.0;
42 | var t3:Number = 0.0;
43 | var nt:Number = 0.0;
44 | var nt2:Number = 0.0;
45 | var nt3:Number = 0.0;
46 | var x:Number = 0.0;
47 | var y:Number = 0.0;
48 | var dx:Number = 0.0;
49 | var dy:Number = 0.0;
50 | var prevX:Number = fromX;
51 | var prevY:Number = fromY;
52 |
53 | for (var i:int = 1; i <= n; ++i) {
54 | t = i / n;
55 | t2 = t * t;
56 | t3 = t2 * t;
57 | nt = (1.0 - t);
58 | nt2 = nt * nt;
59 | nt3 = nt2 * nt;
60 |
61 | x = (nt3 * fromX) + (3.0 * nt2 * t * cpX) + (3.0 * nt * t2 * cpX2) + (t3 * toX);
62 | y = (nt3 * fromY) + (3.0 * nt2 * t * cpY) + (3 * nt * t2 * cpY2) + (t3 * toY);
63 | dx = prevX - x;
64 | dy = prevY - y;
65 | prevX = x;
66 | prevY = y;
67 | result += Math.sqrt((dx * dx) + (dy * dy));
68 | }
69 | return result;
70 | }
71 |
72 | /**
73 | * Calculate the points for a bezier curve and then draws it.
74 | *
75 | * @param {number} cpX - Control point x
76 | * @param {number} cpY - Control point y
77 | * @param {number} cpX2 - Second Control point x
78 | * @param {number} cpY2 - Second Control point y
79 | * @param {number} toX - Destination point x
80 | * @param {number} toY - Destination point y
81 | * @param {number[]} points - Path array to push points into
82 | */
83 | public static function curveTo(cpX:Number, cpY:Number, cpX2:Number, cpY2:Number, toX:Number, toY:Number, points:Array):void {
84 | var fromX:Number = points[points.length - 2];
85 | var fromY:Number = points[points.length - 1];
86 | points.length -= 2;
87 | var n:uint = GraphicCurves.segmentsCount(
88 | BezierUtils.curveLength(fromX, fromY, cpX, cpY, cpX2, cpY2, toX, toY)
89 | );
90 | // trace("curve Segment count:", n, GraphicCurves.adaptive, GraphicCurves.maxLength );
91 | var dt:Number = 0;
92 | var dt2:Number = 0;
93 | var dt3:Number = 0;
94 | var t2:Number = 0;
95 | var t3:Number = 0;
96 |
97 | points.push(fromX, fromY);
98 |
99 | for (var i:uint = 1, j:Number = 0; i <= n; ++i) {
100 | j = i / n;
101 |
102 | dt = (1 - j);
103 | dt2 = dt * dt;
104 | dt3 = dt2 * dt;
105 |
106 | t2 = j * j;
107 | t3 = t2 * j;
108 | points.push(
109 | (dt3 * fromX) + (3 * dt2 * j * cpX) + (3 * dt * t2 * cpX2) + (t3 * toX),
110 | (dt3 * fromY) + (3 * dt2 * j * cpY) + (3 * dt * t2 * cpY2) + (t3 * toY)
111 | );
112 | }
113 | }
114 |
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/ArcUtils.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 04/01/2019.
4 | //
5 | // ported from PixiJS
6 | // https://github.com/pixijs/pixi.js
7 | //
8 | // =================================================================================================
9 |
10 | package com.roipeker.starling.draw.utils {
11 | public class ArcUtils {
12 |
13 | public function ArcUtils() {
14 | }
15 |
16 | /**
17 | * The arcTo() method creates an arc/curve between two tangents on the canvas.
18 | *
19 | * "borrowed" from https://code.google.com/p/fxcanvas/ - thanks google!
20 | *
21 | * @param {number} x1 - The x-coordinate of the beginning of the arc
22 | * @param {number} y1 - The y-coordinate of the beginning of the arc
23 | * @param {number} x2 - The x-coordinate of the end of the arc
24 | * @param {number} y2 - The y-coordinate of the end of the arc
25 | * @param {number} radius - The radius of the arc
26 | * @return {object} If the arc length is valid, return center of circle, radius and other info otherwise `null`.
27 | */
28 | public static function curveTo(x1:Number, y1:Number, x2:Number, y2:Number, radius:Number, points:Array):Object {
29 | const fromX:Number = points[points.length - 2];
30 | const fromY:Number = points[points.length - 1];
31 |
32 | const a1:Number = fromY - y1;
33 | const b1:Number = fromX - x1;
34 | const a2:Number = y2 - y1;
35 | const b2:Number = x2 - x1;
36 | const mm:Number = Math.abs((a1 * b2) - (b1 * a2));
37 |
38 | if (mm < 1.0e-8 || radius === 0) {
39 | if (points[points.length - 2] !== x1 || points[points.length - 1] !== y1) {
40 | points.push(x1, y1);
41 | }
42 | return null;
43 | }
44 |
45 | const dd:Number = (a1 * a1) + (b1 * b1);
46 | const cc:Number = (a2 * a2) + (b2 * b2);
47 | const tt:Number = (a1 * a2) + (b1 * b2);
48 | const k1:Number = radius * Math.sqrt(dd) / mm;
49 | const k2:Number = radius * Math.sqrt(cc) / mm;
50 | const j1:Number = k1 * tt / dd;
51 | const j2:Number = k2 * tt / cc;
52 | const cx:Number = (k1 * b2) + (k2 * b1);
53 | const cy:Number = (k1 * a2) + (k2 * a1);
54 | const px:Number = b1 * (k2 + j1);
55 | const py:Number = a1 * (k2 + j1);
56 | const qx:Number = b2 * (k1 + j2);
57 | const qy:Number = a2 * (k1 + j2);
58 | const startAngle:Number = Math.atan2(py - cy, px - cx);
59 | const endAngle:Number = Math.atan2(qy - cy, qx - cx);
60 |
61 | return {
62 | cx: (cx + x1),
63 | cy: (cy + y1),
64 | radius: radius,
65 | startAngle: startAngle,
66 | endAngle: endAngle,
67 | anticlockwise: (b1 * a2 > b2 * a1)
68 | };
69 | }
70 |
71 | /**
72 | * The arc method creates an arc/curve (used to create circles, or parts of circles).
73 | *
74 | * @param {number} startX - Start x location of arc
75 | * @param {number} startY - Start y location of arc
76 | * @param {number} cx - The x-coordinate of the center of the circle
77 | * @param {number} cy - The y-coordinate of the center of the circle
78 | * @param {number} radius - The radius of the circle
79 | * @param {number} startAngle - The starting angle, in radians (0 is at the 3 o'clock position
80 | * of the arc's circle)
81 | * @param {number} endAngle - The ending angle, in radians
82 | * @param {boolean} anticlockwise - Specifies whether the drawing should be
83 | * counter-clockwise or clockwise. False is default, and indicates clockwise, while true
84 | * indicates counter-clockwise.
85 | * @param {number} n - Number of segments
86 | * @param {number[]} points - Collection of points to add to
87 | */
88 | public static function arc(startX:Number, startY:Number, cx:Number, cy:Number, radius:Number,
89 | startAngle:Number, endAngle:Number, anticlockwise:Boolean, points:Array):void {
90 | const sweep:Number = endAngle - startAngle;
91 | const n:uint = GraphicCurves.segmentsCount(
92 | Math.abs(sweep) * radius,
93 | Math.ceil(Math.abs(sweep) / (GraphUtils.PI2)) * 30
94 | );
95 | const theta:Number = (sweep) / (n * 2);
96 | const theta2:Number = theta * 2;
97 | const cTheta:Number = Math.cos(theta);
98 | const sTheta:Number = Math.sin(theta);
99 | const segMinus:Number = n - 1;
100 | const remainder:Number = (segMinus % 1) / segMinus;
101 | for (var i:int = 0; i <= segMinus; ++i) {
102 | var real:Number = i + (remainder * i);
103 | var angle:Number = ((theta) + startAngle + (theta2 * real));
104 | var c:Number = Math.cos(angle);
105 | var s:Number = -Math.sin(angle);
106 | points.push(
107 | (((cTheta * c) + (sTheta * s)) * radius) + cx,
108 | (((cTheta * -s) + (sTheta * c)) * radius) + cy
109 | );
110 | }
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/demos/DemoTextureFill.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-15.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 |
10 | import flash.display.BitmapData;
11 | import flash.display.Shape;
12 | import flash.geom.Matrix;
13 |
14 | import starling.core.Starling;
15 | import starling.display.Image;
16 | import starling.display.Sprite;
17 | import starling.events.Event;
18 | import starling.textures.RenderTexture;
19 | import starling.textures.Texture;
20 | import starling.utils.MathUtil;
21 |
22 | public class DemoTextureFill extends Sprite {
23 |
24 | [Embed(source="../assets/bricks_pow2.jpg")]
25 | public static const BricksSmallTextureAsset:Class;
26 |
27 | [Embed(source="../assets/roi.png")]
28 | public static const RoiTextureAsset:Class;
29 |
30 | [Embed(source="../assets/line_pattern.png")]
31 | public static const LinePattTextureAsset:Class;
32 |
33 | public function DemoTextureFill() {
34 | addEventListener(Event.ADDED_TO_STAGE, init);
35 | }
36 |
37 | private function init(event:Event):void {
38 |
39 | stage.color = 0x333333;
40 |
41 |
42 | // test1();
43 | test2();
44 | }
45 |
46 | private function test2():void {
47 | var bd:BitmapData = new RoiTextureAsset().bitmapData;
48 | // NOTE: mipmapping is required for texture repeat!
49 | var tx:Texture = Texture.fromBitmapData(bd, false);
50 |
51 | const graphics:Draw = new Draw();
52 | graphics.x = 50;
53 | graphics.y = 20;
54 | addChild(graphics);
55 |
56 | // -- Flash drawing API --
57 | var shape:Shape = new Shape();
58 | Starling.current.nativeOverlay.addChild(shape);
59 | shape.x = 400;
60 | shape.y = 50//graphics.y + tx.height + 10;
61 |
62 | var matrix:Matrix = new Matrix();
63 | var displacement:Number = 0;
64 |
65 | // addEventListener(Event.ENTER_FRAME, update);
66 |
67 | // var img:Image = new Image();
68 | // img.tileGrid
69 | // update(null);
70 |
71 | // hack texture size?
72 | var tw:int = MathUtil.getNextPowerOfTwo(tx.width);
73 | // var tw2:int = MathUtil.getNextPowerOfTwo(tx.width/2);
74 | var th:int = MathUtil.getNextPowerOfTwo(tx.height);
75 | var img:Image = new Image(tx);
76 | img.readjustSize(tw,th);
77 | trace(tx.width,tx.height, img.width, tw, th);
78 | // need to use mipmaps in RenderTexture, so make a new RenderTexture with those params in true.
79 | var rt:RenderTexture = new RenderTexture(tw,th, true, 2);
80 | rt.draw(img);
81 | tx = rt ;
82 | // addChild(img);
83 |
84 | update(null);
85 |
86 | function update(e:Event):void {
87 |
88 | displacement += .5;// 2.5;
89 |
90 | matrix.identity();
91 | matrix.scale(.2, .2);
92 | // matrix.translate(displacement, 0);
93 | // matrix.rotate(Math.PI / 3);
94 |
95 | graphics.clear()
96 | .beginTextureFill(tx, 0xffffff, .8, matrix, true)
97 | .drawRect(0, 0, tx.width/2, tx.height/2 )
98 | .endFill();
99 |
100 | // flash drawing.
101 | shape.graphics.clear();
102 | shape.graphics.beginBitmapFill(bd, matrix, true);
103 | shape.graphics.drawRect(0, 0, bd.width / 2, bd.height / 2);
104 | shape.graphics.endFill();
105 | }
106 | }
107 |
108 | private function test1():void {
109 | var bd:BitmapData = new BricksSmallTextureAsset().bitmapData;
110 |
111 | // NOTE: mipmapping is required for texture repeat!
112 | var tx:Texture = Texture.fromBitmapData(bd, true);
113 |
114 | var line_tx:Texture = Texture.fromEmbeddedAsset(LinePattTextureAsset, true);
115 |
116 | const graphics:Draw = new Draw();
117 | graphics.x = 50;
118 | graphics.y = 20;
119 | addChild(graphics);
120 |
121 | const graphicsBorder:Draw = new Draw();
122 | graphicsBorder.x = 50;
123 | graphicsBorder.y = 20;
124 | addChild(graphicsBorder);
125 |
126 | // -- Flash drawing API --
127 | var shape:Shape = new Shape();
128 | Starling.current.nativeOverlay.addChild(shape);
129 | shape.x = 50;
130 | shape.y = graphics.y + tx.height + 10;
131 |
132 | var matrix:Matrix = new Matrix();
133 | var displacement:Number = 0;
134 |
135 | addEventListener(Event.ENTER_FRAME, update);
136 |
137 | function update(e:Event):void {
138 |
139 | displacement += .5;// 2.5;
140 |
141 | matrix.identity();
142 | matrix.translate(displacement, 0);
143 |
144 |
145 | graphicsBorder.clear()
146 | .lineTextureStyle(12, line_tx, true, 0x0000ff, 1, matrix)
147 | .drawCircle(120, 120, 120)
148 | .endFill();
149 |
150 |
151 | matrix.rotate(Math.PI / 3);
152 |
153 | graphics.clear()
154 | .beginTextureFill(tx, 0xffffff, .8, matrix, true)
155 | .drawRoundRect(0, 0, tx.width, tx.height, 10)
156 | .endFill();
157 |
158 | // flash drawing.
159 | shape.graphics.clear();
160 | shape.graphics.beginBitmapFill(bd, matrix, true);
161 | shape.graphics.drawRoundRect(0, 0, tx.width, tx.height + 20, 10);
162 | shape.graphics.endFill();
163 | }
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/demos/DemoPieChart.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-16.
4 | //
5 | // =================================================================================================
6 |
7 | package demos {
8 | import com.roipeker.starling.draw.Draw;
9 | import com.roipeker.starling.draw.utils.GraphicCurves;
10 |
11 | import starling.animation.Transitions;
12 |
13 | import starling.core.Starling;
14 |
15 | import starling.display.Sprite;
16 | import starling.events.Event;
17 | import starling.events.Touch;
18 | import starling.events.TouchEvent;
19 | import starling.events.TouchPhase;
20 | import starling.text.BitmapFont;
21 | import starling.text.TextField;
22 | import starling.text.TextFormat;
23 |
24 | public class DemoPieChart extends Sprite {
25 |
26 | public function DemoPieChart() {
27 | addEventListener(Event.ADDED_TO_STAGE, init);
28 | }
29 |
30 | private function init(event:Event):void {
31 |
32 | // improves
33 | GraphicCurves.adaptive = true;
34 |
35 |
36 | var cmd:Draw = new Draw();
37 | addChild(cmd);
38 |
39 | cmd.x = stage.stageWidth / 2;
40 | cmd.y = stage.stageHeight / 2;
41 |
42 | // colors...
43 | const colors:Array = [
44 | 0x4D51A7, 0xF389D8, 0x4AC1B9, 0xAA74F3, 0xE3B490
45 | ];
46 |
47 | const colorData:Array = [];
48 | const numbers:Array = [
49 | 'one', 'two', 'three', 'four', 'five'
50 | ];
51 |
52 | const format:TextFormat = new TextFormat(BitmapFont.MINI, -1, 0x0);
53 | colors.forEach(function (color:uint, i:int, arr:Array) {
54 |
55 | var p:Number = 1 / (colors.length);
56 | var text:TextField = new TextField(60, 10, numbers[i], format);
57 | text.batchable = true;
58 | text.alignPivot();
59 | // text.filter = new DropShadowFilter(1, 0.78, 0x0, .6, 1, .6);
60 | addChild(text);
61 |
62 | colorData.push({
63 | text: text,
64 | color: color,
65 | percent: p,
66 | fromPercent: 0,
67 | selectionPercent: 0,
68 | toPercent: 0
69 | });
70 | });
71 |
72 | var _selected:int = -1;
73 | stage.addEventListener(TouchEvent.TOUCH, handleStageTouch);
74 |
75 | function handleStageTouch(e:TouchEvent) {
76 | var t:Touch = e.getTouch(stage, TouchPhase.BEGAN);
77 | if (t) {
78 | selectNext();
79 | }
80 | }
81 |
82 | function selectNext():void {
83 | if (_selected > -1) {
84 | // tweeen the object.
85 | toggleSelection(_selected, false);
86 | }
87 | ++_selected;
88 | if (_selected > colorData.length - 1) {
89 | _selected = 0;
90 | }
91 | toggleSelection(_selected, true);
92 | }
93 |
94 | function toggleSelection(idx:int, flag:Boolean):void {
95 | Starling.juggler.tween(
96 | colorData[idx], .5, {
97 | selectionPercent: flag ? 1 : 0,
98 | transition:Transitions.EASE_IN_OUT,
99 | onUpdate: renderColors
100 | }
101 | );
102 | }
103 |
104 | const tweenPercent:Object = {p: 0};
105 | const numColors:int = colorData.length;
106 | var startAngle:Number = 0;
107 |
108 | // grab one and tween.
109 | randomPercent();
110 |
111 | function randomPercent():void {
112 | var p:Number = 1;
113 | for (var i:int = 0; i < numColors; i++) {
114 | var reducer:Number = .1 + Math.random() * .4;
115 | var p2:Number = p * reducer;
116 | if (i == numColors - 1) {
117 | p2 = p;
118 | } else {
119 | p -= p2;
120 | }
121 | colorData[i].fromPercent = colorData[i].percent;
122 | colorData[i].toPercent = p2;
123 | }
124 |
125 | tweenPercent.p = 0;
126 | Starling.juggler.tween(tweenPercent, .5,
127 | {
128 | delay: .4,
129 | p: 1,
130 | onUpdate: transitionPercents,
131 | onComplete: randomPercent,
132 | transition: Transitions.EASE_OUT
133 | });
134 | }
135 |
136 | function transitionPercents():void {
137 | for (var i:int = 0; i < numColors; i++) {
138 | const vo:Object = colorData[i];
139 | vo.percent = vo.fromPercent + (vo.toPercent - vo.fromPercent) * tweenPercent.p;
140 |
141 | }
142 | // cmd.rotation += .001;
143 | startAngle += .001;
144 | renderColors();
145 | }
146 |
147 | function renderColors() {
148 | const maxAngle:Number = Math.PI * 2;
149 | const radius:Number = 100;
150 |
151 | cmd.clear();
152 |
153 | for (var i:int = 0; i < colorData.length; i++) {
154 | const vo:Object = colorData[i];
155 | var angPercent:Number = vo.percent * maxAngle;
156 | var endAngle:Number = startAngle + angPercent;
157 |
158 | var selection:Number = vo.selectionPercent;
159 | var thickness:Number = 30 + selection * 30 ;
160 |
161 | cmd.lineStyle(thickness, vo.color, 1, selection/2);
162 | cmd.arc(0, 0, radius, startAngle, endAngle);
163 |
164 | var tf:TextField = vo.text;
165 | var textAngle:Number = startAngle + angPercent / 2;
166 | tf.x = cmd.x + Math.cos(textAngle) * (radius + 30 / 2 );
167 | tf.y = cmd.y + Math.sin(textAngle) * (radius + 30 / 2 );
168 | tf.rotation = textAngle + Math.PI / 2;
169 | tf.text = numbers[i] + ' ' + Math.round(vo.percent * 100) + '%';
170 |
171 | startAngle += angPercent;
172 | }
173 | }
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/builders/RoundRectBuilder.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.builders {
8 | import com.roipeker.starling.draw.GraphData;
9 | import com.roipeker.starling.draw.GraphGeom;
10 | import com.roipeker.starling.draw.math.shapes.RoundRect;
11 | import com.roipeker.starling.draw.utils.earcut.Earcut;
12 |
13 | import starling.utils.MathUtil;
14 |
15 | public class RoundRectBuilder extends AbsShapeBuilder {
16 |
17 | // number of segments per corner, is NOT a divider, but the actual segment number.
18 | public static var cornerNumSegments:uint = 20;
19 |
20 | private static function quadraticBezierCurve(fromX:Number, fromY:Number, cpX:Number, cpY:Number, toX:Number, toY:Number, out:Array = null) {
21 | if (!out) out = [];
22 | var n:int = cornerNumSegments;
23 | var xa:Number = 0;
24 | var ya:Number = 0;
25 | var xb:Number = 0;
26 | var yb:Number = 0;
27 | var x:Number = 0;
28 | var y:Number = 0;
29 |
30 |
31 | for (var i:int = 0, j:Number = 0; i <= n; ++i) {
32 |
33 |
34 | j = i / n;
35 | xa = getPt(fromX, cpX, j);
36 | ya = getPt(fromY, cpY, j);
37 | xb = getPt(cpX, toX, j);
38 | yb = getPt(cpY, toY, j);
39 |
40 | x = getPt(xa, xb, j);
41 | y = getPt(ya, yb, j);
42 |
43 | /* if ( constrain ){
44 | if ( x < constrain.x || y < constrain.y
45 | || x > constrain.right || y > constrain.bottom ){
46 | violated = true ;
47 | break ;
48 | trace('vuiolation')
49 | }
50 | }*/
51 | // out.push(x, y);
52 | out[out.length] = x;
53 | out[out.length] = y;
54 | }
55 | return out;
56 | }
57 |
58 | [Inline]
59 | private static function getPt(n1:Number, n2:Number, perc:Number):Number {
60 | return n1 + ((n2 - n1) * perc);
61 | }
62 |
63 | public function RoundRectBuilder() {
64 | super();
65 | }
66 |
67 | override public function build(shapeData:GraphData):void {
68 | const rrect:RoundRect = shapeData.shape as RoundRect;
69 | const points:Array = shapeData.points;
70 |
71 | const x:Number = rrect.x,
72 | y:Number = rrect.y,
73 | w:Number = rrect.w,
74 | h:Number = rrect.h;
75 |
76 | var tlr:Number = rrect.tlr,
77 | trr:Number = rrect.trr,
78 | blr:Number = rrect.blr,
79 | brr:Number = rrect.brr;
80 |
81 | // constrains the radius to the maximun size.
82 | const MIN_SIZE:Number = 0.0000000001;
83 |
84 | // TODO: fix MAX_SIZE and clamp(), when using complex round rect, corners can be larger than h/2 || w/2.
85 | const MAX_SIZE:Number = MathUtil.min(w, h) / 2;
86 |
87 | tlr = MathUtil.clamp(tlr, MIN_SIZE, MAX_SIZE);
88 | trr = MathUtil.clamp(trr, MIN_SIZE, MAX_SIZE);
89 | blr = MathUtil.clamp(blr, MIN_SIZE, MAX_SIZE);
90 | brr = MathUtil.clamp(brr, MIN_SIZE, MAX_SIZE);
91 |
92 | points.length = 0;
93 |
94 | var allSame:Boolean = tlr == trr && tlr == blr && tlr == brr;
95 |
96 | // special case for "pill" shapes, the quad curve doesn't make a proper half circle.
97 | // although Circle or Bezier curves should be an optional setting maybe.
98 | if (allSame && tlr == MAX_SIZE) {
99 | const len:int = cornerNumSegments;
100 | var hpi:Number = Math.PI / 2;
101 | const segment:Number = hpi / len;
102 | var cx:Number = x + tlr;
103 | var cy:Number = y + tlr;
104 | var offsetAngle:Number = Math.PI;
105 |
106 | // top left
107 | addSegments(points, len, segment, cx, cy, offsetAngle, tlr);
108 |
109 | cx = x + w - tlr;
110 | offsetAngle += hpi;
111 | // top right
112 | addSegments(points, len, segment, cx, cy, offsetAngle, tlr);
113 |
114 | offsetAngle += hpi;
115 | cy = y + h - tlr;
116 | // bottom right
117 | addSegments(points, len, segment, cx, cy, offsetAngle, tlr);
118 |
119 | offsetAngle += hpi;
120 | cx = x + tlr;
121 | // bottom left
122 | addSegments(points, len, segment, cx, cy, offsetAngle, tlr);
123 |
124 | points.push(points[0], points[1]);
125 |
126 | } else {
127 | points.push(x, y + tlr);
128 | quadraticBezierCurve(x, y + h - blr, x, y + h, x + blr, y + h, points);
129 | quadraticBezierCurve(x + w - brr, y + h, x + w, y + h, x + w, y + h - brr, points);
130 | quadraticBezierCurve(x + w, y + trr, x + w, y, x + w - trr, y, points);
131 | quadraticBezierCurve(x + tlr, y, x, y, x, y + tlr + MIN_SIZE, points);
132 | }
133 | }
134 |
135 | private function addSegments(points:Array, len:int, segment:Number, cx:Number, cy:Number, offsetAngle:Number, radius:Number):void {
136 | var angle:Number;
137 | for (var i:int = 0; i < len; i++) {
138 | angle = segment * i + offsetAngle;
139 | points.push(
140 | cx + (Math.cos(angle) * radius),
141 | cy + (Math.sin(angle) * radius)
142 | );
143 | }
144 | }
145 |
146 | override public function triangulate(shapeData:GraphData, geometry:GraphGeom):void {
147 | const points:Array = shapeData.points;
148 | const indices:Array = geometry.indices;
149 | const verts:Array = geometry.points;
150 | const vertPos:int = verts.length >> 1;
151 |
152 | // reduce the triangulation.
153 | const triangles:Array = Earcut.earcut( points );
154 |
155 | // const triangles:Array = points;
156 | var ilen:int;
157 |
158 | ilen = triangles.length;
159 | for (var i:int = 0; i < ilen; i += 3) {
160 | indices[indices.length] = vertPos + triangles[i];
161 | indices[indices.length] = vertPos + triangles[i + 1];
162 | indices[indices.length] = vertPos + triangles[i + 2];
163 | }
164 |
165 | /*ilen = points.length;
166 | for (i = 0; i < ilen; i++) {
167 | // verts.push(points[i], points[++i]);
168 | verts[verts.length] = points[i];
169 | verts[verts.length] = points[++i];
170 | }*/
171 | ilen = points.length;
172 | for (i = 0; i < ilen; i++) {
173 | verts[verts.length] = points[i];
174 | }
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/svg/SVGColor.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 06/01/2019.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.utils.svg {
8 | public class SVGColor {
9 |
10 | private static var names:Object = {
11 | aliceblue: "f0f8ff",
12 | antiquewhite: "faebd7",
13 | aqua: "0ff",
14 | aquamarine: "7fffd4",
15 | azure: "f0ffff",
16 | beige: "f5f5dc",
17 | bisque: "ffe4c4",
18 | black: "000",
19 | blanchedalmond: "ffebcd",
20 | blue: "00f",
21 | blueviolet: "8a2be2",
22 | brown: "a52a2a",
23 | burlywood: "deb887",
24 | burntsienna: "ea7e5d",
25 | cadetblue: "5f9ea0",
26 | chartreuse: "7fff00",
27 | chocolate: "d2691e",
28 | coral: "ff7f50",
29 | cornflowerblue: "6495ed",
30 | cornsilk: "fff8dc",
31 | crimson: "dc143c",
32 | cyan: "0ff",
33 | darkblue: "00008b",
34 | darkcyan: "008b8b",
35 | darkgoldenrod: "b8860b",
36 | darkgray: "a9a9a9",
37 | darkgreen: "006400",
38 | darkgrey: "a9a9a9",
39 | darkkhaki: "bdb76b",
40 | darkmagenta: "8b008b",
41 | darkolivegreen: "556b2f",
42 | darkorange: "ff8c00",
43 | darkorchid: "9932cc",
44 | darkred: "8b0000",
45 | darksalmon: "e9967a",
46 | darkseagreen: "8fbc8f",
47 | darkslateblue: "483d8b",
48 | darkslategray: "2f4f4f",
49 | darkslategrey: "2f4f4f",
50 | darkturquoise: "00ced1",
51 | darkviolet: "9400d3",
52 | deeppink: "ff1493",
53 | deepskyblue: "00bfff",
54 | dimgray: "696969",
55 | dimgrey: "696969",
56 | dodgerblue: "1e90ff",
57 | firebrick: "b22222",
58 | floralwhite: "fffaf0",
59 | forestgreen: "228b22",
60 | fuchsia: "f0f",
61 | gainsboro: "dcdcdc",
62 | ghostwhite: "f8f8ff",
63 | gold: "ffd700",
64 | goldenrod: "daa520",
65 | gray: "808080",
66 | green: "008000",
67 | greenyellow: "adff2f",
68 | grey: "808080",
69 | honeydew: "f0fff0",
70 | hotpink: "ff69b4",
71 | indianred: "cd5c5c",
72 | indigo: "4b0082",
73 | ivory: "fffff0",
74 | khaki: "f0e68c",
75 | lavender: "e6e6fa",
76 | lavenderblush: "fff0f5",
77 | lawngreen: "7cfc00",
78 | lemonchiffon: "fffacd",
79 | lightblue: "add8e6",
80 | lightcoral: "f08080",
81 | lightcyan: "e0ffff",
82 | lightgoldenrodyellow: "fafad2",
83 | lightgray: "d3d3d3",
84 | lightgreen: "90ee90",
85 | lightgrey: "d3d3d3",
86 | lightpink: "ffb6c1",
87 | lightsalmon: "ffa07a",
88 | lightseagreen: "20b2aa",
89 | lightskyblue: "87cefa",
90 | lightslategray: "789",
91 | lightslategrey: "789",
92 | lightsteelblue: "b0c4de",
93 | lightyellow: "ffffe0",
94 | lime: "0f0",
95 | limegreen: "32cd32",
96 | linen: "faf0e6",
97 | magenta: "f0f",
98 | maroon: "800000",
99 | mediumaquamarine: "66cdaa",
100 | mediumblue: "0000cd",
101 | mediumorchid: "ba55d3",
102 | mediumpurple: "9370db",
103 | mediumseagreen: "3cb371",
104 | mediumslateblue: "7b68ee",
105 | mediumspringgreen: "00fa9a",
106 | mediumturquoise: "48d1cc",
107 | mediumvioletred: "c71585",
108 | midnightblue: "191970",
109 | mintcream: "f5fffa",
110 | mistyrose: "ffe4e1",
111 | moccasin: "ffe4b5",
112 | navajowhite: "ffdead",
113 | navy: "000080",
114 | oldlace: "fdf5e6",
115 | olive: "808000",
116 | olivedrab: "6b8e23",
117 | orange: "ffa500",
118 | orangered: "ff4500",
119 | orchid: "da70d6",
120 | palegoldenrod: "eee8aa",
121 | palegreen: "98fb98",
122 | paleturquoise: "afeeee",
123 | palevioletred: "db7093",
124 | papayawhip: "ffefd5",
125 | peachpuff: "ffdab9",
126 | peru: "cd853f",
127 | pink: "ffc0cb",
128 | plum: "dda0dd",
129 | powderblue: "b0e0e6",
130 | purple: "800080",
131 | rebeccapurple: "663399",
132 | red: "f00",
133 | rosybrown: "bc8f8f",
134 | royalblue: "4169e1",
135 | saddlebrown: "8b4513",
136 | salmon: "fa8072",
137 | sandybrown: "f4a460",
138 | seagreen: "2e8b57",
139 | seashell: "fff5ee",
140 | sienna: "a0522d",
141 | silver: "c0c0c0",
142 | skyblue: "87ceeb",
143 | slateblue: "6a5acd",
144 | slategray: "708090",
145 | slategrey: "708090",
146 | snow: "fffafa",
147 | springgreen: "00ff7f",
148 | steelblue: "4682b4",
149 | tan: "d2b48c",
150 | teal: "008080",
151 | thistle: "d8bfd8",
152 | tomato: "ff6347",
153 | turquoise: "40e0d0",
154 | violet: "ee82ee",
155 | wheat: "f5deb3",
156 | white: "fff",
157 | whitesmoke: "f5f5f5",
158 | yellow: "ff0",
159 | yellowgreen: "9acd32"
160 | };
161 |
162 | public static function resolveHex(strColor:String):int {
163 | if (!strColor) return -1;
164 | strColor = strColor.toLowerCase();
165 |
166 | if (strColor.charAt(0) == '#') {
167 | strColor = strColor.substr(1);
168 | strColor = resolveSimplified(strColor);
169 | return parseInt(strColor, 16);
170 | } else {
171 | var c:int = hexByName(strColor);
172 | // strColor.replace(/([a-f0-9])/ig, '$1$1')
173 | if (c == -1) {
174 | trace('color not supported!');
175 | return 0x00ff00;
176 | }
177 | return c;
178 | }
179 | }
180 |
181 | private static function resolveSimplified(strColor:String):String {
182 | if (strColor.length != 3) return strColor;
183 | return strColor.replace(/([a-f0-9])/ig, '$1$1');
184 | }
185 |
186 | // Make it easy to access colors via `hexNames[hex]`
187 | private static var hexNames:Object = flip(names);
188 |
189 | public static function nameByHex(hex:uint):String {
190 | var key:String = hex.toString(16);
191 | if (hexNames[key]) return hexNames[key];
192 | return '';
193 | }
194 |
195 | public static function hexByName(name:String):int {
196 | var res:String = names[name];
197 | if (res) {
198 | return parseInt('0x' + resolveSimplified(res));
199 | } else {
200 | return -1;
201 | }
202 | }
203 |
204 | // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
205 | private static function flip(o:Object):Object {
206 | var flipped:Object = {};
207 | for (var i:String in o) {
208 | if (o.hasOwnProperty(i)) {
209 | flipped[o[i]] = i;
210 | }
211 | }
212 | return flipped;
213 | }
214 |
215 | public function SVGColor() {
216 | }
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/svg/DPathParser.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 05/01/2019.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.utils.svg {
8 | public class DPathParser {
9 |
10 | private var regExpMap:Object = {
11 | command: /\s*([achlmqstvzo])/gi,
12 | number: /\s*([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/gi,
13 | comma: /\s*(?:(,)|\s)/g,
14 | flag: /\s*([01])/g
15 | };
16 |
17 | private var matchers:Object = {};
18 | private var commands:Array;
19 | private var index:int;
20 | private var data:String;
21 | private var relative:Boolean;
22 | private var curr_cmd:String;
23 |
24 | private static const FLAG:String = 'flag';
25 | private static const COMMA:String = 'comma';
26 | private static const COMMAND:String = 'command';
27 |
28 | private static const NUMBER:String = 'number';
29 | private static const COORD_PAIR:String = 'coord_pair';
30 | private static const ARC_DEF:String = 'arc_definition';
31 |
32 |
33 | public function DPathParser() {
34 | init();
35 | }
36 |
37 | private function init():void {
38 | /* var res:* = arrayReduce([0, 1, 2, 3, 4], function (valorAnterior:Number, valorActual:Number, indice:int, vector:Array):Number {
39 | return valorAnterior + valorActual;
40 | }, 10);
41 | trace("REsult:", res);
42 |
43 | return;*/
44 |
45 | matchers[NUMBER] = function (must:Boolean):Number {
46 | var a:* = get(NUMBER, must);
47 | return Number(a);
48 | };
49 |
50 | matchers[COORD_PAIR] = function (must:Boolean):Object {
51 | var xs:String = get(NUMBER, must);
52 | if (!xs && !must) return null;
53 | get(COMMA, false);
54 | var ys:String = get(NUMBER, true);
55 | return {x: Number(xs), y: Number(ys)};
56 | };
57 |
58 | matchers[ARC_DEF] = function (must:Boolean):Object {
59 | var radii:Object = matchers[COORD_PAIR](must);
60 | if (!radii && !must) return null;
61 | get(COMMA);
62 | var rot:Number = Number(get(NUMBER, true));
63 | get(COMMA, true);
64 | var large:Boolean = Boolean(Number(get(FLAG, true)));
65 | get(COMMA);
66 | var clockwise:Boolean = Boolean(Number(get(FLAG, true)));
67 | get(COMMA);
68 | var end:Object = matchers[COORD_PAIR](true);
69 | return {
70 | radii: radii,
71 | rotation: rot, large: large, clockwise: clockwise, end: end
72 | };
73 | }
74 | }
75 |
76 | public function reset():void {
77 | curr_cmd = null;
78 | data = null;
79 | commands = null;
80 | relative = false;
81 | index = 0;
82 | }
83 |
84 | private function get(key:String, must:Boolean = false):String {
85 | var re:RegExp = regExpMap[key];
86 | re.lastIndex = index;
87 | var res:Object = re.exec(data);
88 | if (!res || res.index != index) {
89 | if (!must) return null;
90 | throw new Error("Expected " + key + " at position " + index);
91 | }
92 | index = re.lastIndex;
93 | return res[1];
94 | }
95 |
96 |
97 | public function parse(d:String):Array {
98 | reset();
99 | data = d;
100 | commands = [];
101 | relative = false;
102 |
103 | var len:int = d.length;
104 | while (index < len) {
105 | curr_cmd = get(COMMAND, false);
106 | if (!curr_cmd) {
107 | ++index;
108 | continue;
109 | }
110 | var upcmd:String = curr_cmd.toUpperCase();
111 | relative = curr_cmd != upcmd;
112 | var seq:Array;
113 | if (upcmd == "M") {
114 | seq = getSeq(COORD_PAIR).map(function (coords:Object, i:int, arr:Array) {
115 | if (i == 1) curr_cmd = relative ? 'l' : 'L';
116 | return makeCommand({end: coords});
117 | });
118 | } else if (upcmd == "L" || upcmd == "T") {
119 | seq = getSeq(COORD_PAIR).map(function (coords:Object, i:int, arr:Array) {
120 | return makeCommand({end: coords});
121 | });
122 | } else if (upcmd == "C") {
123 | seq = getSeq(COORD_PAIR);
124 | if (seq.length % 3) {
125 | throw new Error("Expected coordinate pair triplet at position " + index);
126 | }
127 | seq = arrayReduce(seq, function (prev:Object, coords:Object, i:int, arr:Array):* {
128 | var rest:int = i % 3;
129 | if (!rest) {
130 | prev.push(makeCommand({cp1: coords}));
131 | } else {
132 | var last:Object = prev[prev.length - 1];
133 | if (rest === 1) {
134 | last.cp2 = coords;
135 | } else {
136 | last.end = coords;
137 | }
138 | }
139 | return prev;
140 | }, []) as Array;
141 |
142 | } else if (upcmd == "Q" || upcmd == "S") {
143 |
144 | seq = getSeq(COORD_PAIR);
145 | if (seq.length & 1) {
146 | throw new Error("Expected coordinate pair couple at position " + index);
147 | }
148 | seq = arrayReduce(seq, function (prev:Object, coords:Object, i:int, arr:Array):Object {
149 | var odd:int = i & 1;
150 | if (!odd) {
151 | prev.push(makeCommand({cp: coords}));
152 | } else {
153 | var last:Object = prev[prev.length - 1];
154 | last.end = coords;
155 | }
156 | return prev;
157 | }, []) as Array;
158 |
159 | } else if (upcmd == "H" || upcmd == "V") {
160 | // trace("adding h/v!")
161 | seq = getSeq(NUMBER).map(function (value:Object, i:int, arr:Array) {
162 | // trace("WTF ? ! >> ", value)
163 | return makeCommand({value: value});
164 | });
165 | } else if (upcmd == "A") {
166 | seq = getSeq(ARC_DEF).map(function (value:Object, i:int, arr:Array) {
167 | return makeCommand(value);
168 | });
169 | } else if (upcmd == "Z") {
170 | seq = [{code: "Z"}]
171 | } else if (upcmd == "O") {
172 | seq = [{code: "O"}]
173 | }
174 | // trace("Rel", rel, curr_cmd);
175 | // ++index;
176 | commands.push.apply(commands, seq);
177 | }
178 | return commands;
179 | }
180 |
181 | private function makeCommand(obj:Object):Object {
182 | obj.code = curr_cmd;
183 | obj.relative = relative;
184 | return obj;
185 | }
186 |
187 | private function getSeq(id:String):Array {
188 | var s:Array = [];
189 | var matched:Object;
190 | var must:Boolean = true;
191 | var valid:Boolean = true;
192 | while (valid) {
193 | matched = matchers[id](must);
194 | if (matched) {
195 | s.push(matched);
196 | must = Boolean(get("comma", false));
197 | valid = true;
198 | } else {
199 | valid = false;
200 | }
201 | }
202 | return s;
203 | }
204 |
205 | private static function arrayReduce(arr:Array, callback:Function, initValue:* = null):* {
206 | var len:int = arr.length;
207 | var prevValue:*;
208 | if (initValue) {
209 | var i:int = 0;
210 | prevValue = initValue;
211 | } else {
212 | i = 1;
213 | prevValue = arr[0];
214 | }
215 | for (i; i < len; i++) {
216 | var nextValue:* = arr[i];
217 | if (nextValue == null) continue;
218 | prevValue = callback(prevValue, nextValue, i, arr);
219 | }
220 | return prevValue;
221 | }
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/app.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.roipeker.StarlingDraw
4 | Starling Draw Lib
5 | Starling Draw Lib
6 | 1.0.0
7 | roipeker 2019
8 |
9 |
10 | SWF file name is set automatically at compile time
11 | Draw Demo
12 | standard
13 | true
14 | portrait
15 | true
16 | true
17 | direct
18 | true
19 | high
20 |
21 |
22 |
23 |
55 |
56 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
114 |
115 |
116 |
117 |
118 |
119 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
150 |
151 | ]]>
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/GraphGeom.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-11.
4 | //
5 | // ported from PixiJS
6 | // https://github.com/pixijs/pixi.js
7 | //
8 | // =================================================================================================
9 |
10 | package com.roipeker.starling.draw {
11 | import com.roipeker.starling.draw.builders.AbsShapeBuilder;
12 | import com.roipeker.starling.draw.math.shapes.AbsShape;
13 | import com.roipeker.starling.draw.math.shapes.ShapeType;
14 | import com.roipeker.starling.draw.styles.FillStyle;
15 | import com.roipeker.starling.draw.styles.LineStyle;
16 | import com.roipeker.starling.draw.utils.GraphUtils;
17 |
18 | import flash.geom.Matrix;
19 |
20 | import starling.textures.Texture;
21 |
22 | public class GraphGeom {
23 |
24 | private var _shapesData:Array;
25 | private var _dirty:int = 0;
26 | private var _cacheDirty:int = -1;
27 |
28 | public var points:Array;
29 | public var indices:Array;
30 | public var colors:Array;
31 | public var gradients:Array;
32 |
33 | // todo: remove this "globals" props when supporting batching or/and multiple Meshes rendering.
34 | public var texture:Texture;
35 | public var textureRepeat:Boolean;
36 |
37 | public var uvs:Array;
38 |
39 | public function GraphGeom() {
40 | points = [];
41 | indices = [];
42 | colors = [];
43 | gradients = [];
44 | uvs = [];
45 | _shapesData = [];
46 | }
47 |
48 | public function drawHole(shape:AbsShape, matrix:Matrix):GraphData {
49 | if (!_shapesData.length) return null;
50 | const shapeData:GraphData = new GraphData(shape, null, null, matrix);
51 | const lastShapeData:GraphData = _shapesData[int(_shapesData.length - 1)];
52 | lastShapeData.holes.push(shapeData);
53 | _dirty++;
54 | return shapeData;
55 | }
56 |
57 | public function drawShape(shape:AbsShape, fillStyle:FillStyle, lineStyle:LineStyle, matrix:Matrix):GraphGeom {
58 | const shapeData:GraphData = new GraphData(shape, fillStyle, lineStyle, matrix);
59 | _shapesData[_shapesData.length] = shapeData;
60 | _dirty++;
61 | return this;
62 | }
63 |
64 | public function calculate():void {
65 | if (_dirty == _cacheDirty) return;
66 |
67 | _cacheDirty = _dirty;
68 | const shapes:Array = _shapesData;
69 | var currentColor:uint = 0xFFFFFF;
70 | var currentAlpha:Number = 1;
71 |
72 | texture = null;
73 | textureRepeat = false;
74 |
75 | this.uvs.length = 0;
76 | this.points.length = 0;
77 | this.indices.length = 0;
78 | this.colors.length = 0;
79 | this.gradients.length = 0;
80 |
81 | for (var i:int = 0, ilen:int = shapes.length; i < ilen; i++) {
82 | const shapeData:GraphData = _shapesData[i];
83 | const command:AbsShapeBuilder = AbsShapeBuilder.get(shapeData.type);
84 | command.build(shapeData);
85 | if (shapeData.matrix) {
86 | transformPoints(shapeData.points, shapeData.matrix);
87 | }
88 |
89 | var fillStyle:FillStyle = shapeData.fillStyle;
90 | var lineStyle:FillStyle = shapeData.lineStyle;
91 |
92 | // trick to toggle fill/line.
93 | for (var j:int = 0; j < 2; j++) {
94 | var style:FillStyle = j == 0 ? fillStyle : lineStyle;
95 | if (!style.visible) continue;
96 | if (style.color != currentColor || style.alpha != currentAlpha) {
97 | currentColor = style.color;
98 | currentAlpha = style.alpha;
99 | }
100 | const start:int = points.length >> 1;
101 | if (j == 0) {
102 | if (shapeData.holes.length) {
103 | processHoles(shapeData.holes);
104 | AbsShapeBuilder.get(ShapeType.POLY).triangulate(shapeData, this);
105 | } else {
106 | command.triangulate(shapeData, this);
107 | }
108 | } else {
109 | GraphUtils.resolveLine(shapeData, this);
110 | }
111 | const size:int = (points.length >> 1) - start;
112 |
113 | if (style.gradient && style.gradient.visible) {
114 | gradients.push(
115 | start,
116 | start + size,
117 | style.gradient.color1, style.gradient.color2,
118 | style.gradient.angle,
119 | style.gradient.alpha1, style.gradient.alpha2
120 | );
121 | } else {
122 | colors.push(
123 | start,
124 | size,
125 | currentColor,
126 | currentAlpha
127 | );
128 | }
129 | texture = style.texture;
130 | textureRepeat = style.textureRepeat;
131 | if (style.texture) {
132 | addUvs(points, uvs, style.texture, start, size, style.matrix);
133 | }
134 | }
135 | }
136 | }
137 |
138 | private function addUvs(verts:Array, uvs:Array, texture:Texture, start:int, size:int, matrix:Matrix):void {
139 | var index:int = 0;
140 | while (index < size) {
141 | var x:Number = verts[(start + index) * 2];
142 | var y:Number = verts[((start + index) * 2) + 1];
143 | if (matrix) {
144 | const nx:Number = (matrix.a * x) + (matrix.c * y) + matrix.tx;
145 | y = (matrix.b * x) + (matrix.d * y) + matrix.ty;
146 | x = nx;
147 | }
148 | index++;
149 | uvs.push(x / texture.width, y / texture.height);
150 | }
151 |
152 | // TODO: adjust UVS for textureAtlases.
153 | // check if it works.
154 | trace(texture.frame);
155 | if ( texture.frame && ( texture.frame.width < texture.width || texture.frame.height < texture.height )) {
156 | adjustUvs(uvs, texture, uvs.length, size);
157 | }
158 | }
159 |
160 | private function adjustUvs(uvs:Array, texture:Texture, start:int, size:int) {
161 | const eps:Number = 1e-6;
162 | const finish:int = start + (size * 2);
163 | const frame:flash.geom.Rectangle = texture.frame;
164 | const scaleX:Number = frame.width / texture.width;
165 | const scaleY:Number = frame.height / texture.height;
166 | var offsetX:Number = frame.x / frame.width;
167 | var offsetY:Number = frame.y / frame.height;
168 | var minX:int = Math.floor(uvs[start] + eps);
169 | var minY:int = Math.floor(uvs[start + 1] + eps);
170 | for (var i:int = start + 2; i < finish; i += 2) {
171 | minX = Math.min(minX, Math.floor(uvs[i] + eps));
172 | minY = Math.min(minY, Math.floor(uvs[i + 1] + eps));
173 | }
174 | offsetX -= minX;
175 | offsetY -= minY;
176 | for (i = start; i < finish; i += 2) {
177 | uvs[i] = (uvs[i] + offsetX) * scaleX;
178 | uvs[i + 1] = (uvs[i + 1] + offsetY) * scaleY;
179 | }
180 | }
181 |
182 | private function transformPoints(points:Array, matrix:Matrix):void {
183 | /*var x:Number, y:Number, idx:int;
184 | for (var i:int = 0, len:uint = points.length >> 1; i < len; i++) {
185 | idx = i * 2;
186 | x = points[idx];
187 | y = points[idx + 1];
188 | points[idx] = (matrix.a * x) + (matrix.c * y) + matrix.tx;
189 | points[idx + 1] = (matrix.b * x) + (matrix.d * y) + matrix.ty;
190 | /!*points[idx] += matrix.tx;
191 | points[idx + 1] += matrix.ty;*!/
192 | }*/
193 |
194 | const len:int = points.length / 2;
195 | for (var i:int = 0; i < len; i++) {
196 | const x:Number = points[(i * 2)];
197 | const y:Number = points[(i * 2) + 1];
198 |
199 | points[(i * 2)] = (matrix.a * x) + (matrix.c * y) + matrix.tx;
200 | points[(i * 2) + 1] = (matrix.b * x) + (matrix.d * y) + matrix.ty;
201 | }
202 | }
203 |
204 | private function processHoles(holes:Array):void {
205 | for (var i:int = 0, ilen:int = holes.length; i < ilen; i++) {
206 | var command:AbsShapeBuilder = AbsShapeBuilder.get(holes[i].type);
207 | command.build(holes[i]);
208 | if (holes[i].matrix) {
209 | transformPoints(holes[i].points, holes[i].matrix);
210 | }
211 | }
212 | }
213 |
214 | public function clear():GraphGeom {
215 | if (_shapesData.length > 0) {
216 | // TODO: return shapesData objects to pool.
217 | _dirty++;
218 | _shapesData.length = 0;
219 | uvs.length = 0;
220 | texture = null;
221 | points.length = 0;
222 | indices.length = 0;
223 | colors.length = 0;
224 | gradients.length = 0;
225 | }
226 | return this;
227 | }
228 |
229 | public function clone():GraphGeom {
230 | var output:GraphGeom = new GraphGeom();
231 | output._shapesData = _shapesData.concat();
232 | output.points = points.concat();
233 | output.indices = indices.concat();
234 | output.colors = colors.concat();
235 | output.gradients = gradients.concat();
236 | return output;
237 | }
238 |
239 | public function dispose():void {
240 | clear();
241 | _shapesData = null;
242 | points = null;
243 | uvs = null;
244 | indices = null;
245 | colors = null;
246 | gradients = null;
247 | }
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/GraphUtils.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 03/01/2019.
4 | //
5 | // ported from PixiJS
6 | // https://github.com/pixijs/pixi.js
7 | //
8 | // =================================================================================================
9 |
10 | package com.roipeker.starling.draw.utils {
11 | import com.roipeker.starling.draw.GraphData;
12 | import com.roipeker.starling.draw.GraphGeom;
13 | import com.roipeker.starling.draw.styles.LineStyle;
14 | import com.roipeker.starling.draw.utils.lines.LineStrokePixi;
15 | import com.roipeker.starling.draw.utils.lines.ComplexLineStroke;
16 |
17 | import flash.display.CapsStyle;
18 | import flash.display.JointStyle;
19 | import flash.geom.Point;
20 |
21 | public class GraphUtils {
22 |
23 | public static const PI2:Number = Math.PI * 2;
24 |
25 | public function GraphUtils() {
26 | }
27 |
28 | // buffer points.
29 | private static const POINT1:Point = new Point();
30 | private static const POINT2:Point = new Point();
31 |
32 | public static function resolveLine(graphData:GraphData, graphGeom:GraphGeom):void {
33 | if (graphData.points.length === 0) {
34 | return;
35 | }
36 | // if using complex line... build with another approach (way more triangles though).
37 | if ( graphData.lineStyle.joint != JointStyle.MITER || graphData.lineStyle.caps != CapsStyle.SQUARE ) {
38 |
39 | // most versatile approach.
40 | buildLineComplex(graphData, graphGeom);
41 |
42 | // only accepts partially line joins.
43 | // LineStrokePixi.buildLine(graphData, graphGeom);
44 | } else {
45 | buildLine(graphData, graphGeom);
46 | }
47 | }
48 |
49 | public static function buildLineComplex(graphicsData:GraphData, graphicsGeometry:GraphGeom):void {
50 | // TODO: optimize...
51 | var points:Array = graphicsData.points;
52 | var style:LineStyle = graphicsData.lineStyle;
53 | var verts:Array = graphicsGeometry.points;
54 |
55 | trace(points.length,verts.length);
56 | ComplexLineStroke.minSegmentPixels = 2;
57 |
58 | var ppts:Array = ComplexLineStroke.getPntsFromNumbers(points);
59 | var res:Array = ComplexLineStroke.getStrokeGeometry(ppts, {
60 | width: style.width,
61 | cap: style.caps,
62 | join: style.joint,
63 | miterLimit: style.miterLimit
64 | });
65 |
66 | var indexCount:int = res.length;
67 | var indexStart:int = verts.length / 2;
68 | // const indices:Array = graphicsGeometry.indices;
69 |
70 | // push all the geom.
71 | for (var i:int = 0, j:int = verts.length, ilen:int = res.length; i < ilen; i++) {
72 | verts[j++] = res[i].x;
73 | verts[j++] = res[i].y;
74 | }
75 |
76 | var v:Array = graphicsGeometry.indices;
77 | var si:int = v.length;
78 | for (i = 0; i < indexCount; i++) {
79 | v[si++] = indexStart + i;
80 | }
81 | }
82 |
83 | public static function buildLine(graphicsData:GraphData, geom:GraphGeom):void {
84 | // TODO OPTIMISE!
85 | var points:Array = graphicsData.points;//|| graphicsData.shape.points.slice();
86 | if (points.length === 0) {
87 | return;
88 | }
89 |
90 | var style:LineStyle = graphicsData.lineStyle;
91 | // get first and last point.. figure out the middle!
92 | var firstPoint:Point = POINT1;//new Point(points[0], points[1]);
93 | var lastPoint:Point = POINT2;//new Point(points[points.length - 2], points[points.length - 1]);
94 | firstPoint.x = points[0];
95 | firstPoint.y = points[1];
96 | lastPoint.x = points[points.length - 2];
97 | lastPoint.y = points[points.length - 1];
98 |
99 | // if the first point is the last point - gonna have issues :)
100 | if (firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) {
101 | // need to clone as we are going to slightly modify the shape..
102 | points = points.slice();
103 | points.pop();
104 | points.pop();
105 | // lastPoint = new Point(points[points.length - 2], points[points.length - 1]);
106 | lastPoint.x = points[points.length - 2];
107 | lastPoint.y = points[points.length - 1];
108 |
109 | var midPointX:Number = lastPoint.x + ((firstPoint.x - lastPoint.x) * 0.5);
110 | var midPointY:Number = lastPoint.y + ((firstPoint.y - lastPoint.y) * 0.5);
111 |
112 | points.unshift(midPointX, midPointY);
113 | points.push(midPointX, midPointY);
114 | }
115 |
116 | // var verts:Array = graphicsGeometry.points;
117 | var indexCount:int = points.length;
118 | var length:int = indexCount / 2;
119 | var indexStart:int = geom.points.length / 2;
120 | // DRAW the Line
121 | var width:Number = style.width / 2;
122 |
123 | // sort color
124 | var p1x:Number = points[0];
125 | var p1y:Number = points[1];
126 | var p2x:Number = points[2];
127 | var p2y:Number = points[3];
128 | var p3x:Number = 0;
129 | var p3y:Number = 0;
130 |
131 | var perpx:Number = -(p1y - p2y);
132 | var perpy:Number = p1x - p2x;
133 | var perp2x:Number = 0;
134 | var perp2y:Number = 0;
135 | var perp3x:Number = 0;
136 | var perp3y:Number = 0;
137 |
138 | var dist:Number = Math.sqrt((perpx * perpx) + (perpy * perpy));
139 |
140 | perpx /= dist;
141 | perpy /= dist;
142 | perpx *= width;
143 | perpy *= width;
144 |
145 | const ratio:Number = style.alignment;
146 | var r1:Number = (1 - ratio) * 2;
147 | var r2:Number = ratio * 2;
148 |
149 | // start
150 | geom.points.push(
151 | p1x - (perpx * r1),
152 | p1y - (perpy * r1));
153 |
154 | geom.points.push(
155 | p1x + (perpx * r2),
156 | p1y + (perpy * r2));
157 |
158 | var i:int;
159 | for (i = 1; i < length - 1; ++i) {
160 | p1x = points[(i - 1) * 2];
161 | p1y = points[((i - 1) * 2) + 1];
162 |
163 | p2x = points[i * 2];
164 | p2y = points[(i * 2) + 1];
165 |
166 | p3x = points[(i + 1) * 2];
167 | p3y = points[((i + 1) * 2) + 1];
168 |
169 | perpx = -(p1y - p2y);
170 | perpy = p1x - p2x;
171 |
172 | dist = Math.sqrt((perpx * perpx) + (perpy * perpy));
173 | perpx /= dist;
174 | perpy /= dist;
175 | perpx *= width;
176 | perpy *= width;
177 |
178 | perp2x = -(p2y - p3y);
179 | perp2y = p2x - p3x;
180 |
181 | dist = Math.sqrt((perp2x * perp2x) + (perp2y * perp2y));
182 | perp2x /= dist;
183 | perp2y /= dist;
184 | perp2x *= width;
185 | perp2y *= width;
186 |
187 | var a1:Number = (-perpy + p1y) - (-perpy + p2y);
188 | var b1:Number = (-perpx + p2x) - (-perpx + p1x);
189 | var c1:Number = ((-perpx + p1x) * (-perpy + p2y)) - ((-perpx + p2x) * (-perpy + p1y));
190 | var a2:Number = (-perp2y + p3y) - (-perp2y + p2y);
191 | var b2:Number = (-perp2x + p2x) - (-perp2x + p3x);
192 | var c2:Number = ((-perp2x + p3x) * (-perp2y + p2y)) - ((-perp2x + p2x) * (-perp2y + p3y));
193 |
194 | var denom:Number = (a1 * b2) - (a2 * b1);
195 | if (Math.abs(denom) < 0.1) {
196 | denom += 10.1;
197 | geom.points.push(
198 | p2x - (perpx * r1),
199 | p2y - (perpy * r1));
200 |
201 | geom.points.push(
202 | p2x + (perpx * r2),
203 | p2y + (perpy * r2));
204 |
205 | continue;
206 | }
207 |
208 | var px:Number = ((b1 * c2) - (b2 * c1)) / denom;
209 | var py:Number = ((a2 * c1) - (a1 * c2)) / denom;
210 | var pdist:Number = ((px - p2x) * (px - p2x)) + ((py - p2y) * (py - p2y));
211 |
212 | if (pdist > (196 * width * width)) {
213 | perp3x = perpx - perp2x;
214 | perp3y = perpy - perp2y;
215 |
216 | dist = Math.sqrt((perp3x * perp3x) + (perp3y * perp3y));
217 | perp3x /= dist;
218 | perp3y /= dist;
219 | perp3x *= width;
220 | perp3y *= width;
221 |
222 | geom.points.push(p2x - (perp3x * r1), p2y - (perp3y * r1));
223 |
224 | geom.points.push(p2x + (perp3x * r2), p2y + (perp3y * r2));
225 |
226 | geom.points.push(p2x - (perp3x * r2 * r1), p2y - (perp3y * r1));
227 |
228 | indexCount++;
229 | } else {
230 | geom.points.push(p2x + ((px - p2x) * r1), p2y + ((py - p2y) * r1));
231 | geom.points.push(p2x - ((px - p2x) * r2), p2y - ((py - p2y) * r2));
232 | }
233 | }
234 |
235 | p1x = points[(length - 2) * 2];
236 | p1y = points[((length - 2) * 2) + 1];
237 |
238 | p2x = points[(length - 1) * 2];
239 | p2y = points[((length - 1) * 2) + 1];
240 |
241 | perpx = -(p1y - p2y);
242 | perpy = p1x - p2x;
243 |
244 | dist = Math.sqrt((perpx * perpx) + (perpy * perpy));
245 | perpx /= dist;
246 | perpy /= dist;
247 | perpx *= width;
248 | perpy *= width;
249 |
250 | geom.points.push(p2x - (perpx * r1), p2y - (perpy * r1));
251 | geom.points.push(p2x + (perpx * r2), p2y + (perpy * r2));
252 |
253 | // var indices:Array = graphicsGeometry.indices;
254 | // indices.push(indexStart);
255 | indexCount -= 2;
256 | for (i = 0; i < indexCount; ++i) {
257 | // indices.push(indexStart, indexStart + 1, indexStart + 2);
258 | geom.indices.push(indexStart, indexStart + 1, indexStart + 2);
259 | indexStart++;
260 | }
261 | }
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/lines/LineStrokePixi.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 2019-05-16.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.utils.lines {
8 | import com.roipeker.starling.draw.GraphData;
9 | import com.roipeker.starling.draw.GraphGeom;
10 | import com.roipeker.starling.draw.styles.LineStyle;
11 |
12 | import flash.display.JointStyle;
13 |
14 | import flash.geom.Point;
15 |
16 | /**
17 | *
18 | * Unfinished implementation (round joins are not accurate) and missing cap styles.
19 | *
20 | * Ported from:
21 | * https://github.com/pixijs/pixi.js/pull/5325
22 | *
23 | * Might be available in Pixi v5.1
24 | *
25 | */
26 | public class LineStrokePixi {
27 | public function LineStrokePixi() {
28 | }
29 |
30 | private static const TOLERANCE:Number = 0.0001;
31 | private static const PI_LBOUND:Number = Math.PI - TOLERANCE;
32 | private static const PI_UBOUND:Number = Math.PI + TOLERANCE;
33 | public static const PI_2:Number = 2 * Math.PI;
34 |
35 | public static function buildLine(graphicsData:GraphData, graphicsGeometry:GraphGeom )
36 | {
37 | // TODO OPTIMISE!
38 | var points:Array = graphicsData.points ;//|| graphicsData.shape.points.slice();
39 | if (points.length === 0)
40 | {
41 | return;
42 | }
43 | // if the line width is an odd number add 0.5 to align to a whole pixel
44 | // commenting this out fixes #711 and #1620
45 | // if (graphicsData.lineWidth%2)
46 | // {
47 | // for (i = 0; i < points.length; i++)
48 | // {
49 | // points[i] += 0.5;
50 | // }
51 | // }
52 |
53 | const style:LineStyle = graphicsData.lineStyle;
54 |
55 | // get first and last point.. figure out the middle!
56 | var firstPoint:Point = new Point(points[0], points[1]);
57 | var lastPoint:Point = new Point(points[points.length - 2], points[points.length - 1]);
58 |
59 | // if the first point is the last point - gonna have issues :)
60 | if (firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y)
61 | {
62 | // need to clone as we are going to slightly modify the shape..
63 | points = points.slice();
64 | points.pop();
65 | points.pop();
66 |
67 | lastPoint = new Point(points[points.length - 2], points[points.length - 1]);
68 |
69 | const midPointX:Number = lastPoint.x + ((firstPoint.x - lastPoint.x) * 0.5);
70 | const midPointY:Number = lastPoint.y + ((firstPoint.y - lastPoint.y) * 0.5);
71 |
72 | points.unshift(midPointX, midPointY);
73 | points.push(midPointX, midPointY);
74 | }
75 |
76 | const verts:Array = graphicsGeometry.points;
77 | const length:int = points.length / 2;
78 | var indexCount:int = points.length;
79 | var indexStart:int = verts.length / 2;
80 | trace(verts);
81 |
82 | // DRAW the Line
83 | const width:Number = style.width / 2;
84 |
85 | // sort color
86 | var p1x:Number = points[0];
87 | var p1y:Number = points[1];
88 | var p2x:Number = points[2];
89 | var p2y:Number = points[3];
90 | var p3x:Number = 0;
91 | var p3y:Number = 0;
92 |
93 | var perpx:Number = -(p1y - p2y);
94 | var perpy:Number = p1x - p2x;
95 | var perp2x:Number = 0;
96 | var perp2y:Number = 0;
97 |
98 | var midx:Number = 0;
99 | var midy:Number = 0;
100 |
101 | var dist12:Number = 0;
102 | var dist23:Number = 0;
103 | var distMid:Number = 0;
104 | var minDist:Number = 0;
105 |
106 | var dist:Number = Math.sqrt((perpx * perpx) + (perpy * perpy));
107 |
108 | perpx /= dist;
109 | perpy /= dist;
110 | perpx *= width;
111 | perpy *= width;
112 |
113 | const ratio:Number = style.alignment;// 0.5;
114 | const r1:Number = (1 - ratio) * 2;
115 | const r2:Number = ratio * 2;
116 |
117 | // start
118 | verts.push(
119 | p1x - (perpx * r1),
120 | p1y - (perpy * r1));
121 |
122 | verts.push(
123 | p1x + (perpx * r2),
124 | p1y + (perpy * r2));
125 |
126 | for (var i:int = 1; i < length - 1; ++i)
127 | {
128 | p1x = points[(i - 1) * 2];
129 | p1y = points[((i - 1) * 2) + 1];
130 |
131 | p2x = points[i * 2];
132 | p2y = points[(i * 2) + 1];
133 |
134 | p3x = points[(i + 1) * 2];
135 | p3y = points[((i + 1) * 2) + 1];
136 |
137 | perpx = -(p1y - p2y);
138 | perpy = p1x - p2x;
139 |
140 | perp2x = -(p2y - p3y);
141 | perp2y = p2x - p3x;
142 |
143 | dist = len(perpx, perpy);
144 | perpx /= dist;
145 | perpy /= dist;
146 | perpx *= width;
147 | perpy *= width;
148 |
149 | dist = len(perp2x, perp2y);
150 | perp2x /= dist;
151 | perp2y /= dist;
152 | perp2x *= width;
153 | perp2y *= width;
154 |
155 | const a1:Number = p1y - p2y;
156 | const b1:Number = p2x - p1x;
157 | const a2:Number = p3y - p2y;
158 | const b2:Number = p2x - p3x;
159 |
160 | const denom:Number = (a1 * b2) - (a2 * b1);
161 | const join:String = style.joint;
162 |
163 | var px:Number;
164 | var py:Number;
165 | var pdist:Number;
166 |
167 | // parallel or almost parallel ~0 or ~180 deg
168 | if (Math.abs(denom) < TOLERANCE)
169 | {
170 | // bevel, miter or round ~0deg
171 | if (join !== JointStyle.ROUND || Math.abs(angleDiff(perpx, perpy, perp2x, perp2y)) < TOLERANCE)
172 | {
173 | verts.push(
174 | p2x - (perpx * r1),
175 | p2y - (perpy * r1)
176 | );
177 |
178 | verts.push(
179 | p2x + (perpx * r2),
180 | p2y + (perpy * r2)
181 | );
182 |
183 | continue;
184 | }
185 | else // round ~180deg
186 | {
187 | px = p2x;
188 | py = p2y;
189 | pdist = 0;
190 | }
191 | }
192 | else
193 | {
194 | const c1:Number = ((-perpx + p1x) * (-perpy + p2y)) - ((-perpx + p2x) * (-perpy + p1y));
195 | const c2:Number = ((-perp2x + p3x) * (-perp2y + p2y)) - ((-perp2x + p2x) * (-perp2y + p3y));
196 |
197 | px = ((b1 * c2) - (b2 * c1)) / denom;
198 | py = ((a2 * c1) - (a1 * c2)) / denom;
199 | pdist = ((px - p2x) * (px - p2x)) + ((py - p2y) * (py - p2y));
200 | }
201 |
202 | // funky comparison to have backwards compat which will fall back by default to miter
203 | // TODO: introduce miterLimit
204 | if (join !== JointStyle.BEVEL && join !== JointStyle.ROUND && pdist <= (196 * width * width))
205 | {
206 | verts.push(p2x + ((px - p2x) * r1), p2y + ((py - p2y) * r1));
207 |
208 | verts.push(p2x - ((px - p2x) * r2), p2y - ((py - p2y) * r2));
209 | }
210 | else
211 | {
212 | const flip:Boolean = shouldFlip(p1x, p1y, p2x, p2y, p3x, p3y);
213 |
214 | dist12 = len(p2x - p1x, p2y - p1y);
215 | dist23 = len(p3x - p2x, p3y - p2y);
216 | minDist = Math.min(dist12, dist23);
217 |
218 | if (flip)
219 | {
220 | perpx = -perpx;
221 | perpy = -perpy;
222 | perp2x = -perp2x;
223 | perp2y = -perp2y;
224 |
225 | midx = (px - p2x) * r1;
226 | midy = (py - p2y) * r1;
227 | distMid = len(midx, midy);
228 |
229 | if (minDist < distMid)
230 | {
231 | midx /= distMid;
232 | midy /= distMid;
233 | midx *= minDist;
234 | midy *= minDist;
235 | }
236 |
237 | midx = p2x - midx;
238 | midy = p2y - midy;
239 | }
240 | else
241 | {
242 | midx = (px - p2x) * r2;
243 | midy = (py - p2y) * r2;
244 | distMid = len(midx, midy);
245 |
246 | if (minDist < distMid)
247 | {
248 | midx /= distMid;
249 | midy /= distMid;
250 | midx *= minDist;
251 | midy *= minDist;
252 | }
253 |
254 | midx += p2x;
255 | midy += p2y;
256 | }
257 |
258 | // if (join === LINE_JOIN.ROUND)
259 | if (join === JointStyle.ROUND)
260 | {
261 | const rad = flip ? r1 : r2;
262 |
263 | // eslint-disable-next-line max-params
264 | indexCount += buildRoundCap(midx, midy,
265 | p2x + (perpx * rad), p2y + (perpy * rad),
266 | p2x + (perp2x * rad), p2y + (perp2y * rad),
267 | p3x, p3y,
268 | verts,
269 | flip);
270 | }
271 | else if (join === JointStyle.BEVEL || pdist > (196 * width * width)) // TODO: introduce miterLimit
272 | {
273 | if (flip)
274 | {
275 | verts.push(p2x + (perpx * r2), p2y + (perpy * r2));
276 | verts.push(midx, midy);
277 |
278 | verts.push(p2x + (perp2x * r2), p2y + (perp2y * r2));
279 | verts.push(midx, midy);
280 | }
281 | else
282 | {
283 | verts.push(midx, midy);
284 | verts.push(p2x + (perpx * r1), p2y + (perpy * r1));
285 |
286 | verts.push(midx, midy);
287 | verts.push(p2x + (perp2x * r1), p2y + (perp2y * r1));
288 | }
289 |
290 | indexCount += 2;
291 | }
292 | }
293 | }
294 |
295 | p1x = points[(length - 2) * 2];
296 | p1y = points[((length - 2) * 2) + 1];
297 |
298 | p2x = points[(length - 1) * 2];
299 | p2y = points[((length - 1) * 2) + 1];
300 |
301 | perpx = -(p1y - p2y);
302 | perpy = p1x - p2x;
303 |
304 | dist = Math.sqrt((perpx * perpx) + (perpy * perpy));
305 | perpx /= dist;
306 | perpy /= dist;
307 | perpx *= width;
308 | perpy *= width;
309 |
310 | verts.push(p2x - (perpx * r1), p2y - (perpy * r1));
311 |
312 | verts.push(p2x + (perpx * r2), p2y + (perpy * r2));
313 |
314 | const indices:Array = graphicsGeometry.indices;
315 |
316 | // indices.push(indexStart);
317 |
318 | for (i = 0; i < indexCount - 2; ++i)
319 | {
320 | indices.push(indexStart, indexStart + 1, indexStart + 2);
321 |
322 | indexStart++;
323 | }
324 | }
325 |
326 | private static function len(x:Number, y:Number):Number
327 | {
328 | return Math.sqrt((x * x) + (y * y));
329 | }
330 |
331 | /**
332 | * Check turn direction. If counterclockwise, we must invert prep vectors, otherwise they point 'inwards' the angle,
333 | * resulting in funky looking lines.
334 | *
335 | * @ignore
336 | * @private
337 | * @param {number} p0x - x of 1st point
338 | * @param {number} p0y - y of 1st point
339 | * @param {number} p1x - x of 2nd point
340 | * @param {number} p1y - y of 2nd point
341 | * @param {number} p2x - x of 3rd point
342 | * @param {number} p2y - y of 3rd point
343 | *
344 | * @returns {boolean} true if perpendicular vectors should be flipped, otherwise false
345 | */
346 | private static function shouldFlip(p0x:Number, p0y:Number, p1x:Number, p1y:Number, p2x:Number, p2y:Number):Boolean
347 | {
348 | return ((p1x - p0x) * (p2y - p0y)) - ((p2x - p0x) * (p1y - p0y)) < 0;
349 | }
350 |
351 | private static function angleDiff(p0x:Number, p0y:Number, p1x:Number, p1y:Number):Number
352 | {
353 | const angle1:Number = Math.atan2(p0x, p0y);
354 | const angle2:Number = Math.atan2(p1x, p1y);
355 |
356 | if (angle2 > angle1)
357 | {
358 | if ((angle2 - angle1) >= PI_LBOUND)
359 | {
360 | return angle2 - PI_2 - angle1;
361 | }
362 | }
363 | else if ((angle1 - angle2) >= PI_LBOUND)
364 | {
365 | return angle2 - (angle1 - PI_2);
366 | }
367 |
368 | return angle2 - angle1;
369 | }
370 |
371 | private static function buildRoundCap(cx:Number, cy:Number, p1x:Number, p1y:Number, p2x:Number, p2y:Number, nxtPx:Number, nxtPy:Number, verts:Array, flipped:Boolean):int
372 | {
373 | const cx2p0x:Number = p1x - cx;
374 | const cy2p0y:Number = p1y - cy;
375 |
376 | var angle0:Number = Math.atan2(cx2p0x, cy2p0y);
377 | var angle1:Number = Math.atan2(p2x - cx, p2y - cy);
378 |
379 | var startAngle:Number = angle0;
380 |
381 | if (angle1 > angle0)
382 | {
383 | if ((angle1 - angle0) >= PI_LBOUND)
384 | {
385 | angle1 = angle1 - PI_2;
386 | }
387 | }
388 | else if ((angle0 - angle1) >= PI_LBOUND)
389 | {
390 | angle0 = angle0 - PI_2;
391 | }
392 |
393 | var angleDiff:Number = angle1 - angle0;
394 | const absAngleDiff:Number = Math.abs(angleDiff);
395 |
396 | if (absAngleDiff >= PI_LBOUND && absAngleDiff <= PI_UBOUND)
397 | {
398 | const r1x:Number = cx - nxtPx;
399 | const r1y:Number = cy - nxtPy;
400 |
401 | if (r1x === 0)
402 | {
403 | if (r1y > 0)
404 | {
405 | angleDiff = -angleDiff;
406 | }
407 | }
408 | else if (r1x >= -TOLERANCE)
409 | {
410 | angleDiff = -angleDiff;
411 | }
412 | }
413 |
414 | const radius:Number = len(cx2p0x, cy2p0y);
415 | const segCount:int = ((15 * absAngleDiff * Math.sqrt(radius) / Math.PI) >> 0) + 1;
416 | const angleInc:Number = angleDiff / segCount;
417 |
418 | startAngle += angleInc;
419 | var i:int ;
420 | var angle:Number;
421 | if (flipped)
422 | {
423 | verts.push(p1x, p1y);
424 | verts.push(cx, cy);
425 |
426 | for (i = 1, angle = startAngle; i < segCount; i++, angle += angleInc)
427 | {
428 | verts.push(cx + ((Math.sin(angle) * radius)),
429 | cy + ((Math.cos(angle) * radius)));
430 | verts.push(cx, cy);
431 | }
432 |
433 | verts.push(p2x, p2y);
434 | verts.push(cx, cy);
435 | }
436 | else
437 | {
438 | verts.push(cx, cy);
439 | verts.push(p1x, p1y);
440 |
441 | for ( i = 1, angle = startAngle; i < segCount; i++, angle += angleInc)
442 | {
443 | verts.push(cx, cy);
444 | verts.push(cx + ((Math.sin(angle) * radius)),
445 | cy + ((Math.cos(angle) * radius)));
446 | }
447 |
448 | verts.push(cx, cy);
449 | verts.push(cx + ((Math.sin(angle1) * radius)),
450 | cy + ((Math.cos(angle1) * radius)));
451 | }
452 |
453 | return segCount * 2;
454 | }
455 |
456 | }
457 | }
458 |
--------------------------------------------------------------------------------
/draw_lib/src/com/roipeker/starling/draw/utils/lines/ComplexLineStroke.as:
--------------------------------------------------------------------------------
1 | // =================================================================================================
2 | //
3 | // Created by Rodrigo Lopez [roipeker™] on 10/01/2019.
4 | //
5 | // =================================================================================================
6 |
7 | package com.roipeker.starling.draw.utils.lines {
8 | import flash.display.CapsStyle;
9 | import flash.display.JointStyle;
10 |
11 | /**
12 | * Ported from
13 | * https://hypertolosana.github.io/efficient-webgl-stroking
14 | *
15 | * Not optimized in any way.
16 | *
17 | */
18 |
19 | public class ComplexLineStroke {
20 | public function ComplexLineStroke() {
21 | }
22 |
23 | public static var EPSILON:Number = 0.0001;
24 | public static var minSegmentPixels:Number = 6;
25 |
26 | public static const PI_M_EP:Number = Math.PI - EPSILON;
27 | public static const PI_P_EP:Number = Math.PI + EPSILON;
28 | public static const PI_2:Number = 2 * Math.PI;
29 |
30 |
31 | public static function getStrokeGeometry(points:Array, attrs:Object, vert:Array = null):Array {
32 | // trivial reject
33 | if (points.length < 2) {
34 | return null;
35 | }
36 | var cap:String = attrs.cap || CapsStyle.NONE;
37 | var join:String = attrs.join || JointStyle.BEVEL;
38 | var lineWidth:Number = (attrs.width || 1) / 2;
39 | var miterLimit:Number = attrs.miterLimit || 10;
40 | // var vertices:Array = [];
41 | if (!vert) vert = [];
42 | var middlePnts:Array = []; // middle points per each line segment.
43 | var closed:Boolean = false;
44 | if (points.length === 2) {
45 | join = JointStyle.BEVEL;
46 | createTriangles(points[0], Pnt.Middle(points[0], points[1]), points[1], vert, lineWidth, join, miterLimit);
47 | } else {
48 | // * Disable for simulation purposes, but uncomment for have the system working otherwise.
49 | if (points[0] === points[points.length - 1] ||
50 | (points[0].x === points[points.length - 1].x && points[0].y === points[points.length - 1].y)) {
51 | var p0:Pnt = points.shift();
52 | p0 = Pnt.Middle(p0, points[0]);
53 | points.unshift(p0);
54 | points.push(p0);
55 | closed = true;
56 | }
57 | const npoints:uint = points.length;
58 | var i:int, len:int;
59 | for (i = 0, len = npoints - 1; i < len; i++) {
60 | if (i == 0) {
61 | middlePnts.push(points[0]);
62 | } else if (i == npoints - 2) {
63 | middlePnts.push(points[uint(npoints - 1)])
64 | } else {
65 | middlePnts.push(Pnt.Middle(points[i], points[uint(i + 1)]));
66 | }
67 | }
68 | for (i = 1, len = middlePnts.length; i < len; i++) {
69 | createTriangles(middlePnts[uint(i - 1)], points[i], middlePnts[i], vert, lineWidth, join, miterLimit);
70 | }
71 | }
72 |
73 | const numPoints:uint = points.length;
74 |
75 | if (!closed) {
76 | var p00:Pnt;
77 | var p01:Pnt;
78 | if (cap === CapsStyle.ROUND) {
79 | p00 = vert[0];
80 | p01 = vert[1];
81 | var p02:Pnt = points[1];
82 | var p10:Pnt = vert[vert.length - 1];
83 | var p11:Pnt = vert[vert.length - 3];
84 | var p12:Pnt = points[numPoints - 2];
85 | createRoundCap(points[0], p00, p01, p02, vert);
86 | createRoundCap(points[numPoints - 1], p10, p11, p12, vert);
87 |
88 | } else if (cap === CapsStyle.SQUARE) {
89 | p00 = vert[vert.length - 1];
90 | p01 = vert[vert.length - 3];
91 | createSquareCap(
92 | vert[0],
93 | vert[1],
94 | Pnt.Sub(points[0], points[1]).normalize().scalarMult(Pnt.Sub(points[0], vert[0]).length()),
95 | vert);
96 | createSquareCap(
97 | p00,
98 | p01,
99 | Pnt.Sub(points[numPoints - 1], points[numPoints - 2]).normalize().scalarMult(Pnt.Sub(p01, points[numPoints - 1]).length()),
100 | vert);
101 | }
102 | }
103 | return vert;
104 | }
105 |
106 | public static function createSquareCap(p0:Pnt, p1:Pnt, dir:Pnt, verts:Array) {
107 | const p1dir:Pnt = Pnt.Add(p1, dir);
108 | verts.push(p0, Pnt, p1dir, p1, p1dir, p0);
109 | }
110 |
111 |
112 | public static function createRoundCap(center:Pnt, _p0:Pnt, _p1:Pnt, nextPntInLine:Pnt, verts:Array):void {
113 | var radius:Number = Pnt.Sub(center, _p0).length();
114 | var angle0:Number = Math.atan2((_p1.y - center.y), (_p1.x - center.x));
115 | var angle1:Number = Math.atan2((_p0.y - center.y), (_p0.x - center.x));
116 | var orgAngle0:Number = angle0;
117 | if (angle1 > angle0) {
118 | if (angle1 - angle0 >= PI_M_EP) {
119 | angle1 = angle1 - PI_2;
120 | }
121 | } else {
122 | if (angle0 - angle1 >= PI_M_EP) {
123 | angle0 = angle0 - PI_2;
124 | }
125 | }
126 | var angleDiff:Number = angle1 - angle0;
127 | const absDiff:Number = Math.abs(angleDiff);
128 | if (absDiff >= PI_M_EP && absDiff <= PI_P_EP) {
129 | var r1:Pnt = Pnt.Sub(center, nextPntInLine);
130 | if (r1.x === 0) {
131 | if (r1.y > 0) {
132 | angleDiff = -angleDiff;
133 | }
134 | } else if (r1.x >= -EPSILON) {
135 | angleDiff = -angleDiff;
136 | }
137 | }
138 | var nsegments:uint = (Math.abs(angleDiff * radius) / minSegmentPixels) >> 0;
139 | nsegments++;
140 | var angleInc:Number = angleDiff / nsegments;
141 | for (var i:int = 0; i < nsegments; i++) {
142 |
143 | verts.push(new Pnt(center.x, center.y));
144 | verts.push(new Pnt(
145 | center.x + radius * Math.cos(orgAngle0 + angleInc * i),
146 | center.y + radius * Math.sin(orgAngle0 + angleInc * i)
147 | ));
148 | verts.push(new Pnt(
149 | center.x + radius * Math.cos(orgAngle0 + angleInc * (1 + i)),
150 | center.y + radius * Math.sin(orgAngle0 + angleInc * (1 + i))
151 | ));
152 | }
153 | }
154 |
155 | [Inline]
156 | public static function signedArea(p0:Pnt, p1:Pnt, p2:Pnt):Number {
157 | return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
158 | }
159 |
160 | [Inline]
161 | public static function lineIntersection(p0:Pnt, p1:Pnt, p2:Pnt, p3:Pnt):Pnt {
162 | var a0:Number = p1.y - p0.y;
163 | var b0:Number = p0.x - p1.x;
164 | var a1:Number = p3.y - p2.y;
165 | var b1:Number = p2.x - p3.x;
166 | var det:Number = a0 * b1 - a1 * b0;
167 | if (det > -EPSILON && det < EPSILON) {
168 | return null;
169 | } else {
170 | var c0:Number = a0 * p0.x + b0 * p0.y;
171 | var c1:Number = a1 * p2.x + b1 * p2.y;
172 | var x:Number = (b1 * c0 - b0 * c1) / det;
173 | var y:Number = (a0 * c1 - a1 * c0) / det;
174 | return new Pnt(x, y);
175 | }
176 | }
177 |
178 |
179 | public static function createTriangles(p0:Pnt, p1:Pnt, p2:Pnt, verts:Array, width:Number, join:String, miterLimit:Number) {
180 | var t0:Pnt = Pnt.Sub(p1, p0);
181 | var t2:Pnt = Pnt.Sub(p2, p1);
182 |
183 | t0.perpendicular();
184 | t2.perpendicular();
185 | // triangle composed by the 3 points if clockwise or couterclockwise.
186 | // if counterclockwise, we must invert the line threshold points, otherwise the intersection point
187 | // could be erroneous and lead to odd results.
188 | if (signedArea(p0, p1, p2) > 0) {
189 | t0.invert();
190 | t2.invert();
191 | }
192 | t0.normalize();
193 | t2.normalize();
194 | t0.scalarMult(width);
195 | t2.scalarMult(width);
196 |
197 |
198 | const a_p0t0:Pnt = Pnt.Add(p0, t0);
199 | const a_p1t0:Pnt = Pnt.Add(p1, t0);
200 | const s_p0t0:Pnt = Pnt.Sub(p0, t0);
201 | const s_p1t2:Pnt = Pnt.Sub(p1, t2);
202 | const a_p2t2:Pnt = Pnt.Add(p2, t2);
203 | const a_p1t2:Pnt = Pnt.Add(p1, t2);
204 | const s_p2t2:Pnt = Pnt.Sub(p2, t2);
205 |
206 | var pintersect:Pnt = lineIntersection(
207 | a_p0t0,
208 | a_p1t0,
209 | a_p2t2,
210 | a_p1t2
211 | );
212 |
213 | var anchor:Pnt = null;
214 | var anchorLength:Number = Number.MAX_VALUE;
215 | if (pintersect) {
216 | anchor = Pnt.Sub(pintersect, p1);
217 | anchorLength = anchor.length();
218 | }
219 | var dd:int = (anchorLength / width) | 0;
220 | var p0p1:Pnt = Pnt.Sub(p0, p1);
221 | var p0p1Length:Number = p0p1.length();
222 | var p1p2:Pnt = Pnt.Sub(p1, p2);
223 | var p1p2Length:Number = p1p2.length();
224 |
225 |
226 | /**
227 | * the cross point exceeds any of the segments dimension.
228 | * do not use cross point as reference.
229 | */
230 | if (anchorLength > p0p1Length || anchorLength > p1p2Length) {
231 | verts.push(a_p0t0, s_p0t0, a_p1t0, s_p0t0, a_p1t0, Pnt.Sub(p1, t0));
232 | if (join === JointStyle.ROUND) {
233 | createRoundCap(p1, a_p1t0, a_p1t2, p2, verts);
234 | } else if (join === JointStyle.BEVEL || (join === JointStyle.MITER && dd >= miterLimit)) {
235 | verts.push(p1, a_p1t0, a_p1t2);
236 | } else if (join === JointStyle.MITER && dd < miterLimit && pintersect) {
237 | verts.push(a_p1t0, p1, pintersect, a_p1t2, p1, pintersect);
238 | }
239 | verts.push(a_p2t2, s_p1t2, a_p1t2, a_p2t2, s_p1t2, s_p2t2);
240 | } else {
241 | const s_p1an:Pnt = Pnt.Sub(p1, anchor);
242 |
243 | verts.push(a_p0t0, s_p0t0, s_p1an, a_p0t0, s_p1an, a_p1t0);
244 | if (join === JointStyle.ROUND) {
245 | var _p0:Pnt = a_p1t0;
246 | var _p1:Pnt = a_p1t2;
247 | var _p2:Pnt = s_p1an;
248 | var center:Pnt = p1;
249 | verts.push(_p0, center, _p2);
250 | createRoundCap(center, _p0, _p1, _p2, verts);
251 | verts.push(center, _p1, _p2);
252 | } else {
253 | if (join === JointStyle.BEVEL || join === JointStyle.MITER) {
254 | verts.push(a_p1t0, a_p1t2, s_p1an);
255 | }
256 | if (join === JointStyle.MITER && dd < miterLimit) {
257 | verts.push(pintersect, a_p1t0, a_p1t2);
258 | }
259 | }
260 | verts.push(a_p2t2, s_p1an, a_p1t2, a_p2t2, s_p1an, s_p2t2);
261 | }
262 | }
263 |
264 |
265 | /*public static function createTriangles2(p0:Pnt, p1:Pnt, p2:Pnt, verts:Array, indices:Array, width:Number, join:String, miterLimit:Number) {
266 | var t0:Pnt = Pnt.Sub(p1, p0);
267 | var t2:Pnt = Pnt.Sub(p2, p1);
268 |
269 | t0.perpendicular();
270 | t2.perpendicular();
271 |
272 | // triangle composed by the 3 points if clockwise or couterclockwise.
273 | // if counterclockwise, we must invert the line threshold points, otherwise the intersection point
274 | // could be erroneous and lead to odd results.
275 | if (signedArea(p0, p1, p2) > 0) {
276 | t0.invert();
277 | t2.invert();
278 | }
279 | t0.normalize();
280 | t2.normalize();
281 | t0.scalarMult(width);
282 | t2.scalarMult(width);
283 | var pintersect:Pnt = lineIntersection(
284 | Pnt.Add(t0, p0),
285 | Pnt.Add(t0, p1),
286 | Pnt.Add(t2, p2),
287 | Pnt.Add(t2, p1)
288 | );
289 |
290 | var anchor:Pnt = null;
291 | var anchorLength:Number = Number.MAX_VALUE;
292 | if (pintersect) {
293 | anchor = Pnt.Sub(pintersect, p1);
294 | anchorLength = anchor.length();
295 | }
296 | var dd:Number = (anchorLength / width) | 0;
297 | var p0p1:Pnt = Pnt.Sub(p0, p1);
298 | var p0p1Length:Number = p0p1.length();
299 | var p1p2:Pnt = Pnt.Sub(p1, p2);
300 | var p1p2Length:Number = p1p2.length();
301 | /!**
302 | * the cross point exceeds any of the segments dimension.
303 | * do not use cross point as reference.
304 | *!/
305 | var idx:int = indices.length - 1;
306 |
307 | if (anchorLength > p0p1Length || anchorLength > p1p2Length) {
308 |
309 | verts.push(Pnt.Add(p0, t0));
310 | indices.push(idx + 1);
311 |
312 | verts.push(Pnt.Sub(p0, t0));
313 | indices.push(idx + 2);
314 |
315 | verts.push(Pnt.Add(p1, t0));
316 | indices.push(idx + 3);
317 |
318 | verts.push(Pnt.Sub(p0, t0));//2
319 | indices.push(idx + 2);
320 |
321 | verts.push(Pnt.Add(p1, t0));//3
322 | indices.push(idx + 3);
323 |
324 | verts.push(Pnt.Sub(p1, t0));
325 | indices.push(idx + 4);
326 |
327 | var lastIdx:int;
328 | if (join === JOIN_ROUND) {
329 | createRoundCap(p1, Pnt.Add(p1, t0), Pnt.Add(p1, t2), p2, verts, indices);
330 | } else if (join === JOIN_BEVEL || (join === JOIN_MITER && dd >= miterLimit)) {
331 |
332 | verts.push(p1);
333 | indices.push(idx + 5);
334 |
335 | verts.push(Pnt.Add(p1, t0));//3
336 | indices.push(idx + 3);
337 |
338 | verts.push(Pnt.Add(p1, t2));
339 | indices.push(idx + 6);
340 | lastIdx = idx + 6;
341 |
342 | } else if (join === JOIN_MITER && dd < miterLimit && pintersect) {
343 |
344 | verts.push(Pnt.Add(p1, t0));//3
345 | indices.push(idx + 3);
346 |
347 | verts.push(p1);
348 | indices.push(idx + 5);
349 |
350 | verts.push(pintersect);
351 | indices.push(idx + 6);
352 |
353 | verts.push(Pnt.Add(p1, t2));
354 | indices.push(idx + 7);
355 | lastIdx = idx + 7;
356 |
357 | verts.push(p1);//5
358 | indices.push(idx + 5);
359 |
360 | verts.push(pintersect);//6
361 | indices.push(idx + 6);
362 | }
363 |
364 | // offset.
365 | idx = lastIdx;
366 |
367 | verts.push(Pnt.Add(p2, t2));
368 | indices.push(idx + 1);
369 |
370 | verts.push(Pnt.Sub(p1, t2));
371 | indices.push(idx + 2);
372 |
373 | verts.push(Pnt.Add(p1, t2));//6
374 | indices.push(lastIdx);
375 |
376 | verts.push(Pnt.Add(p2, t2));//7
377 | indices.push(idx + 1);
378 |
379 | verts.push(Pnt.Sub(p1, t2));//8
380 | indices.push(idx + 2);
381 |
382 | verts.push(Pnt.Sub(p2, t2));
383 | indices.push(idx + 3);
384 |
385 | } else {
386 |
387 | verts.push(Pnt.Add(p0, t0));
388 | indices.push(idx + 1);
389 |
390 | verts.push(Pnt.Sub(p0, t0));
391 | indices.push(idx + 2);
392 |
393 | verts.push(Pnt.Sub(p1, anchor));
394 | indices.push(idx + 3);
395 |
396 | verts.push(Pnt.Add(p0, t0));//1
397 | indices.push(idx + 1);
398 |
399 | verts.push(Pnt.Sub(p1, anchor));//3
400 | indices.push(idx + 3);
401 |
402 | verts.push(Pnt.Add(p1, t0));
403 | indices.push(idx + 4);
404 |
405 | if (join === JOIN_ROUND) {
406 | var _p0:Pnt = Pnt.Add(p1, t0);
407 | var _p1:Pnt = Pnt.Add(p1, t2);
408 | var _p2:Pnt = Pnt.Sub(p1, anchor);
409 | var center:Pnt = p1;
410 |
411 | verts.push(_p0);
412 | indices.push(idx + 5);
413 |
414 | verts.push(center);
415 | indices.push(idx + 6);
416 |
417 | verts.push(_p2);
418 | indices.push(idx + 7);
419 |
420 | createRoundCap(center, _p0, _p1, _p2, verts, indices);
421 |
422 | verts.push(center);//6
423 | indices.push(idx + 6);
424 |
425 | verts.push(_p1);
426 | indices.push(idx + 8);
427 |
428 | verts.push(_p2);//7
429 | indices.push(idx + 7);
430 |
431 | } else {
432 | if (join === JOIN_BEVEL || (join === JOIN_MITER && dd >= miterLimit)) {
433 | verts.push(Pnt.Add(p1, t0));
434 | indices.push(idx + 5);
435 |
436 | verts.push(Pnt.Add(p1, t2));
437 | indices.push(idx + 6);
438 |
439 | verts.push(Pnt.Sub(p1, anchor));
440 | indices.push(idx + 7);
441 | }
442 |
443 | if (join === JOIN_MITER && dd < miterLimit) {
444 | var max:int = verts.length;
445 | verts.push(pintersect);
446 | verts.push(Pnt.Add(p1, t0));
447 | verts.push(Pnt.Add(p1, t2));
448 | indices.push(max, max + 1, max + 2);
449 | }
450 | }
451 |
452 | max = verts.length;
453 | verts.push(Pnt.Add(p2, t2));
454 | indices.push(max);
455 |
456 | verts.push(Pnt.Sub(p1, anchor));//3
457 | indices.push(idx + 3);
458 |
459 | verts.push(Pnt.Add(p1, t2));
460 | indices.push(max + 1);
461 |
462 | verts.push(Pnt.Add(p2, t2));
463 | indices.push(max + 2);
464 |
465 | verts.push(Pnt.Sub(p1, anchor));//3
466 | indices.push(idx + 3);
467 |
468 | verts.push(Pnt.Sub(p2, t2));
469 | indices.push(max + 3);
470 |
471 | }
472 | }*/
473 |
474 |
475 | public static function getPntsFromNumbers(points:Object):Array {
476 | var res:Array = [];
477 | var len:int = points.length;
478 | for (var i:int = 0; i < len; i += 2) {
479 | res.push(new Pnt(points[i], points[i + 1]));
480 | }
481 | return res;
482 | }
483 | }
484 | }
485 |
--------------------------------------------------------------------------------