I have several images already loaded on a canvas and i want to add simple collision detection (when any of them collide, show a message or change the color, etc). I'm loading the images by way of:
// Taurus Image
var taurusImg = new Kinetic.Image({
x: 0,
y: 0,
image: images.Taurus,
width: 216,
height: 75,
name: 'image'
});
taurusGroup.add(taurusImg);
taurusGroup.on('dragstart', function() {
this.moveToTop();
});
// Truck Image
var truckImg = new Kinetic.Image({
x: 0,
y: 0,
image: images.Truck,
width: 950,
height: 158,
name: 'image'
});
truckGroup.add(truckImg);
truckGroup.on('dragstart', function() {
this.moveToTop();
});
And i'm loading them in the sources:
var sources = {
Taurus: 'content/taurus.gif',
Truck: 'content/truck2.gif'
};
after calling a function loadimages i thought i could use something like this:
function collides(a, b)
{
if (a.x < b.x + b.width &&
a.x + a.width > b.x &&
a.y < b.y + b.height &&
a.y + a.height > b.y) return true;
}
But nothings happening. Do i need to load the images another way? As i said they are all on the screen already and i'm able to drag them around i just can't get collision detection working. Thank you so much for any help/advice you guys might have!!
A couple possible issues here:
1. Your collides function isn't referencing the correct object properties
If you're passing the Kinetic objects themselves, you'll want to use the accessor methods:
function rectanglesOverlap(r1, r2) {
return (r1.x() < r2.x() + r2.width() &&
r1.x() + r1.width() > r2.x() &&
r1.y() < r2.y() + r2.height() &&
r1.y() + r1.height() > r2.y());
}
2. Are you running your collision detection?
layer.find('.image').on('dragmove', function() {
detectCollisions();
});
function detectCollisions() {
var images = layer.find('.image');
images.stroke(null);
for (var i=0; i<images.length; i++) {
for (var j=i+1; j<images.length; j++) {
if ( rectanglesOverlap(images[i], images[j]) ) {
images[i].stroke('red');
images[j].stroke('red');
}
}
}
}
Here's a fiddle demonstrating collision detection (it substitutes rectangles for your images):
http://jsfiddle.net/klenwell/z6zTA/
Related
How can I add a click event on pushpins to autodesk forge? In an existing extension, he opens the content for pushpin using the camera focus on it. I want to open it with click on pushpin.
But I add the pushpini to the model manually and translate the coordinates with the following code
var setData = function (event) {
var screenPoint = {
x: event.clientX,
y: event.clientY
};
if (screenPoint) {
var n = normalize(screenPoint);
var dbId = /*_viewer.utilities.getHitPoint*/ getHitDbId(n.x, n.y);
if (dbId == null) return;
}
}
function getHitDbId(x, y) {
y = 1.0 - y;
x = x * 2.0 - 1.0;
y = y * 2.0 - 1.0;
var vpVec = new THREE.Vector3(x, y, 1);
var result = viewer.impl.hitTestViewport(vpVec, false);
console.log(result);
if (result) {
dummyData.push({
icon: Math.round(Math.random() * 3),
x: result.point.x,
y: result.point.y,
z: result.point.z,
});
window.dispatchEvent(new CustomEvent('newData', {
'detail': dummyData
}))
} else {
return
}
return result ? result.dbId : null;
};
function normalize(screenPoint) {
var viewport = viewer.navigation.getScreenViewport();
var n = {
x: (screenPoint.x - viewport.left) / viewport.width,
y: (screenPoint.y - viewport.top) / viewport.height
};
return n;
}
Is this even possible?
This has already been taken care of in the original plugin - simply append your own handler in the click callbacks below where hit test on the markups is performed:
this.onMouseMove = function(event) {
if(this.line3d){
this.update_DivLabel('onMarkupMove');
this.updateHitTest(event);
}
}
this.onClick = function(event) {
this.updateHitTest(event);
if (!this.hovered) return;
this.selected = this.hovered; //
//your own callback goes here
}
And adjust here to fine tune hit test accuracy:
function markup3d(viewer, options) {
this.raycaster.params.PointCloud.threshold = 5; // hit-test markup size. Change this if markup 'hover' doesn't work
this.size = 150.0; // markup size. Change this if markup size is too big or small
The original live sample handles click as well by displaying a hoevering label:
I want to realize pushpin extension for autodesk forge with point cloud, but with custom coordinates. I want to get custom coordinates on model click event. I cant normalize the points so that they appear where I clicked.
I try to normalize points with this code, but its not working.
viewer.canvas.addEventListener( 'click', (event) => {
var screenPoint = {
x: event.clientX,
y: event.clientY
};
var n = normalize(screenPoint);
var dbId = /*_viewer.utilities.getHitPoint*/ getHitDbId(n.x, n.y);
if (dbId == null) return;
})
function getHitDbId(x, y) {
x = x * 2.0 - 1.0;
y = y * 2.0 - 1.0;
var vpVec = new THREE.Vector3(x, y, 0.5);
var result = viewer.impl.hitTestViewport(vpVec, false);
result.distance = 1;
if(result){
dummyData.push({
icon: Math.round(Math.random()*3),
x: result.point.x,
y: result.point.y,
z: result.point.z,
});
window.dispatchEvent(new CustomEvent('newData', {
'detail': dummyData
}))
} else {
return
}
};
function normalize(screenPoint) {
var viewport = viewer.navigation.getScreenViewport();
var n = {
x: (screenPoint.x - viewport.left) / viewport.width,
y: (screenPoint.y - viewport.top) / viewport.height
};
console.log(n);
return n;
}
Edited answer.
Now i have another problem after when i normalize offset. Some pushpins are appearing incorrect. You can see problem in picture.
How can i fix it?
You can find the sample code for finding the corresponding world coordinates in the Forge Digital Twin code: https://github.com/petrbroz/forge-digital-twin/blob/master/public/scripts/extensions/issues.js.
Live demo: http://forge-digital-twin.autodesk.io/ (try the flag icon in the toolbar).
"Hero" needs to get flipped horizontally (coordinate or new sprite, doesn't matter) on left/right keyDown. Including draw and keyDown sections. Where exactly would coordinate offset, or new sprite go, when left/right keyDown? Thanks in advance.
var heroReady = false;
var heroImage = new Image();
heroImage.onload = function () {
heroReady = true;
};
heroImage.src = "hero.png";
var owlReady = false;
var owlImage = new Image();
owlImage.onload = function () {
owlReady = true;
};
owlImage.src = "owl.png";
var hero = {
speed: 256,
x: 0,
y: 0
};
var owl = {
x: 0,
y: 0
};
var keysDown = {};
addEventListener("keydown", function (e) {
keysDown[e.keyCode] = true;
}, false);
addEventListener("keyup", function (e) {
delete keysDown[e.keyCode];
}, false);
var reset = function () {
hero.x = canvas.width / 2;
hero.y = canvas.height / 2;
var update = function (modifier) {
if (38 in keysDown) {
hero.y -= hero.speed * modifier;
}
if (40 in keysDown) {
hero.y += hero.speed * modifier;
}
if (37 in keysDown) {
hero.x -=hero.speed * modifier;
}
if (39 in keysDown) {
hero.x +=hero.speed * modifier;
}
//collision
if (
hero.x <= (owl.x + 35)
&& owl.x <= (hero.x + 35)
&& hero.y <= (owl.y + 35)
&& owl.y <= (hero.y + 35)
) {
++owlsCaught;
reset();
}
};
//DRAW
var render = function () {
if (bgReady) {
context.drawImage(bgImage, 0, 0);
}
if (heroReady) {
context.drawImage(heroImage, hero.x, hero.y);
}
if (owlReady) {
context.drawImage(owlImage, owl.x, owl.y);
}
//game loop
var main = function () {
var now = Date.now();
var delta = now - then;
update(delta / 1000);
render();
then = now;
requestAnimationFrame(main);
};
//play
var then = Date.now();
reset();
main();
</script>
Just use the scale() method with negative one to flip the coordinate system. You also need to think "negative" in the sense that you move the character in negative direction with the effect of actually going in positive direction after doing this.
Example:
ctx.scale(-1, 1); // flip hor.
ctx.drawImage(img, -x - img.width, 0); // use neg. position -image width
ctx.scale(-1, 1); // flip back to normal
FIDDLE
You could of course prep the character by creating an off-screen canvas the size of the image, flip the axis, draw the image flipped and use the off-screen canvas as an image source.
Update (for the new code shown)
You could use a flag to know which direction the hero is currently facing:
var flipped = false;
if (37 in keysDown) {
hero.x -=hero.speed * modifier;
flipped = true;
}
if (39 in keysDown) {
hero.x +=hero.speed * modifier;
flipped = false;
}
Then in the render function:
if (heroReady) {
if (flipped) {
context.scale(-1, 1);
context.drawImage(heroImage, -hero.x - heroImage.width, hero.y);
context.scale(-1, 1);
}
else {
context.drawImage(heroImage, hero.x, hero.y);
}
}
I am learning kineticjs through tutorials provided at http://www.html5canvastutorials.com, things are good and easy to understand but, I am having issue in understanding the getIntersection function that i want to use among different objects while dragging to detect collision / overlapping objects.
As far as I have understood the example the getIntersection function expects a position and checks if its intersecting with any other object or not..
Though I got them but with some issues.
I am unable to accomplish this..
Below is the code that I have tried up to now..
<script>
var stage = new Kinetic.Stage({
container: 'container',
width: 1000,
height: 500,
opacity: 0.5
});
var layer = new Kinetic.Layer();
var previous_position;
var new_position;
var collision = false;
var colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple'];
var yellowBox = null;
for(var n = 0; n < 6; n++) {
// anonymous function to induce scope
(function() {
var i = n;
if(n < 3){
y = 50;
x = i * 100 + i * 10;
}else{
y = 150;
x = (i - 3) * 100 + (i - 3) * 10 ;
if(n == 3){
x = 0;
}
}
var box = new Kinetic.Rect({
x: x,
y: y,
width: 100,
height: 50,
fill: colors[i],
stroke: 'black',
strokeWidth: 4,
draggable: true,
name: colors[i]
});
box.on('dragstart', function() {
previous_position = {
x: this.attrs.x,
y: this.attrs.y
};
});
box.on('dragend', function() {
if(collision){
//this.setPosition(previous_position);
layer.draw();
collision = false;
}else{
//this.setPosition(new_position);
layer.draw();
}
});
box.on("dragmove", function(evt) {
console.log(layer.children.length);
if(layer.children.length > 1){
console.log('dragging');
new_position = {x: this.attrs.x,
y: this.attrs.y};
// var posBL = {x: this.attrs.x,
// y: this.attrs.height + this.attrs.y};
// var posTR = {x: this.attrs.x + this.attrs.width,
// y: this.attrs.y};
var posBR = {x: this.attrs.x + this.attrs.width,
y: this.attrs.y + this.attrs.height };
var collisionTL = this.getStage().getIntersections(new_position);
// var collisionBL = this.getStage().getIntersections(posBL);
// var collisionTR = this.getStage().getIntersections(posTR);
// var collisionBR = this.getStage().getIntersections(posBR);
console.log(collisionTL);
console.log(collisionTL.shapes);
// if(collisionTL.length > 1 || collisionBL.length > 0 || collisionTR.length > 0 || collisionBR.length > 0){
if(collisionTL.length > 1){
console.log(collisionTL.shapes);
collision = true;
}else{ //if(collisionBR.length > 0){
collision = true;
}
// for(i=0; i < collision.length; i++){
// // console.log(collision[i]._id);
// }
}
});
if(colors[i] === 'yellow') {
yellowBox = box;
}
layer.add(box);
})();
}
stage.add(layer);
</script>
in the dragmove event you guyz can see I get the four corner positions of the dragging box {commented right now} and with this I was able to detect the overlap / collision but it has 2 issues:
1. very slow with only 3 objects in my test
2. if non of the corner points intersect it didn't fire the collision stuff {for this one box can be bigger so it can cover the other entirely}
I would highly apreciate if anyone can please help me accomplish this stuff..
[A] Any object dragging if by any mean overlaps any other object I want it to show collision.
[B] If possible make getIntersection to work on a particular layer group whichever is possible
[C] any other workaround beside kineticJS to accomplish the above task
Regards
Ok, the developer of KineticJS is working on improving the .getIntersections() function... or at least he said he is. But until the function is improved you have to make your own collision detection function. Assuming that your objects are rectangles or can be broken into a series of points you should go with something like this:
Create a function which determines if a point is in a shape (if the corner of a rectangle is inside another rectangle) like so:
function checkCollide(pointX, pointY, objectx, objecty, objectw, objecth) { // pointX, pointY belong to one rectangle, while the object variables belong to another rectangle
var oTop = objecty;
var oLeft = objectx;
var oRight = objectx+objectw;
var oBottom = objecty+objecth;
if(pointX > oLeft && pointX < oRight){
if(pointY > oTop && pointY < oBottom ){
return 1;
}
}
else
return 0;
};
then you can do a big loop which iterates through all objects in a layer to check collision, like so:
var children = layer.getChildren();
for( var i=0; i<children.length; i++){ // for each single shape
for( var j=0; j<children.length; j++){ //check each other shape
if(i != j){ //skip if shape is the same
if(checkCollide(children[i].getX(), children[i].getY(), children[j].getX(), children[j].getY(), children[j].getWidth(), children[j].getHeight()))
alert('top left corner collided');
}
}
}
the checkCollide function I provided only checks the collision for the top left corner on each shape, so you have to modify the function to check all corners, it's not a long rewrite, and there are plenty tutorials even here on stackoverflow which deal with 'bounding rectangles collision detection'
This may seem like it is a very heavy function, but surprisingly it is still faster than .getIntersections(). Also, you should throw in extra if statements so that the function doesn't run through all the checks all the time.
I created a game myself and was using .intersects() and was having a lot of slow down. I switched over to this type of 'simpler' collision detection and now my game runs around 60FPS. http://cs.neiu.edu/~tsam/physics/index.phtml (test/test) if you want to check it out. You can view page source to see how I structured the collision detection to be more efficient (such as in checkIntersectsGoal() function.
If I define a canvas and draw few shapes onto it, then how can I pinpoint each of the shape or image so as to declare events and other properties on the each shape. Consider I have a Rectangle and a triangle. SO can I have some mechanism so as to define them as specific entity and can I deal with them individually. I know about KineticJS but I would like to implement this functionality on my own(For learning purpose). Can anyone pinpoint the way to do it. Or may be an algorithmic approach??
I would like explain pinpoint using mouse events
First of all you have to implement a method to get mouse position
function getMousePos(canvas, evt){
// get canvas position
var obj = canvas;
wrapper = document.getElementById('wrapper');
var top = 0;
var left = 0;
while (obj && obj.tagName != 'BODY') {
top += obj.offsetTop;
left += obj.offsetLeft;
obj = obj.offsetParent;
}
// return relative mouse position
var mouseX = evt.clientX - left + window.pageXOffset+wrapper.scrollLeft;
var mouseY = evt.clientY - top + window.pageYOffset+wrapper.scrollTop;
return {
x: mouseX,
y: mouseY
};
}
Rectangle
Say, we have a rectangle with following values x1, y1, w, h
$(canvas).mousemove(function(e){
//Now call the method getMousePos
var mouseX, mouseY;
var mousePos = getMousePos(canvas, e);
mouseX=mousePos.x;
mouseY=mousePos.y;
// check if move on the rect
if(mouseX>x1 && mouseX<x1+w && mouseY>y1 && mouseY<y1+h)
{
alert('mouse on rect')
}
});
Circle
Say, we have a circle with following values x, y, r
$(canvas).mousemove(function(e){
//Now call the method getMousePos
var mouseX, mouseY;
var mousePos = getMousePos(canvas, e);
mouseX=mousePos.x;
mouseY=mousePos.y;
// check if move on the rect
if(Math.pow(mouseX-x,2)+Math.pow(mouseY-y,2)<Math.pow(r,2))
{
alert('mouse on circle')
}
});
By this way we can pinpoint a object of canvas
You can't use any existing functionality in the DOM for that. So you have to write it yourself. You could start by making an object model like this:
function Shape(x, y) {
var obj = {};
obj.x = x;
obj.y = y;
obj.paint = function(canvasTarget) {
}
return obj;
}
function Rectangle(x, y, width, height) {
var obj = Shape(x, y);
obj.width = width;
obj.height = height;
obj.paint = function(canvasTarget) {
//paint rectangle on the canvas
}
return obj;
}
function Canvas(target) {
var obj = {};
obj.target = target
obj.shapes = [];
obj.paint = function() {
//loop through shapes and call paint
}
obj.addShape(shape) {
this.shapes.push(shape);
}
}
After making the object model you could use it to draw the shapes like this:
var cnv = new Canvas();
cnv.addShape(new Rectangle(10,10,100,100));
cnv.paint();
Then you can handle the onclick event on the canvas and determine which shape is clicked on.