简体   繁体   中英

How to rotate an array of canvas rectangles

I'm creating a Pentomino puzzle game for a final project in a class I'm taking. I've created all dozen of the required puzzle pieces and can drag those around here . And I've tried this code to rotate the array (without using canvas.rotate() & located at the very bottom of the fiddle), it basically swaps the X & Y coordinates when drawing the new piece:

var newPiece = targetPiece;
pieces.splice(pieces.indexOf(targetPiece), 1);
targetPiece = null;
console.log(newPiece);
var geometry = [];
for (var i = 0; i < newPiece.geometry.length; i++) {
    geometry.push([newPiece.geometry[i][3], newPiece.geometry[i][0]]);
}
var offset = [newPiece.offset[1], newPiece.offset[0]];
console.log(geometry);
console.log(offset);
newPiece.geometry = geometry;
newPiece.position = geometry;
newPiece.offset = offset;
pieces.push(newPiece);
console.log(pieces);
for (var j = 0; j < pieces.length; j++) {
    draw(pieces[j]);
}

This doesn't work properly, but has promise.

In this fiddle , I've isolated the problem down to a single piece and tried to use canvas.rotate() to rotate the array by double clicking, but what's actually happening is it's rotating each piece of the array (I think), which results in nothing happening because each block of the array is just a 50x50 rectangle and when you rotate a square, it still looks just like a square.

function doubleClickListener(e) {
var br = canvas.getBoundingClientRect();
mouse_x = (e.clientX - br.left) * (canvas.width / br.width);
mouse_y = (e.clientY - br.top) * (canvas.height / br.height);
var pieceToggle = false;
for (var i = 0; i < pieces.length; i++) {
    if (onTarget(pieces[i], mouse_x, mouse_y)) {
        targetPiece = pieces[i];
        rotate(targetPiece);
    }
}
}

function rotate() {
targetPiece.rotationIndex = targetPiece.rotationIndex === 0 ?
1 : targetPiece.rotationIndex === 1 ?
2 : targetPiece.rotationIndex === 2 ?
3 : 0;
for (var j = 0; j < pieces.length; j++) {
    draw(pieces[j]);
}
}

Just FYI, I've tried creating the puzzle pieces as individual polygons, but could not figure out how to capture it with a mousedown event and move it with mousemove, so I abandoned it for the canvas rectangle arrays which were relatively simple to grab & move.

There's a brute force solution to this, and a total rewrite solution, both of which I'd rather avoid (I'm up against a deadline-ish). The brute force solution is to create geometry for all possible pieces (rotations & mirroring), which requires 63 separate geometry variants for the 12 pieces and management of those states. The rewrite would be to use fabric.js (which I'll probably do after class is over because I want to have a fully functional puzzle).

What I'd like to be able to do is rotate the array of five blocks with a double click (don't care which way it goes as long as it's sequential 90° rotations).


Approaching a usable puzzle:

With lots of help from @absolom , here's what I have, you can drag with a mouse click & drag, rotate a piece by double clicking it, and mirror a piece by right clicking it (well, mostly, it won't actually rotate until you next move the piece, I'm working on that). The Z-order of the pieces are manipulated so that the piece you're working with is always on top (it has to be the last one in the array to appear on top of all the other pieces):

Pentominoes II


The final solution

I've just handed the game in for grading, thanks for all the help! There was a lot more tweaking to be done, and there are still some things I'd change if I rewrite it, but I'm pretty happy with the result.

Pentominoes Final

Quick & Dirty:

The quick & dirty solution is when 2+ pieces are assembled you create a single image of them (using an in-memory canvas). That way you can move / rotate the 2-piece-as-1-image as a single entity.

More Proper:

If the 2+ piece assembly must later be disassembled, then you will need the more proper way of maintaining transformation state per piece. That more proper way is to assign a transformation matrix to each piece.

Stackoverflow contributor Ken Fyrstenberg (K3N) has coded a nice script which allows you to track individual polygons (eg your rects) using transformation matrices: https://github.com/epistemex/transformation-matrix-js

Does this code do what you need? The rotate method looks like this now:

   function rotate(piece) {
       for (i = 0; i < piece.geometry.length; i++) {
           var x = piece.geometry[i][0];
           var y = piece.geometry[i][2];
           piece.geometry[i][0] = -y;
           piece.geometry[i][3] = x;
       }
       drawAll();
   }

I simplified how your geometry and positioning was handled too. It's not perfect, but it can gives you some hints on how to handle your issues.

Please note that this solution works because each piece is composed of blocks with the same color and your rotations are 90 degrees. I only move the blocks around to simulate the rotation but nothing is rotated per-se. If you build your pieces differently or if you need to rotate at different angles, then you would need to go with another approach like transformation matrices.

UPDATE

Here is a better solution: fiddle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM