diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9b73521 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/src/haxe/ru/m/puzzlez/core/GameEvent.hx b/src/haxe/ru/m/puzzlez/core/GameEvent.hx index 9385a8a..ed13b58 100644 --- a/src/haxe/ru/m/puzzlez/core/GameEvent.hx +++ b/src/haxe/ru/m/puzzlez/core/GameEvent.hx @@ -1,5 +1,8 @@ package ru.m.puzzlez.core; +import flash.geom.Point; + enum GameEvent { START(state:GameState); + PART_MOVE(id:Int, position:Point); } diff --git a/src/haxe/ru/m/puzzlez/core/GamePreset.hx b/src/haxe/ru/m/puzzlez/core/GamePreset.hx index 1a4ae6f..83ef05c 100644 --- a/src/haxe/ru/m/puzzlez/core/GamePreset.hx +++ b/src/haxe/ru/m/puzzlez/core/GamePreset.hx @@ -1,6 +1,10 @@ package ru.m.puzzlez.core; +import flash.geom.Rectangle; + typedef GamePreset = { var image:String; var grid:Grid; + var tableRect:Rectangle; + var imageRect:Rectangle; } diff --git a/src/haxe/ru/m/puzzlez/core/GameState.hx b/src/haxe/ru/m/puzzlez/core/GameState.hx index 38fcd32..9b58af3 100644 --- a/src/haxe/ru/m/puzzlez/core/GameState.hx +++ b/src/haxe/ru/m/puzzlez/core/GameState.hx @@ -2,4 +2,5 @@ package ru.m.puzzlez.core; typedef GameState = { var preset:GamePreset; + var parts:Array; } diff --git a/src/haxe/ru/m/puzzlez/core/GameUtil.hx b/src/haxe/ru/m/puzzlez/core/GameUtil.hx index ee7c0b7..eac84a1 100644 --- a/src/haxe/ru/m/puzzlez/core/GameUtil.hx +++ b/src/haxe/ru/m/puzzlez/core/GameUtil.hx @@ -1,10 +1,37 @@ package ru.m.puzzlez.core; +import flash.geom.Rectangle; + class GameUtil { + public static function buildPreset(image:String):GamePreset { + return { + image:image, + grid: {width: 8, height: 8}, + tableRect: new Rectangle(0, 0, 0, 0), + imageRect: new Rectangle(0, 0, 2304, 2304), + } + } + public static function buildState(preset:GamePreset):GameState { + var parts:Array = []; + var partWidth = preset.imageRect.width / preset.grid.width; + var partHeight = preset.imageRect.height / preset.grid.height; + var offset = 5; + for (x in 0...preset.grid.width) { + for (y in 0...preset.grid.height) { + var id = (x << 16) + y; + parts.push({ + id: id, + gridX: x, + gridY: y, + rect: new Rectangle(offset + x * (partWidth + offset), offset + y * (partHeight + offset), partWidth, partHeight), + }); + } + } return { preset: preset, + parts: parts, } } } diff --git a/src/haxe/ru/m/puzzlez/core/Part.hx b/src/haxe/ru/m/puzzlez/core/Part.hx index c7cf989..d525821 100644 --- a/src/haxe/ru/m/puzzlez/core/Part.hx +++ b/src/haxe/ru/m/puzzlez/core/Part.hx @@ -1,6 +1,10 @@ package ru.m.puzzlez.core; +import flash.geom.Rectangle; + typedef Part = { - var x:Int; - var y:Int; + var id:Int; + var gridX:Int; + var gridY:Int; + var rect:Rectangle; } diff --git a/src/haxe/ru/m/puzzlez/render/IRender.hx b/src/haxe/ru/m/puzzlez/render/IRender.hx index 3822b50..e54ddd0 100644 --- a/src/haxe/ru/m/puzzlez/render/IRender.hx +++ b/src/haxe/ru/m/puzzlez/render/IRender.hx @@ -1,8 +1,10 @@ package ru.m.puzzlez.render; +import haxework.signal.Signal; import haxework.view.IView; import ru.m.puzzlez.core.GameEvent; interface IRender extends IView { + public var signal(default, null):Signal; public function onGameEvent(event:GameEvent):Void; } diff --git a/src/haxe/ru/m/puzzlez/render/PartView.hx b/src/haxe/ru/m/puzzlez/render/PartView.hx new file mode 100644 index 0000000..028a0c3 --- /dev/null +++ b/src/haxe/ru/m/puzzlez/render/PartView.hx @@ -0,0 +1,20 @@ +package ru.m.puzzlez.render; + +import flash.display.Bitmap; +import flash.display.BitmapData; +import flash.display.Sprite; + +class PartView extends Sprite { + + public var id(default, null):Int; + + private var bitmap:Bitmap; + + public function new(id:Int, image:BitmapData) { + super(); + this.id = id; + bitmap = new Bitmap(); + bitmap.bitmapData = image; + addChild(bitmap); + } +} diff --git a/src/haxe/ru/m/puzzlez/render/Render.hx b/src/haxe/ru/m/puzzlez/render/Render.hx index a01d887..f80c0f0 100644 --- a/src/haxe/ru/m/puzzlez/render/Render.hx +++ b/src/haxe/ru/m/puzzlez/render/Render.hx @@ -1,6 +1,6 @@ package ru.m.puzzlez.render; -import flash.display.Bitmap; +import haxework.signal.Signal; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.MouseEvent; @@ -12,67 +12,69 @@ import ru.m.puzzlez.core.GameState; class Render extends SpriteView implements IRender { + public var signal(default, null):Signal; + private var state:GameState; private var image:BitmapData; private var table:Sprite; - private var parts:Array; - private var activePart:Bitmap; + private var parts:Map; + private var activePart:PartView; private var activePoint:Point; public function new() { super(); + signal = new Signal(); table = new Sprite(); table.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); content.addChild(table); - parts = []; + parts = new Map(); } public function onGameEvent(event:GameEvent):Void { switch event { - case START(state): onStart(state); + case START(state): + onStart(state); + case PART_MOVE(id, point): + var part = parts[id]; + part.x = point.x; + part.y = point.y; } } private function onStart(state:GameState):Void { clean(); image = Assets.getBitmapData(state.preset.image); - image = RenderUtil.cropImage(image, state.preset.grid); - var partWidth = image.width / state.preset.grid.width + 2; - var partHeight = image.height / state.preset.grid.height + 2; - for (x in 0...state.preset.grid.width) { - for (y in 0...state.preset.grid.height) { - var partImage = RenderUtil.cropImagePart(image, state.preset.grid, x, y); - var bitmap = new Bitmap(partImage); - bitmap.x = 5 + partWidth * x; - bitmap.y = 5 + partHeight * y; - parts.push(bitmap); - table.addChild(bitmap); - } + image = RenderUtil.cropImage(image, state.preset.imageRect); + for (part in state.parts) { + var partImage = RenderUtil.cropImagePart(image, part.gridX, part.gridY, part.rect); + var partView = new PartView(part.id, partImage); + partView.x = part.rect.x; + partView.y = part.rect.y; + parts.set(part.id, partView); + table.addChild(partView); } - table.graphics.clear(); - table.graphics.beginFill(0xffff00, 1); - table.graphics.drawRect(0, 0, image.width, image.height); - table.graphics.endFill(); table.scaleX = table.scaleY = 0.25; } private function onMouseDown(event:MouseEvent):Void { - for (part in parts) { - if (part.x < event.localX && part.x + part.width > event.localX && part.y < event.localY && part.y + width > event.localY) { - activePart = part; - table.setChildIndex(activePart, table.numChildren - 1); - activePoint = new Point(event.localX - part.x, event.localY - part.y); - table.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); - table.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); - break; - } + if (Std.is(event.target, PartView)) { + activePart = event.target; + table.setChildIndex(activePart, table.numChildren - 1); + activePoint = table.globalToLocal(new Point(event.stageX, event.stageY)); + table.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); + table.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); } } private function onMouseMove(event:MouseEvent):Void { - activePart.x = event.localX - activePoint.x; - activePart.y = event.localY - activePoint.y; + var newPoint = table.globalToLocal(new Point(event.stageX, event.stageY)); + var partPosition = new Point( + activePart.x + newPoint.x - activePoint.x, + activePart.y + newPoint.y - activePoint.y + ); + signal.emit(GameEvent.PART_MOVE(activePart.id, partPosition)); + activePoint = newPoint; } private function onMouseUp(event:MouseEvent):Void { @@ -83,9 +85,9 @@ class Render extends SpriteView implements IRender { } private function clean() { - for (bitmap in parts) { - table.removeChild(bitmap); + for (partView in parts) { + table.removeChild(partView); } - parts = []; + parts = new Map(); } } diff --git a/src/haxe/ru/m/puzzlez/render/RenderUtil.hx b/src/haxe/ru/m/puzzlez/render/RenderUtil.hx index 4c4931b..8ce1152 100644 --- a/src/haxe/ru/m/puzzlez/render/RenderUtil.hx +++ b/src/haxe/ru/m/puzzlez/render/RenderUtil.hx @@ -1,41 +1,27 @@ package ru.m.puzzlez.render; -import flash.geom.Matrix; import flash.display.BitmapData; -import ru.m.puzzlez.core.Grid; +import flash.geom.Matrix; import flash.geom.Rectangle; class RenderUtil { - public static function buildRectangle(source:Rectangle, grid:Grid):Rectangle { - var r1 = grid.width / grid.height; - var r2 = source.width / source.height; - if (r1 > r2) { - var height = source.width * r1; - return new Rectangle(0, (source.height - height) / 2, source.width, height); - } else { - var width = source.height * r1; - return new Rectangle((source.width - width) / 2, 0, width, source.height); - } - } - - public static function cropImage(source:BitmapData, grid:Grid):BitmapData { - var rect = buildRectangle(new Rectangle(0, 0, source.width, source.height), grid); + public static function cropImage(source:BitmapData, rect:Rectangle):BitmapData { var image = new BitmapData(Std.int(rect.width), Std.int(rect.height)); var matrix = new Matrix(); - matrix.translate(-rect.x, -rect.y); + var scale = Math.max(rect.width / source.width, rect.height / source.height); + matrix.scale(scale, scale); + matrix.translate((rect.width - source.width * scale) / 2, (rect.height - source.height * scale) / 2); image.draw(source, matrix); return image; } - public static function cropImagePart(source:BitmapData, grid:Grid, x:Int, y:Int):BitmapData { - var partWidth = source.width / grid.width; - var partHeight = source.height / grid.height; - var rect = new Rectangle(partWidth * x, partHeight * y, partWidth, partHeight); - var image = new BitmapData(Std.int(partWidth), Std.int(partHeight)); + public static function cropImagePart(source:BitmapData, gridX:Int, gridY:Int, rect:Rectangle):BitmapData { + var rect = new Rectangle(rect.width * gridX, rect.height * gridY, rect.width, rect.width); + var image = new BitmapData(Std.int(rect.width), Std.int(rect.width)); var matrix = new Matrix(); matrix.translate(-rect.x, -rect.y); - image.draw(source, matrix); + image.draw(source, matrix, null, null, null, true); return image; } diff --git a/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.hx b/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.hx index 6100a67..9833fb4 100644 --- a/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.hx +++ b/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.hx @@ -1,12 +1,13 @@ package ru.m.puzzlez.view; -import haxework.view.utils.DrawUtil.FillType; import haxework.view.data.DataView; import haxework.view.group.VGroupView; import haxework.view.ImageView; +import haxework.view.utils.DrawUtil; import openfl.utils.Assets; import openfl.utils.AssetType; import ru.m.puzzlez.core.Game; +import ru.m.puzzlez.core.GameUtil; import ru.m.puzzlez.core.IGame; import ru.m.puzzlez.render.IRender; @@ -33,13 +34,15 @@ import ru.m.puzzlez.render.IRender; public function start(image:String):Void { stop(); - game = new Game({image:image, grid: {width: 7, height: 5}}); + game = new Game(GameUtil.buildPreset(image)); game.signal.connect(render.onGameEvent); + render.signal.connect(game.signal.emit); game.start(); } public function stop():Void { if (game != null) { + render.signal.disconnect(game.signal.emit); game.stop(); game.dispose(); game = null; diff --git a/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.yaml b/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.yaml index 2cfa446..3b083c2 100644 --- a/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.yaml +++ b/src/haxe/ru/m/puzzlez/view/PuzzlezAppView.yaml @@ -5,18 +5,21 @@ views: - $type: haxework.view.form.LabelView text: Puzzle'z font.size: 42 - - id: images - $type: haxework.view.data.DataView - layout: - $type: haxework.view.layout.HorizontalLayout - geometry.width: 100% - # geometry.height: 100 - factory: ~imageViewFactory - +onDataSelect: ~start - geometry.margin: 5 - - id: render - $type: ru.m.puzzlez.render.Render - geometry.width: 100% - geometry.height: 100% - geometry.margin: 5 - style: frame + - $type: haxework.view.group.HGroupView + geometry.stretch: true + views: + - id: images + $type: haxework.view.data.DataView + layout: + $type: haxework.view.layout.VerticalLayout + geometry.height: 100% + # geometry.height: 100 + factory: ~imageViewFactory + +onDataSelect: ~start + geometry.margin: 5 + - id: render + $type: ru.m.puzzlez.render.Render + geometry.width: 100% + geometry.height: 100% + geometry.margin: 5 + style: frame diff --git a/src/resources/avatar_2019.jpg b/src/resources/avatar_2019.jpg new file mode 100644 index 0000000..cc8c5c1 Binary files /dev/null and b/src/resources/avatar_2019.jpg differ