createjs struggling with hitTest() - javascript

I am populating the canvas with small circles to form a shape and would like to destroy some of the circles where ever user clicks on the screen using the following function.
dotsArray : is the array of all circles
dot.ball: is the circle being drawn
bomb: is the circle drawn using user input being taken by the mouse click and hold.The bomb scales up in size as the mouse is pressed
function onMouseDown(event){
bomb.x = event.stageX;
bomb.y = event.stageY;
bomb.active = true;
}
function decimateBalls(){
for (var i = 0; i < dotsArray.length; i++){
for ( var j = 0; j < dotsArray[i].length; j++){
var dot = dotsArray[i][j];
var pt = dot.ball.localToLocal(dot.ball.x, dot.ball.y,bomb);
if(bomb.hitTest(pt.x,pt.y)){
dot.setType("empty"); // this changes dot's circle to white making it seem invisible
}
}
}
}
When I use the above code, even though i calculate the location of the small dots with respect to the bomb circle being drawn, the dots that disappear are the ones that are a lot offset from the bomb circle being drawn.
Am i doing something obviously wrong? Is there a better way to approach this problem?
Thanks a lot for your time.

I used the following work around to get the desired result.
made sure everything is added to stage. got rid of containers.
Use if(bomb.hitTest(dot.ball.x - bomb.x,dot.ball.y - bomb.y)) and got rid of localToLocal() function provided by createjs to find local coordinates of an object with respect to another object.
Hope this helps someone who comes across a similar issue.

Related

How to make sure that a shape has straight corners

i'm looking for a way to draw a shape on an image, get the coordinates, then make minor corrections to have a perfect square or diamond.
I have a floor plan and i'm trying to highlight certain areas. Since i'm drawing over the image, i can't always get it a perfect shape and manual corrections takes time.
I would like to draw over the image, then pass it to a functiton which makes sure that each angles are 90 degrees, or it fixes the coordinates. The shape is always gonna be a square or a rectangle. All I want are the corrected coordinates so they can be saved in a file.
Here's a sample of the image i'm working with, along with one location:
var loc6 = [{x:711, y:596}, {x:787, y:595}, {x:784, y:641}, {x:711, y:641}];
https://imagebin.ca/v/2zjcnTJCRL2n
Math isn't my biggest asset, i've got the function below, but it only works to make a perfect square on squares, and messes up diamonds.
function fixRectangle(coords){
var precision = 8;
if(coords[0].y - coords[1].y < precision){
coords[0].y = coords[1].y;
}
if(coords[1].x - coords[0].x < precision){
coords[0].x = coords[1].x;
}
if(coords[2].y - coords[3].y < precision){
coords[2].y = coords[3].y;
}
if(coords[2].x - coords[3].x < precision){
coords[2].x = coords[3].x;
}
return coords;
}
(I do use jquery in case it is required)
Thanks for your help !

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

How to place one element inside another in raphael js?

Im trying to create an interactive seating layout like this Seats.io. However I dont need the exact features but just few things such as:
Plotting seats anywhere on the screen
Plotting list of seats from one point to another
Seats hover as circle when plotting from one mouse click point to another
After much research in Jquery and simultaneously on raphaeljs, I have decided to start working with raphaeljs. Im totally new to the vector graphics. So obviously there might be something that I may be missing. I have followed this fiddle to draw a straight line. I have also created another script to plot circles anywhere on the window(the circles will mean seats) following is the script
window.onload = function () {
var height = $(document).outerHeight(true);
var width = $(document).width();
var radius = 10;
var paper = Raphael(0, 0, width, height);
var i = 0;
$(document).click(function (e) {
i = i + 1;
var x = e.pageX;
var y = e.pageY;
var seat = paper.circle(x, y, radius)
.attr({stroke: "none", fill: "#f00", opacity: .4})
.data("i", i);
seat.mouseover(function () {
this.attr("opacity", 1);
});
seat.mouseout(function () {
this.attr("opacity", .4);
});
});
}
using the above script I'm able to plot circles(seats) on my screen. Now based on the fiddle example lines are drawn using 'path', so is it possible to load circles on every path and draw them as sequential line of circles one after the other, or do I have to take any different approach.
Also on a side note is there any opensource project or code for the Seats.io
Any help would be really appreciated
Ben from seats.io here.
http://raphaeljs.com/reference.html#Element.getPointAtLength is indeed what we use. You'll basically need to
calculate a helper path between start and end point. You already have that.
calculate the distance between seats (based on seat size): helperPath.getTotalLength() / (numberOfSeats - 1);
for each seat, call getPointAtLength and draw a circle around that
point: helperPath.getPointAtLength(distanceBetweenSeatsOnHelperPath * i++)
Obviously, it gets more interesting if you want to snap to a grid to align rows, curve rows, etc, but you should be able to get started with the above.

Javascript: Simple Particle Motion, Particle Elastically Bouncing Off Other Particle

