Merge JigsawX

This commit is contained in:
2022-06-26 19:53:26 +00:00
parent d739f2a6cf
commit 41af3009bb
48 changed files with 8654 additions and 0 deletions

View File

@@ -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;
}

View File

@@ -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 );
}
}

View File

@@ -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(){}
}

View File

@@ -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;
}
}
}

View File

@@ -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 );
}
}

View File

@@ -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;
}
}

View File

@@ -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_;
}
}