[improve] ClassicMaskBuilder

This commit is contained in:
2020-01-15 17:05:26 +03:00
parent 1f5a8de4be
commit fc9d43c938
9 changed files with 141 additions and 51 deletions

View File

@@ -20,9 +20,17 @@ class Game implements IGame {
} }
public function start():Void { public function start():Void {
shuffle();
signal.emit(GameEvent.START(state)); signal.emit(GameEvent.START(state));
} }
public function shuffle():Void {
for (part in state.parts) {
part.rect.x = Math.random() * (state.preset.tableRect.width - part.rect.width);
part.rect.y = Math.random() * (state.preset.tableRect.height - part.rect.height);
}
}
private static function distance(a:Point, b:Point):Float { private static function distance(a:Point, b:Point):Float {
var diff = a.subtract(b); var diff = a.subtract(b);
return Math.abs(diff.x) + Math.abs(diff.y); return Math.abs(diff.x) + Math.abs(diff.y);

View File

@@ -2,18 +2,76 @@ package ru.m.puzzlez.core;
import flash.geom.Point; import flash.geom.Point;
import flash.geom.Rectangle; import flash.geom.Rectangle;
import ru.m.puzzlez.core.Part.Bounds; import ru.m.puzzlez.core.Part;
import ru.m.puzzlez.core.Part.BoundType;
class BoundsMap {
private var grid:Grid;
private var bounds:Array<PartBound>;
public function new(grid:Grid) {
this.grid = grid;
bounds = [];
for (x in 0...grid.width + 1) {
for (y in 0...grid.height + 1) {
bounds.push(buildPartBound());
bounds.push(buildPartBound());
}
}
}
public function buildBound():BoundType {
return Math.random() > 0.5 ? BoundType.OUT : BoundType.IN;
}
public function buildPartBound():PartBound {
return {
spike: buildBound(),
side: buildBound(),
}
}
public function buildNonePartBound():PartBound {
return {
spike: NONE,
side: NONE,
}
}
public function revert(type:BoundType):BoundType {
return switch type {
case IN: OUT;
case OUT: IN;
case NONE: NONE;
}
}
public function getBound(x:Int, y:Int, side:Side):PartBound {
var index = switch side {
case TOP: x + (grid.width + 1) * (y * 2);
case LEFT: x + (grid.width + 1) * (y + 1);
case RIGHT: x + (grid.width + 1) * (y + 1) + 1;
case BOTTOM: x + (grid.width + 1) * (y * 2) + (grid.width + 1) * 2;
}
return switch side {
case TOP | LEFT: {spike: revert(bounds[index].spike), side: revert(bounds[index].side)};
case _: bounds[index];
}
}
}
class GameUtil { class GameUtil {
public static function buildPreset(image:ImageSource):GamePreset { public static function buildPreset(image:ImageSource):GamePreset {
var size = 8;
var offset = 100;
var imageRect = new Rectangle(offset, offset, 1024, 1024);
var tableRect = new Rectangle(0, 0, imageRect.width + offset * 2, imageRect.height + offset * 2);
return { return {
image:image, image:image,
grid: {width: 8, height: 8}, grid: {width: size, height: size},
tableRect: new Rectangle(0, 0, 1128, 1128), tableRect: tableRect,
//imageRect: new Rectangle(0, 0, 2304, 2304), imageRect: imageRect,
imageRect: new Rectangle(0, 0, 1024, 1024),
} }
} }
@@ -21,29 +79,27 @@ class GameUtil {
var parts:Array<Part> = []; var parts:Array<Part> = [];
var partWidth = preset.imageRect.width / preset.grid.width; var partWidth = preset.imageRect.width / preset.grid.width;
var partHeight = preset.imageRect.height / preset.grid.height; var partHeight = preset.imageRect.height / preset.grid.height;
var topLeft = new Point( var topLeft = preset.imageRect.topLeft;
(preset.tableRect.width - preset.imageRect.width) / 2, var boundsMap = new BoundsMap(preset.grid);
(preset.tableRect.height - preset.imageRect.height) / 2 for (y in 0...preset.grid.height) {
); for (x in 0...preset.grid.width) {
for (x in 0...preset.grid.width) {
for (y in 0...preset.grid.height) {
var bounds:Bounds = { var bounds:Bounds = {
left: BoundType.IN, left: boundsMap.getBound(x, y, LEFT),
right: BoundType.OUT, right: boundsMap.getBound(x, y, RIGHT),
top: BoundType.IN, top: boundsMap.getBound(x, y, TOP),
bottom: BoundType.OUT, bottom: boundsMap.getBound(x, y, BOTTOM),
} }
if (x == 0) { if (x == 0) {
bounds.left = BoundType.NONE; bounds.left = boundsMap.buildNonePartBound();
} }
if (y == 0) { if (y == 0) {
bounds.top = BoundType.NONE; bounds.top = boundsMap.buildNonePartBound();
} }
if (x == preset.grid.width - 1) { if (x == preset.grid.width - 1) {
bounds.right = BoundType.NONE; bounds.right = boundsMap.buildNonePartBound();
} }
if (y == preset.grid.height - 1) { if (y == preset.grid.height - 1) {
bounds.bottom = BoundType.NONE; bounds.bottom = boundsMap.buildNonePartBound();
} }
var id = (x << 16) + y; var id = (x << 16) + y;
var position = new Point(topLeft.x + x * partWidth, topLeft.y + y * partHeight); var position = new Point(topLeft.x + x * partWidth, topLeft.y + y * partHeight);

View File

@@ -3,17 +3,29 @@ package ru.m.puzzlez.core;
import flash.geom.Point; import flash.geom.Point;
import flash.geom.Rectangle; import flash.geom.Rectangle;
enum Side {
TOP;
LEFT;
RIGHT;
BOTTOM;
}
enum BoundType { enum BoundType {
NONE; NONE;
OUT; OUT;
IN; IN;
} }
typedef PartBound = {
var spike: BoundType;
var side: BoundType;
}
typedef Bounds = { typedef Bounds = {
var left:BoundType; var left:PartBound;
var right:BoundType; var right:PartBound;
var top:BoundType; var top:PartBound;
var bottom:BoundType; var bottom:PartBound;
} }
typedef Part = { typedef Part = {

View File

@@ -76,6 +76,13 @@ class Render extends SpriteView implements IRender {
table.graphics.drawRect(state.preset.tableRect.x, state.preset.tableRect.y, state.preset.tableRect.width, state.preset.tableRect.height); table.graphics.drawRect(state.preset.tableRect.x, state.preset.tableRect.y, state.preset.tableRect.width, state.preset.tableRect.height);
table.graphics.endFill(); table.graphics.endFill();
table.graphics.lineStyle(); table.graphics.lineStyle();
table.graphics.lineStyle(2, 0xCCCCCC);
table.graphics.beginFill(0x777777);
table.graphics.drawRect(state.preset.imageRect.x, state.preset.imageRect.y, state.preset.imageRect.width, state.preset.imageRect.height);
table.graphics.endFill();
table.graphics.lineStyle();
toUpdate(); toUpdate();
} }

View File

@@ -18,9 +18,9 @@ class RenderUtil {
public static function resolveImage(source:ImageSource):Promise<BitmapData> { public static function resolveImage(source:ImageSource):Promise<BitmapData> {
return switch source { return switch source {
case ASSET(name): case ASSET(name):
return Promise.promise(Assets.getBitmapData(name)); Promise.promise(Assets.getBitmapData(name));
case URL(url): case URL(url):
return new ImageLoader().GET(url); new ImageLoader().GET(url);
} }
} }

View File

@@ -11,10 +11,13 @@ class BaseMaskBuilder implements IPartMaskBuilder {
public function new() { public function new() {
} }
private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, type:BoundType):Void { private function createAngle(path:DrawPath, x:Float, y:Float):Void {
path.commands.push(GraphicsPathCommand.LINE_TO); path.commands.push(GraphicsPathCommand.LINE_TO);
path.data.push(toX); path.data.push(x);
path.data.push(toY); path.data.push(y);
}
private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, bound:PartBound):Void {
} }
public function build(rect:Rectangle, bounds:Bounds):DrawPath { public function build(rect:Rectangle, bounds:Bounds):DrawPath {
@@ -22,13 +25,15 @@ class BaseMaskBuilder implements IPartMaskBuilder {
commands: new Vector<GraphicsPathCommand>(), commands: new Vector<GraphicsPathCommand>(),
data: new Vector<Float>(), data: new Vector<Float>(),
} }
path.commands.push(GraphicsPathCommand.MOVE_TO); createAngle(path, rect.left, rect.top);
path.data.push(rect.left);
path.data.push(rect.top);
createBound(path, rect.left, rect.top, rect.right, rect.top, bounds.top); createBound(path, rect.left, rect.top, rect.right, rect.top, bounds.top);
createAngle(path, rect.right, rect.top);
createBound(path, rect.right, rect.top, rect.right, rect.bottom, bounds.right); createBound(path, rect.right, rect.top, rect.right, rect.bottom, bounds.right);
createAngle(path, rect.right, rect.bottom);
createBound(path, rect.right, rect.bottom, rect.left, rect.bottom, bounds.bottom); createBound(path, rect.right, rect.bottom, rect.left, rect.bottom, bounds.bottom);
createAngle(path, rect.left, rect.bottom);
createBound(path, rect.left, rect.bottom, rect.left, rect.top, bounds.left); createBound(path, rect.left, rect.bottom, rect.left, rect.top, bounds.left);
createAngle(path, rect.left, rect.top);
return path; return path;
} }
} }

View File

@@ -6,34 +6,38 @@ import ru.m.puzzlez.render.mask.IPartMaskBuilder;
class ClassicMaskBuilder extends BaseMaskBuilder { class ClassicMaskBuilder extends BaseMaskBuilder {
override private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, type:BoundType):Void { override private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, bound:PartBound):Void {
var dx = toX == fromX ? 0 : (toX - fromX) / Math.abs(toX - fromX); var dx = toX == fromX ? 0 : (toX - fromX) / Math.abs(toX - fromX);
var dy = toY == fromY ? 0 : (toY - fromY) / Math.abs(toY - fromY); var dy = toY == fromY ? 0 : (toY - fromY) / Math.abs(toY - fromY);
var boundLength = Math.max(Math.abs(toX - fromX), Math.abs(toY - fromY)); var boundLength = Math.max(Math.abs(toX - fromX), Math.abs(toY - fromY));
var spikeWidth = boundLength * 0.2; var spikeWidth = boundLength * 0.2;
var spikeOffset = (boundLength - spikeWidth) / 2; var spikeOffset = (boundLength - spikeWidth) / 2;
var spikeDepth = switch type { var spikeDepth = switch bound.spike {
case NONE: 0; case NONE: 0;
case IN: spikeWidth; case IN: spikeWidth;
case OUT: -spikeWidth; case OUT: -spikeWidth;
}; };
switch type { var spikeSideOffset = switch bound.side {
case NONE: 0;
case IN: boundLength * -0.04;
case OUT: boundLength * 0.04;
}
switch bound.spike {
case NONE: case NONE:
case IN | OUT: case IN | OUT:
path.commands.push(GraphicsPathCommand.LINE_TO); path.commands.push(GraphicsPathCommand.LINE_TO);
path.data.push(fromX + spikeOffset * dx); path.data.push(fromX + spikeOffset * dx + spikeSideOffset * dy);
path.data.push(fromY + spikeOffset * dy); path.data.push(fromY + spikeOffset * dy + spikeSideOffset * dx);
path.commands.push(GraphicsPathCommand.CURVE_TO); path.commands.push(GraphicsPathCommand.CURVE_TO);
path.data.push(fromX + (spikeOffset * 0.8) * dx - spikeDepth * dy); path.data.push(fromX + (spikeOffset * 0.8) * dx - spikeDepth * dy + spikeSideOffset * dy);
path.data.push(fromY + spikeDepth * dx + (spikeOffset * 0.8) * dy); path.data.push(fromY + spikeDepth * dx + (spikeOffset * 0.8) * dy + spikeSideOffset * dx);
path.data.push(fromX + (spikeOffset + spikeWidth / 2) * dx - (spikeDepth * 1.1) * dy); path.data.push(fromX + (spikeOffset + spikeWidth / 2) * dx - (spikeDepth * 1.1) * dy + spikeSideOffset * dy);
path.data.push(fromY + (spikeDepth * 1.1) * dx + (spikeOffset + spikeWidth / 2) * dy); path.data.push(fromY + (spikeDepth * 1.1) * dx + (spikeOffset + spikeWidth / 2) * dy + spikeSideOffset * dx);
path.commands.push(GraphicsPathCommand.CURVE_TO); path.commands.push(GraphicsPathCommand.CURVE_TO);
path.data.push(fromX + (spikeOffset + spikeWidth + spikeOffset * 0.2) * dx - spikeDepth * dy); path.data.push(fromX + (spikeOffset + spikeWidth + spikeOffset * 0.2) * dx - spikeDepth * dy + spikeSideOffset * dy);
path.data.push(fromY + spikeDepth * dx + (spikeOffset + spikeWidth + spikeOffset * 0.2) * dy); path.data.push(fromY + spikeDepth * dx + (spikeOffset + spikeWidth + spikeOffset * 0.2) * dy + spikeSideOffset * dx);
path.data.push(fromX + (spikeOffset + spikeWidth) * dx); path.data.push(fromX + (spikeOffset + spikeWidth) * dx + spikeSideOffset * dy);
path.data.push(fromY + (spikeOffset + spikeWidth) * dy); path.data.push(fromY + (spikeOffset + spikeWidth) * dy + spikeSideOffset * dx);
} }
super.createBound(path, fromY, fromY, toX, toY, type);
} }
} }

View File

@@ -4,7 +4,6 @@ import flash.display.Shape;
import flash.geom.Rectangle; import flash.geom.Rectangle;
import ru.m.puzzlez.core.Part; import ru.m.puzzlez.core.Part;
class PartMask extends Shape { class PartMask extends Shape {
public var rect(default, null):Rectangle; public var rect(default, null):Rectangle;

View File

@@ -6,18 +6,18 @@ import ru.m.puzzlez.render.mask.IPartMaskBuilder;
class SquareMaskBuilder extends BaseMaskBuilder { class SquareMaskBuilder extends BaseMaskBuilder {
override private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, type:BoundType):Void { override private function createBound(path:DrawPath, fromX:Float, fromY:Float, toX:Float, toY:Float, bound:PartBound):Void {
var dx = toX == fromX ? 0 : (toX - fromX) / Math.abs(toX - fromX); var dx = toX == fromX ? 0 : (toX - fromX) / Math.abs(toX - fromX);
var dy = toY == fromY ? 0 : (toY - fromY) / Math.abs(toY - fromY); var dy = toY == fromY ? 0 : (toY - fromY) / Math.abs(toY - fromY);
var boundLength = Math.max(Math.abs(toX - fromX), Math.abs(toY - fromY)); var boundLength = Math.max(Math.abs(toX - fromX), Math.abs(toY - fromY));
var spikeWidth = boundLength * 0.2; var spikeWidth = boundLength * 0.2;
var spikeOffset = (boundLength - spikeWidth) / 2; var spikeOffset = (boundLength - spikeWidth) / 2;
var spikeDepth = switch type { var spikeDepth = switch bound.spike {
case NONE: 0; case NONE: 0;
case IN: spikeWidth; case IN: spikeWidth;
case OUT: -spikeWidth; case OUT: -spikeWidth;
} }
switch type { switch bound.spike {
case NONE: case NONE:
case IN | OUT: case IN | OUT:
path.commands.push(GraphicsPathCommand.LINE_TO); path.commands.push(GraphicsPathCommand.LINE_TO);
@@ -33,6 +33,5 @@ class SquareMaskBuilder extends BaseMaskBuilder {
path.data.push(fromX + (spikeOffset + spikeWidth) * dx); path.data.push(fromX + (spikeOffset + spikeWidth) * dx);
path.data.push(fromY + (spikeOffset + spikeWidth) * dy); path.data.push(fromY + (spikeOffset + spikeWidth) * dy);
} }
super.createBound(path, fromY, fromY, toX, toY, type);
} }
} }