I've created this rather simple javascript; balls or 'molecules' moving around the screen. I was hoping to add to the functionality that when one ball comes into contact with another, they swap velocities. We don't need to worry about any angles, just when they come into contact with each other, the velocities swap. (Instead of changing the velocities though, in the code linked I've just coded a colour change)
I've been trying to call the function 'someplace' to recognise when the molecules touch, but I've had no luck with that. I don't really understand why.
Link to code:
http://jsbin.com/arokuz/5/
There seems to be three main problems:
The molecules seem to be randomly changing, rather than when two molecules touch.
When one sets the array to have say, 3 molecules, only two appear, the first is actually there, but unresponsive to .fillstyle changes, so invisible against the canvas
With the function method I would only be able to recognise when molecules in series (1 and 2 or 4 and 5) in the array touch...how could I check all the molecules?
You are only comparing a molecule with 2 other ones, which in fact might be anywhere.
Collision detection is a topic quite hard to solve, but if you want to have your idea
working quickly you might go for a n^2 algorithm with 2 nested for loops.
the code is quite expected :
// collision
for(var t = 0; t < molecules.length-1; t++)
for(var tt = t+1; tt < molecules.length; tt++) {
var p1 = molecules[t];
var p2 = molecules[tt];
if (sq(p1.x-p2.x) +sq(p1.y-p2.y) < sq(p1.radius+p2.radius) )
{
p1.collided = 8; // will diplay for next 8 frames
p2.collided = 8; // .
}
}
the fiddle is here :
http://jsbin.com/arokuz/10
The reason only two appear when three are made isn't because the first one doesn't render it is rather the last one doesn't, this is because of how you draw them by comparing its distance with the next one in the list - as it is the last there is no next and thus throws a null error and continues (check the console).
The reason why they seem to "randomly" detect collisions or not is because they are not checking against all other molecules - only the next in the list, unfortunately the only simply way to do it would be to go through all other balls for every ball and checking.
To get the molecules to detect distance you could use the pythagorean theorem, I typically use it such as:
var distx = Math.abs(molecule1.x - molecule2.x);
var disty = Math.abs(molecule1.x - molecule2.y);
var mindist = molecule1.radius + molecule2.radius;
return Math.sqrt(distx*distx+disty*disty) < mindist;

Why is my canvas fill pattern repeating/overlapping itself?

I've drawn a grid of rounded rectangles, and I need to fill them with an image background. Eventually I'll have many image backgrounds, but for now, I'm trying to get it to work with one. I'm close, my rectangles draw but the fill does something a little whacky - it overlaps itself and kills my pattern (except on the edges) basically filling the whole canvas with the image. I've tried to 'clip' my path but that just causes only one rectangle to fill. I'm not sure what I'm doing wrong, and I'm hoping a canvas expert can spot it?
/* build rounded rectangle */
var roundedRect=function(ctx,x,y,width,height,radius,fill,stroke)
{
ctx.save(); // save the context so we don't mess up others ctx.beginPath();
// draw top and top right corner ctx.moveTo(x+radius,y);
ctx.arcTo(x+width,y,x+width,y+radius,radius);
// draw right side and bottom right corner
ctx.arcTo(x+width,y+height,x+width-radius,y+height,radius);
// draw bottom and bottom left corner
ctx.arcTo(x,y+height,x,y+height-radius,radius);
// draw left and top left corner
ctx.arcTo(x,y,x+radius,y,radius);
ctx.clip();
if(fill){ ctx.fill(); }
if(stroke){ ctx.stroke(); }
ctx.restore(); // restore context to what it was on entry
}
/* onload, fill canvas with pattern of rounded rectangles separated by 2px */
window.onload = function() {
var canvas = document.getElementById("canvas");
/* rounded filled rectangle pattern */
var canvasWidth=530;
var canvasHeight=530;
var recWidth=42;
var recHeight=42;
var grout=2;
var radius=2;
var cols=canvasWidth/(recWidth+grout);
var rows=canvasWidth/(recHeight+grout);
/* loop through each row/column to build pattern */
alert("rows" + rows + " & cols " + cols);
for (i=1; i<rows; i++){
for (j=1; j<cols; j++){
var ctx = canvas.getContext("2d");
/* fill pattern */
var img=document.getElementById("poppy");
var pat=ctx.createPattern(img,"repeat");
ctx.fillStyle=pat;
roundedRect(ctx,(j*grout + (j-1)*recWidth),(i*grout + (i-1)*recHeight),recWidth,recHeight,radius,true,false);
}
}
};
Let's assume that your problem is reproduced in this JSFiddle: http://jsfiddle.net/hBEwr/. (If this is not the case, the rest of this answer is ~bunk; please edit to match your exact problem.)
If we remove the pattern from the equation altogether, we can see that it is not directly to blame.
http://jsfiddle.net/hBEwr/1/
Instead, the rounded rects are not leaving the grout necessary between them.
Since everything is running over each other, we then change the fill color to a low-opacity blue. Interesting!
http://jsfiddle.net/hBEwr/2/
We see that many of the paths are getting drawn again and again. This insight leads us to look at the implementation of roundedRect() more closely and notice that we never call beginPath(). Indeed, in your code from the question above it seems to have gotten eaten by a comment. Without this call, each invocation of roundedRect() adds additional rectangles to an ever-growing path, instead of starting over.
Adding the call to beginPath() back in, we see that we are on the path to success:
http://jsfiddle.net/hBEwr/3/
Now we can add back in the pattern (with some slight changes) and achieve glorious victory:
http://jsfiddle.net/hBEwr/4/
Some notes on other small tweaks I made:
Make sure you always var your local variables:
for (i=0; i<10; i++) // Oops! i is a global variable
for (var i=0; i<10; i++) // Much better
It's inefficient to re-get the ctx, img, and create the pattern for each tile. In the final code above I've moved these outside the loops for simplicity and speed.
Using console.log instead of alert for debugging is often far easier. (Open the developer console to see the output.)
I added the final outer row/col of grout the the column calculations to ensure that there is space for it to be included. Use or not as you like.

Categories