Merge JigsawX
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package jigsawx;
|
||||
|
||||
class JigsawMagicNumbers{
|
||||
// move to external file
|
||||
public static inline var dMore: Float = 12*2/1.5;
|
||||
public static inline var dinout: Float = 5*2/1.5;
|
||||
public static inline var ellipseSmallx: Float = 2*18/6/1.5;
|
||||
public static inline var ellipseSmally: Float = 2*11/6/1.5;
|
||||
public static inline var ellipseLargex: Float = 2*45/8/1.5;
|
||||
public static inline var ellipseLargey: Float = 2*18*2/8/1.5;
|
||||
public static inline var stepSize: Float = 10/1.5;
|
||||
|
||||
}
|
@@ -0,0 +1,189 @@
|
||||
package jigsawx;
|
||||
import jigsawx.JigsawSideData;
|
||||
import jigsawx.math.Vec2;
|
||||
import jigsawx.JigsawMagicNumbers;
|
||||
enum Compass{
|
||||
NORTH;
|
||||
SOUTH;
|
||||
EAST;
|
||||
WEST;
|
||||
}
|
||||
class JigsawPiece{
|
||||
public var enabled: Bool;
|
||||
private var curveBuilder: OpenEllipse;
|
||||
private var stepAngle: Float;
|
||||
private var centre: Vec2;
|
||||
private var points: Array<Vec2>;
|
||||
public var sideData: JigsawPieceData;
|
||||
private var first: Vec2;
|
||||
public var xy: Vec2;
|
||||
public var row: Int;
|
||||
public var col: Int;
|
||||
public function new( xy_: Vec2
|
||||
, row: Int
|
||||
, col: Int
|
||||
, lt: Vec2, rt: Vec2, rb: Vec2, lb: Vec2
|
||||
, sideData_: JigsawPieceData
|
||||
){
|
||||
enabled = true;
|
||||
xy = new Vec2( xy_.x, xy_.y );
|
||||
sideData = sideData_;
|
||||
points = [];
|
||||
stepAngle = JigsawMagicNumbers.stepSize*Math.PI/180;
|
||||
first = lt;
|
||||
// NORTH side
|
||||
if( sideData.north != null ) createVertSide( lt, rt, sideData.north, NORTH );
|
||||
points.push( rt );
|
||||
// EAST side
|
||||
if( sideData.east != null ) createHoriSide( rt, rb, sideData.east, EAST );
|
||||
points.push( rb );
|
||||
// SOUTH side
|
||||
if( sideData.south != null ) createVertSide( rb, lb, sideData.south, SOUTH );
|
||||
points.push( lb );
|
||||
// WEST side
|
||||
if( sideData.west != null ) createHoriSide( lb, lt, sideData.west, WEST );
|
||||
points.push( lt );
|
||||
}
|
||||
public function getPoints(): Array<Vec2> {
|
||||
return points;
|
||||
}
|
||||
public function getFirst(): Vec2 {
|
||||
return first;
|
||||
}
|
||||
private function createVertSide( A: Vec2
|
||||
, B: Vec2
|
||||
, side: JigsawSideData
|
||||
, compass: Compass
|
||||
){
|
||||
drawSide( A.x + ( B.x - A.x )/2 + JigsawMagicNumbers.dMore/2 - side.squew*( JigsawMagicNumbers.dMore )
|
||||
, A.y + ( B.y - A.y )/2 + JigsawMagicNumbers.dinout/2 - side.inout*( JigsawMagicNumbers.dinout )
|
||||
, side
|
||||
, compass
|
||||
);
|
||||
}
|
||||
private function createHoriSide ( A: Vec2
|
||||
, B: Vec2
|
||||
, side: JigsawSideData
|
||||
, compass: Compass
|
||||
){
|
||||
|
||||
drawSide( A.x + ( B.x - A.x )/2 + JigsawMagicNumbers.dinout/2 - side.inout*( JigsawMagicNumbers.dinout )
|
||||
, A.y + ( B.y - A.y )/2 + JigsawMagicNumbers.dMore/2 - side.squew*( JigsawMagicNumbers.dMore )
|
||||
, side
|
||||
, compass
|
||||
);
|
||||
}
|
||||
private function drawSide( dx: Float, dy: Float, sideData: JigsawSideData, compass: Compass ){
|
||||
var halfPI = Math.PI/2;
|
||||
var dimensions = new Vec2();
|
||||
var offsetCentre = new Vec2();
|
||||
var bubble = sideData.bubble;
|
||||
centre =
|
||||
switch( compass )
|
||||
{
|
||||
case NORTH: new Vec2( dx, dy + 6*switch bubble{ case IN: 1; case OUT: -1; } );
|
||||
case EAST: new Vec2( dx - 6*switch bubble{ case IN: 1; case OUT: -1; }, dy );
|
||||
case SOUTH: new Vec2( dx, dy - 6*switch bubble{ case IN: 1; case OUT: -1; } );
|
||||
case WEST: new Vec2( dx + 6*switch bubble{ case IN: 1; case OUT: -1; }, dy );
|
||||
}
|
||||
curveBuilder = new OpenEllipse();
|
||||
curveBuilder.centre = centre;
|
||||
// large Arc
|
||||
dimensions.x = ( 1 + ( 0.5 - sideData.centreWide )/2 ) * JigsawMagicNumbers.ellipseLargex;
|
||||
dimensions.y = ( 1 + ( 0.5 - sideData.centreHi )/2 ) * JigsawMagicNumbers.ellipseLargex;
|
||||
curveBuilder.dimensions = dimensions;
|
||||
curveBuilder.beginAngle = Math.PI/8;
|
||||
curveBuilder.finishAngle = -Math.PI/8;
|
||||
curveBuilder.stepAngle = stepAngle;
|
||||
curveBuilder.rotation = switch bubble { case IN: 0; case OUT: Math.PI; }
|
||||
switch( compass ){
|
||||
case NORTH:
|
||||
case EAST: curveBuilder.rotation += halfPI;
|
||||
case SOUTH: curveBuilder.rotation += Math.PI;
|
||||
case WEST: curveBuilder.rotation += 3*halfPI;
|
||||
}
|
||||
var secondPoints = curveBuilder.getRenderList();
|
||||
if( bubble == IN ) secondPoints.reverse();
|
||||
var theta = curveBuilder.beginAngle - curveBuilder.finishAngle + Math.PI;
|
||||
var cosTheta = Math.cos( theta );
|
||||
var sinTheta = Math.sin( theta );
|
||||
var hyp = curveBuilder.getBeginRadius();
|
||||
// left Arc
|
||||
dimensions.x = ( 1 + ( 0.5 - sideData.leftWide )/2 ) * JigsawMagicNumbers.ellipseSmallx;
|
||||
dimensions.y = ( 1 + ( 0.5 - sideData.leftHi )/2 ) * JigsawMagicNumbers.ellipseSmally;
|
||||
curveBuilder.dimensions = dimensions;
|
||||
curveBuilder.beginAngle = halfPI;
|
||||
curveBuilder.finishAngle = -halfPI;
|
||||
curveBuilder.stepAngle = stepAngle;
|
||||
curveBuilder.rotation = theta + switch bubble { case IN: 0; case OUT: halfPI; };
|
||||
switch( compass ){
|
||||
case NORTH:
|
||||
case EAST: curveBuilder.rotation += halfPI;
|
||||
case SOUTH: curveBuilder.rotation += Math.PI;
|
||||
case WEST: curveBuilder.rotation += 3*halfPI;
|
||||
}
|
||||
var hypLeft = hyp + curveBuilder.dimensions.x;
|
||||
switch( compass ){
|
||||
case NORTH:
|
||||
offsetCentre.x = centre.x + hypLeft*cosTheta;
|
||||
offsetCentre.y = centre.y + switch bubble { case IN: hypLeft*sinTheta; case OUT: -hypLeft*sinTheta; }
|
||||
case EAST:
|
||||
offsetCentre.x = centre.x + switch bubble { case IN: -hypLeft*cosTheta; case OUT: hypLeft*cosTheta; }
|
||||
offsetCentre.y = centre.y + hypLeft*sinTheta;
|
||||
case SOUTH:
|
||||
offsetCentre.x = centre.x - hypLeft*cosTheta;
|
||||
offsetCentre.y = centre.y - switch bubble { case IN: hypLeft*sinTheta; case OUT: - hypLeft*sinTheta; }
|
||||
case WEST:
|
||||
offsetCentre.x = centre.x + switch bubble { case IN: hypLeft*cosTheta; case OUT: -hypLeft*cosTheta; }
|
||||
offsetCentre.y = centre.y - hypLeft*sinTheta;
|
||||
}
|
||||
curveBuilder.centre = offsetCentre;
|
||||
var startPoint = curveBuilder.getBegin();
|
||||
var firstPoints = curveBuilder.getRenderList();
|
||||
if( sideData.bubble == OUT ) firstPoints.reverse();
|
||||
firstPoints.pop();
|
||||
firstPoints.pop();
|
||||
secondPoints.shift();
|
||||
secondPoints.shift();
|
||||
secondPoints.shift();
|
||||
points = points.concat( firstPoints.concat( secondPoints ) );
|
||||
// right Arc
|
||||
dimensions.x = ( 1 + ( 0.5 - sideData.rightWide )/2 ) * JigsawMagicNumbers.ellipseSmallx;
|
||||
dimensions.y = ( 1 + ( 0.5 - sideData.rightHi )/2 ) * JigsawMagicNumbers.ellipseSmally;
|
||||
curveBuilder.dimensions = dimensions;
|
||||
curveBuilder.beginAngle = halfPI;
|
||||
curveBuilder.finishAngle = -halfPI;
|
||||
curveBuilder.stepAngle = stepAngle;
|
||||
curveBuilder.rotation = theta + switch bubble { case IN: - halfPI; case OUT: Math.PI; };
|
||||
switch( compass ){
|
||||
case NORTH:
|
||||
case EAST: curveBuilder.rotation += halfPI;
|
||||
case SOUTH: curveBuilder.rotation += Math.PI;
|
||||
case WEST: curveBuilder.rotation += 3*halfPI;
|
||||
}
|
||||
var hypRight = hyp + curveBuilder.dimensions.x;
|
||||
switch( compass ){
|
||||
case NORTH:
|
||||
offsetCentre.x = centre.x - hypRight*cosTheta;
|
||||
offsetCentre.y = centre.y + switch bubble { case IN: hypRight*sinTheta; case OUT: -hypRight*sinTheta; };
|
||||
case EAST:
|
||||
offsetCentre.x = centre.x + switch bubble { case IN: -hypLeft*cosTheta; case OUT: hypLeft*cosTheta; }
|
||||
offsetCentre.y = centre.y - hypLeft*sinTheta;
|
||||
case SOUTH:
|
||||
offsetCentre.x = centre.x + hypRight*cosTheta;
|
||||
offsetCentre.y = centre.y - switch bubble { case IN: hypRight*sinTheta; case OUT: -hypRight*sinTheta; };
|
||||
case WEST:
|
||||
offsetCentre.x = centre.x + switch bubble { case IN: hypLeft*cosTheta; case OUT: -hypLeft*cosTheta; }
|
||||
offsetCentre.y = centre.y + hypLeft*sinTheta;
|
||||
}
|
||||
curveBuilder.centre = offsetCentre;
|
||||
var thirdPoints = curveBuilder.getRenderList();
|
||||
if( bubble == OUT ) thirdPoints.reverse();
|
||||
thirdPoints.shift();
|
||||
thirdPoints.shift();
|
||||
points.pop();
|
||||
points.pop();
|
||||
points.pop();
|
||||
points = points.concat( thirdPoints );
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
package jigsawx;
|
||||
typedef JigsawPieceData = {
|
||||
var north: JigsawSideData;
|
||||
var east: JigsawSideData;
|
||||
var south: JigsawSideData;
|
||||
var west: JigsawSideData;
|
||||
}
|
||||
enum Bubble{
|
||||
IN;
|
||||
OUT;
|
||||
}
|
||||
class JigsawSideData{
|
||||
// if the nobble is IN OUT or null ( flat side )
|
||||
public var bubble: Bubble;
|
||||
//offsets random multiplier
|
||||
public var squew: Float;
|
||||
// inout random multiplier
|
||||
public var inout: Float;
|
||||
//ellipse width and height random multiplier, drawn in the order left, centre, right
|
||||
public var leftWide: Float;
|
||||
public var leftHi: Float;
|
||||
public var centreWide: Float;
|
||||
public var centreHi: Float;
|
||||
public var rightWide: Float;
|
||||
public var rightHi: Float;
|
||||
// returns half a jigsawPieceData, the other side is populated from piece above and from left
|
||||
public static function halfPieceData(): JigsawPieceData{
|
||||
#if !noRandom return { north: null, east: create(), south: create(), west: null };
|
||||
// Test use -D noRandom
|
||||
#else return { north: null, east: createSimple(), south: createSimple(), west: null };
|
||||
#end
|
||||
}
|
||||
private static function createBubble(): Bubble {
|
||||
return ( Math.round( Math.random() ) == 1 )? IN: OUT;
|
||||
}
|
||||
private static function swapBubble( bubble: Bubble ): Bubble {
|
||||
if( bubble == OUT ) return IN;
|
||||
if( bubble == IN ) return OUT;
|
||||
return null;
|
||||
}
|
||||
// reflect side
|
||||
public static function reflect( j: JigsawSideData ): JigsawSideData {
|
||||
var side = new JigsawSideData();
|
||||
side.bubble = swapBubble( j.bubble );
|
||||
//left right or up dawn offset.
|
||||
side.squew = j.squew;
|
||||
// in out
|
||||
side.inout = j.inout;
|
||||
// radii of ellipses
|
||||
side.leftWide = j.rightWide;
|
||||
side.leftHi = j.rightHi;
|
||||
side.centreWide = j.centreWide;
|
||||
side.centreHi = j.centreHi;
|
||||
side.rightWide = j.leftWide;
|
||||
side.rightHi = j.leftHi;
|
||||
return side;
|
||||
}
|
||||
// when you want to test no random.
|
||||
public static function createSimple(): JigsawSideData {
|
||||
var side = new JigsawSideData();
|
||||
side.bubble = createBubble();
|
||||
//left right or up dawn offset.
|
||||
side.squew = 0.5;
|
||||
// in out
|
||||
side.inout = 0.5;
|
||||
// radii of ellipses
|
||||
side.leftWide = 0.5;
|
||||
side.leftHi = 0.5;
|
||||
side.centreWide = 0.5;
|
||||
side.centreHi = 0.5;
|
||||
side.rightWide = 0.5;
|
||||
side.rightHi = 0.5;
|
||||
return side;
|
||||
}
|
||||
public static function create(): JigsawSideData {
|
||||
var side = new JigsawSideData();
|
||||
side.bubble = createBubble();
|
||||
//left right or up dawn offset.
|
||||
side.squew = Math.random();
|
||||
// in out
|
||||
side.inout = Math.random();
|
||||
// radii of ellipses
|
||||
side.leftWide = Math.random();
|
||||
side.leftHi = Math.random();
|
||||
side.centreWide = Math.random();
|
||||
side.centreHi = Math.random();
|
||||
side.rightWide = Math.random();
|
||||
side.rightHi = Math.random();
|
||||
return side;
|
||||
}
|
||||
// use create instead
|
||||
private function new(){}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
package jigsawx ;
|
||||
import jigsawx.OpenEllipse ;
|
||||
import jigsawx.JigsawPiece ;
|
||||
import jigsawx.math.Vec2;
|
||||
import jigsawx.JigsawSideData;
|
||||
class Jigsawx {
|
||||
private var rows: Int;
|
||||
private var cols: Int;
|
||||
private var pieces: Array<Array<JigsawPiece>>;
|
||||
public var jigs: Array<JigsawPiece>;
|
||||
private var sides: Array<Array<JigsawPieceData>>;
|
||||
private var lt: Float;
|
||||
private var rt: Float;
|
||||
private var rb: Float;
|
||||
private var lb: Float;
|
||||
private var dx: Float;
|
||||
private var dy: Float;
|
||||
private var length: Int;
|
||||
public function new( dx_: Float
|
||||
, dy_: Float
|
||||
, rows_: Int
|
||||
, cols_: Int
|
||||
) {
|
||||
pieces = [];
|
||||
jigs = [];
|
||||
sides = [];
|
||||
dx = dx_;
|
||||
dy = dy_;
|
||||
rows = rows_;
|
||||
cols = cols_;
|
||||
//corners, theoretically JigsawSideData could be modified to allow these to have a random element.
|
||||
var xy = new Vec2( 20, 20 );
|
||||
var lt = new Vec2( 20, 20 );
|
||||
var rt = new Vec2( 20 + dx, 20 );
|
||||
var rb = new Vec2( 20 + dx, dy + 20 );
|
||||
var lb = new Vec2( 20, dy + 20 );
|
||||
length = 0;
|
||||
var last: JigsawPieceData;
|
||||
for( row in 0...rows ){
|
||||
last = { north: null, east: null, south: null, west: null };
|
||||
sides.push( new Array() );
|
||||
for( col in 0...cols ){
|
||||
var jigsawPiece = JigsawSideData.halfPieceData();
|
||||
if( last.east != null ) jigsawPiece.west = JigsawSideData.reflect( last.east );
|
||||
if( col == cols - 1 ) jigsawPiece.east = null;
|
||||
sides[ row ][ col ] = jigsawPiece;
|
||||
last = jigsawPiece;
|
||||
length++;
|
||||
}
|
||||
}
|
||||
for( col in 0...cols ){
|
||||
last = { north: null, east: null, south: null, west: null };
|
||||
for( row in 0...rows ){
|
||||
var jigsawPiece = sides[ row ][ col ];
|
||||
if( last.south != null ) jigsawPiece.north = JigsawSideData.reflect( last.south );
|
||||
if( row == rows - 1 ) jigsawPiece.south = null;
|
||||
last = jigsawPiece;
|
||||
}
|
||||
}
|
||||
var jig: JigsawPiece;
|
||||
for( row in 0...rows ){
|
||||
pieces.push( new Array() );
|
||||
for( col in 0...cols ){
|
||||
jig = new JigsawPiece( xy, row, col, lt, rt, rb, lb, sides[ row ][ col ] );
|
||||
pieces[ row ][ col ] = jig;
|
||||
jigs.push( jig );
|
||||
xy.x += dx;
|
||||
}
|
||||
xy.x = 20;
|
||||
xy.y += dy;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package jigsawx;
|
||||
import jigsawx.ds.CircleIter;
|
||||
import jigsawx.math.Vec2;
|
||||
class OpenEllipse {
|
||||
public var rotation: Float;
|
||||
public var beginAngle: Float;
|
||||
public var finishAngle: Float;
|
||||
public var stepAngle: Float;
|
||||
public var centre: Vec2;
|
||||
public var dimensions: Vec2;
|
||||
private var circleIter: CircleIter;
|
||||
private var _points: Array<Vec2>;
|
||||
public function new(){}
|
||||
public function getBegin(): Vec2 {
|
||||
return createPoint( centre, dimensions, beginAngle );
|
||||
}
|
||||
public function getFinish(): Vec2 {
|
||||
return createPoint( centre, dimensions, finishAngle );
|
||||
}
|
||||
public function getBeginRadius(){
|
||||
return pointDistance( centre, getBegin() );
|
||||
}
|
||||
public function getFinishRadius(){
|
||||
return pointDistance( centre, getFinish() );
|
||||
}
|
||||
private function pointDistance( A: Vec2, B: Vec2 ): Float {
|
||||
var dx = A.x - B.x;
|
||||
var dy = A.y - B.y;
|
||||
return Math.sqrt( dx*dx + dy*dy );
|
||||
}
|
||||
public function setUp(){
|
||||
circleIter = CircleIter.pi2pi( beginAngle, finishAngle, stepAngle );
|
||||
}
|
||||
public function getRenderList(): Array<Vec2> {
|
||||
_points = new Array();
|
||||
if( circleIter == null ) setUp();
|
||||
_points.push( createPoint( centre, dimensions, beginAngle ) );
|
||||
for( theta in CircleIter.pi2pi( beginAngle, finishAngle, stepAngle ).reset() ){
|
||||
_points.push( createPoint( centre, dimensions, theta ) );
|
||||
}
|
||||
return _points;
|
||||
}
|
||||
public function createPoint( centre: Vec2, dimensions: Vec2, theta: Float ): Vec2 {
|
||||
var offSetA = 3*Math.PI/2 - rotation;// arange so that angle moves from 0... could tidy up dxNew and dyNew!
|
||||
var dx = dimensions.x*Math.sin( theta );// select the relevant sin cos so that 0 is upwards.
|
||||
var dy = -dimensions.y*Math.cos( theta );
|
||||
var dxNew = centre.x -dx*Math.sin( offSetA ) + dy*Math.cos( offSetA );
|
||||
var dyNew = centre.y -dx*Math.cos( offSetA ) - dy*Math.sin( offSetA );
|
||||
return new Vec2( dxNew, dyNew );
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package jigsawx.ds;
|
||||
enum Sign{
|
||||
UP;
|
||||
DOWN;
|
||||
}
|
||||
class CircleIter {
|
||||
var begin: Float;
|
||||
var fin: Float;
|
||||
var step: Float;
|
||||
var min: Float;
|
||||
var max: Float;
|
||||
var current: Float;
|
||||
var onDirection: Sign;
|
||||
public static function pi2( begin_: Float
|
||||
, fin_: Float
|
||||
, step_: Float
|
||||
){
|
||||
return new CircleIter( begin_, fin_, step_, 0, 2*Math.PI );
|
||||
}
|
||||
public static function pi2pi( begin_: Float
|
||||
, fin_: Float
|
||||
, step_: Float
|
||||
){
|
||||
return new CircleIter( begin_, fin_, step_, -Math.PI, Math.PI );
|
||||
}
|
||||
public function new ( begin_: Float
|
||||
, fin_: Float
|
||||
, step_: Float
|
||||
, min_: Float
|
||||
, max_: Float
|
||||
){
|
||||
begin = begin_;
|
||||
current = begin;
|
||||
fin = fin_;
|
||||
step = step_;
|
||||
min = min_;
|
||||
max = max_;
|
||||
onDirection = ( step > 0 )? UP: DOWN;
|
||||
}
|
||||
public function reset(): CircleIter{
|
||||
current = begin;
|
||||
return this;
|
||||
}
|
||||
public function hasNext(): Bool {
|
||||
switch onDirection {
|
||||
case UP:
|
||||
return ( ( current < fin && current + step > fin ) || current == fin )? false: true;
|
||||
case DOWN:
|
||||
return ( ( current > fin && (( current - step ) < fin) )|| current == fin )? false: true;
|
||||
}
|
||||
}
|
||||
public function next() {
|
||||
current += step;
|
||||
switch onDirection{
|
||||
case UP: if( current > max ) current = min + current - max;
|
||||
case DOWN: if( current < min ) current = max + current - min;
|
||||
}
|
||||
if( !hasNext() ) return fin;
|
||||
return current;
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package jigsawx.math;
|
||||
class Vec2{
|
||||
public var x: Float;
|
||||
public var y: Float;
|
||||
public function new( x_ = .0, y_ = .0 ){
|
||||
x = x_;
|
||||
y = y_;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user