255 lines
12 KiB
Haxe
255 lines
12 KiB
Haxe
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 wh: Vec2;
|
|
public var row: Int;
|
|
public var col: Int;
|
|
public function new( xy_: Vec2
|
|
, row: Int
|
|
, col: Int
|
|
, bubbleSize: Float
|
|
, lt: Vec2, rt: Vec2, rb: Vec2, lb: Vec2
|
|
, sideData_: JigsawPieceData
|
|
){
|
|
enabled = true;
|
|
xy = new Vec2( xy_.x, xy_.y );
|
|
this.row = row;
|
|
this.col = col;
|
|
sideData = sideData_;
|
|
points = [];
|
|
stepAngle = JigsawMagicNumbers.stepSize*Math.PI/180;
|
|
first = lt;
|
|
|
|
lt = lt.copy();
|
|
rt = rt.copy();
|
|
lb = lb.copy();
|
|
rb = rb.copy();
|
|
var edgeLeeway = lt.x;
|
|
if (sideData.north == null) {
|
|
lt.y = 0;
|
|
rt.y = 0;
|
|
}
|
|
if (sideData.east == null) {
|
|
rt.x += edgeLeeway;
|
|
rb.x += edgeLeeway;
|
|
}
|
|
if (sideData.south == null) {
|
|
lb.y += edgeLeeway;
|
|
rb.y += edgeLeeway;
|
|
}
|
|
if (sideData.west == null) {
|
|
lt.x = 0;
|
|
lb.x = 0;
|
|
}
|
|
|
|
// NORTH side
|
|
if( sideData.north != null ) createVertSide( lt, rt, bubbleSize, sideData.north, NORTH );
|
|
points.push( rt );
|
|
// EAST side
|
|
if( sideData.east != null ) createHoriSide( rt, rb, bubbleSize, sideData.east, EAST );
|
|
points.push( rb );
|
|
// SOUTH side
|
|
if( sideData.south != null ) createVertSide( rb, lb, bubbleSize, sideData.south, SOUTH );
|
|
points.push( lb );
|
|
// WEST side
|
|
if( sideData.west != null ) createHoriSide( lb, lt, bubbleSize, sideData.west, WEST );
|
|
points.push( lt );
|
|
|
|
var minX = Math.POSITIVE_INFINITY;
|
|
var minY = Math.POSITIVE_INFINITY;
|
|
var maxX = 0.0;
|
|
var maxY = 0.0;
|
|
for (point in points) {
|
|
if (point.x < minX)
|
|
minX = point.x;
|
|
if (point.y < minY)
|
|
minY = point.y;
|
|
if (point.x > maxX)
|
|
maxX = point.x;
|
|
if (point.y > maxY)
|
|
maxY = point.y;
|
|
}
|
|
wh = new Vec2(maxX, maxY);
|
|
// Crop the points so we don't make a bigger source rectangle than needed,
|
|
// and the origin will align with center of mass better
|
|
xy.add(minX, minY);
|
|
wh.subtract(minX, minY);
|
|
points = [for (point in points) {
|
|
point.copy().subtract(minX, minY);
|
|
}];
|
|
for (compass => points in bubblePoints) {
|
|
if (points != null) {
|
|
bubblePoints[compass] = [for (point in points) point.copy().subtract(minX, minY)];
|
|
}
|
|
}
|
|
}
|
|
|
|
public function getPoints(): Array<Vec2> {
|
|
return points;
|
|
}
|
|
public function getFirst(): Vec2 {
|
|
return first;
|
|
}
|
|
private function createVertSide( A: Vec2
|
|
, B: Vec2
|
|
, bubbleSize: Float
|
|
, 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 )
|
|
, bubbleSize
|
|
, side
|
|
, compass
|
|
);
|
|
}
|
|
private function createHoriSide ( A: Vec2
|
|
, B: Vec2
|
|
, bubbleSize: Float
|
|
, 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 )
|
|
, bubbleSize
|
|
, side
|
|
, compass
|
|
);
|
|
}
|
|
|
|
public var bubblePoints:Map<Compass,Array<Vec2>> = [];
|
|
private function drawSide( dx: Float, dy: Float, bubbleSize: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 + bubbleSize*switch bubble{ case IN: 1; case OUT: -1; } );
|
|
case EAST: new Vec2( dx - bubbleSize*switch bubble{ case IN: 1; case OUT: -1; }, dy );
|
|
case SOUTH: new Vec2( dx, dy - bubbleSize*switch bubble{ case IN: 1; case OUT: -1; } );
|
|
case WEST: new Vec2( dx + bubbleSize*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();
|
|
|
|
bubblePoints[compass] = secondPoints;
|
|
|
|
points = points.concat( thirdPoints );
|
|
}
|
|
}
|