#Pointy:
As I said below, the size variable adjusts the size by a percentage. So this number could have, for example, a value of 1, or 1.5, or 2, etc. The default circle diameter is 100 pixels, and the actual diameter of the circle displayed is size*100. When checking for collision I use size*120 because I want to leave some extra room.
To my understanding, the CSS left and top properties positions the object in terms of its left and top bounds, respectively. Thus if I want to check collision on the right side of the object, I'd have to take the left boundary and add the diameter of the circle (which is given by size*100). When checking the collision on the left side, I take the left bound and subtract size*(1.2-1)*100, because I do not need to account for the diameter of the circle as I am starting from the left bound. The same thing applies to the y-axis.
I hope this was understandable, thanks for helping me out.
Original Post:
I have two circles on my screen, a pink one and a purple one. The positions of these circles (x,y) are randomly determined. Initially, only the purple circle is on the page. One second later, the pink circle will appear. I want to check if these two circles will overlap, and if so, reposition the pink circle.
My code:
if(document.getElementById("purple").style.left+(size*120) > x
&& document.getElementById("purple").style.left-(size*20) < x)
{
if(x + 200 <= rightBound) {
x = x+200;
}
else {
x = x-200;
}
}
else if(document.getElementById("purple").style.top+(size*120) > y
&& document.getElementById("purple").style.top-(size*20) < y)
{
if(y+200 <= bottomBound) {
y = y+200;
}
else {
y = y-200;
}
}
document.getElementById("pink").style.left = x+"px";
document.getElementById("pink").style.top = y+"px";
The size variable modifies the circle size by a percentage. The default circle size is 100 pixels, but in my code I specify 120 pixels to leave a bit of extra room.
The rightBound and bottomBound variables dictate the bounds in which the circle must be randomly positioned. My code checks if moving the circle 200 pixels to the right, or 200 pixels down would still position the circle within the bounds. If so, then this is done, otherwise the circle is moved 200 pixels to the left instead.
By the way, in case it is not clear x and y are the randomly generated values for the position of the pink circle.
With this code, however, I have still experienced instances where the pink and purple circle will overlap. I'm wondering what the problem is with my logic, as in my explanation and thinking I feel that everything should be working fine.
Why not just keep trying new pink circles until you get one that's right? Random numbers are cheap, after all:
http://jsfiddle.net/HVX7d/
while(distance(purple, pink) < 120) {
pink = {
x: int_rand(100, 500),
y: int_rand(100, 500)
};
}
Just use a loop to keep generating new positions for pink until you're sure there are no collisions, using Pythagorean to calculate how far apart the circles are.
Original Answer (incorrectly assumed 100 was a radius):
The issue is that you're specifying 120 in your boundary logic but only moving the circle by 200 when you detect it. That means there's a scenario where the circles originally don't overlap, but when you move them by only 200 you've created an overlap that didn't previously exist.
Change your instances of 200 to 240 and you shouldn't see any more issues.
For example: you have purple at 200, 200 and pink at 310, 200. That's enough to trigger your boundary logic because 310 - 200 is only 110, so you choose to move pink. Let's say you move it up because it's near the bottom boundary, so now it's at 110, 200 and you've created overlap.
I think this should do what you need:
function reposition_check(x, y, rightBound, bottomBound, size, elem) {
var c1_size = 120 * size;
var c2_size = 120 * size;
var c1_x = parseInt(elem.style.left.replace("px", "")) + (c1_size / 2); //center the purple x coordinate
var c1_y = parseInt(elem.style.top.replace("px", "")) + (c1_size / 2); //center the purple y coordinate
var c2_x = x + (c2_size / 2); //center the pink x coordinate
var c2_y = y + (c2_size / 2); //center the pink y coordinate
var d_x = c1_x - c2_x; //distance on the x plane
var d_y = c1_y - c2_y; //distance on the y plane
if (Math.sqrt((d_x * d_x) + (d_y * d_y)) < (c1_size / 2) + (c2_size / 2)) { //Pythagorean theorum to determine if overlapping
console.log("is overlapping");
if (Math.abs(d_x) < (c1_size / 2) + (c2_size / 2)) { //if true, the overlap is on the x plane
//if(x + (c2_size) + 200 <= rightBound && d_x > 0) {
if(d_x > 0) {
x = (x + 200 <= rightBound) ? x + 200 : (x - 200 - d_x);
}else{
x = (x + 200 <= rightBound) ? x - d_x + 200 : (x - 200);
}
return {x: x, y: y};
}
if (Math.abs(d_y) < (c1_size / 2) + (c2_size / 2)) { //if true, the overlap is on the y plane
if(d_y > 0) {
y = (y + 200 <= bottomBound) ? y + 200 : (y - 200 - d_y);
}else{
y = (y + 200 <= bottomBound) ? y - d_y + 200 : (y - 200);
}
return {x: x, y: y};
}
}
return {x: x, y: y};
}
//Here's how you would use it:
var purple = document.getElementById("purple");
var values = reposition_check(111, 1, 1000, 800, 1, purple); //x, y, rightBound, bottomBound, size, element
document.getElementById("pink").style.left = values.x + "px";
document.getElementById("pink").style.top = values.y + "px";
Related
In this example we use math functions to create a blurred copy of an image. The way we will blur the image is by scrambling pixels that are near each other.
We begin by creating a blank image and writing the loop to let us color each pixel in the image. For each pixel we will do one of two things: half the time, we will simply copy the pixel from the old picture into the new picture without changing anything. The other half of the time we will find a pixel nearby and copy that one instead.
Now we must figure out how to find a "nearby" pixel. We will define some value for how far away the new pixel will be (we used 10 pixels) and then we write a function that will give a (x,y) point that is a random amount between 0 and 10 pixels away in each direction. Before we use the new (x,y) point, we must check to be sure it is still a valid pixel position in the image. For example, we may be at a pixel that is on the very top of the image. Our random point generator tells us to go up by 3 pixels, but since we are on the top of the image (y = 0) we cannot very well go up by three pixels (y would be -3)! If the random number is too big (larger than the dimension -1) or too small (less than 0) then we will just use the closest number that is valid.
Once we have a valid pixel that is some amount away we use its red, green, and blue values as the new pixel's values.
function nearby(image, x, y) {
var newX = x + Math.random() * 10 - 5;
if (newX > image1.getWidth()) {
newX = newX - 5;
}
if (newX < 0) {
newX = newX + 5;
} else {
newX = newX;
}
var newY = y + Math.random() * 10 - 5;
if (newY > image1.getHeight()) {
newY = newY - 5;
}
if (newY < 0) {
newY = newY + 5;
} else {
newY = newY;
}
return image.getPixel(newX, newY);
}
function blur(image) {
for (var pixel of image.values()) {
x = pixel.getX();
y = pixel.getY();
var orgPixel = image.getPixel(x, y);
if (Math.random() < 0.5) {
var other = nearby(image, x, y);
output.setPixel(x, y, other);
} else {
output.setPixel(x, y, orgPixel);
}
}
return output;
}
var image1 = new SimpleImage("duvall.jpg");
var output = new SimpleImage(image1.getWidth(), image1.getHeight());
blur(image1);
print(output);
----i used one of the pictures in my file but any picture should do.
I'm building a p5js donut chart, but I'm struggling to show the data labels in the middle. I think I have managed to get the boundaries right for it, but how would match the angle that I'm in? Or is there a way of matching just through the colours?
https://i.stack.imgur.com/enTBo.png
I have started by trying to match the boundaries of the chart to the pointer, which I managed to do using mouseX and mouseY. Any suggestions, please?
if(mouseX >= width / 2 - width * 0.2 && mouseY >= height / 2 - width * 0.2
&& mouseX <= width / 2 + width * 0.2 && mouseY <= height / 2 + width * 0.2)
{
//console.log("YAY!!! I'm inside the pie chart!!!");
}
else
{
textSize(14);
text('Hover over to see the labels', width / 2, height / 2);
}
};
[1]: https://i.stack.imgur.com/enTBo.png
While you could theoretically use the get() function to check the color of the pixel under the mouse cursor and correlate that with one of the entries in your dataset, I think you would be much better off doing the math to determine which segment the mouse is currently over. And conveniently p5.js provides helper functions that make it very easy.
In the example you showed you are only checking if the mouse cursor is in a rectangular region. But in reality you want to check if the mouse cursor is within a circle. To do this you can use the dist(x1, y1, x2, y2) function. Once you've established that the mouse cursor is over your pie chart, you'll want to determine which segment it is over. This can be done by finding the angle between a line draw from the center of the chart to the right (or whichever direction is where you started drawing the wedges), and a line drawn from the center of the chart to the mouse cursor. This can be accomplished using the angleBetween() function of p5.Vector.
Here's a working example:
const colors = ['red', 'green', 'blue'];
const thickness = 40;
let segments = {
foo: 34,
bar: 55,
baz: 89
};
let radius = 80, centerX, centerY;
function setup() {
createCanvas(windowWidth, windowHeight);
noFill();
strokeWeight(thickness);
strokeCap(SQUARE);
ellipseMode(RADIUS);
textAlign(CENTER, CENTER);
textSize(20);
centerX = width / 2;
centerY = height / 2;
}
function draw() {
background(200);
let keys = Object.keys(segments);
let total = keys.map(k => segments[k]).reduce((v, s) => v + s, 0);
let start = 0;
// Check the mouse distance and angle
let mouseDist = dist(centerX, centerY, mouseX, mouseY);
// Find the angle between a vector pointing to the right, and the vector
// pointing from the center of the window to the current mouse position.
let mouseAngle =
createVector(1, 0).angleBetween(
createVector(mouseX - centerX, mouseY - centerY)
);
// Counter clockwise angles will be negative 0 to PI, switch them to be from
// PI to TWO_PI
if (mouseAngle < 0) {
mouseAngle += TWO_PI;
}
for (let i = 0; i < keys.length; i++) {
stroke(colors[i]);
let angle = segments[keys[i]] / total * TWO_PI;
arc(centerX, centerY, radius, radius, start, start + angle);
// Check mouse pos
if (mouseDist > radius - thickness / 2 &&
mouseDist < radius + thickness / 2) {
if (mouseAngle > start && mouseAngle < start + angle) {
// If the mouse is the correct distance from the center to be hovering over
// our "donut" and the angle to the mouse cursor is in the range for the
// current slice, display the slice information
push();
noStroke();
fill(colors[i]);
text(`${keys[i]}: ${segments[keys[i]]}`, centerX, centerY);
pop();
}
}
start += angle;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
I think I know the source of the problem was that #thenewbie experienced: it is the p5 library being used. I was using the p5.min.js and experiencing the same problem. Once I started using the full p5.js library, the issue was resolved and #Paul's script worked.
Here is a link I came across while researching this which put me onto the solution:
https://github.com/processing/p5.js/issues/3973
Thanks Paul for the clear explanations and code above.
I am working on a maze game in HTML 5.
Here is the function which I use to draw an "X" on the canvas (using the touchpad, the user will navigate the X through the labyrinth).
dim = 8;
function rect(x,y,xdim){
ctx.beginPath();
ctx.moveTo(x - xdim, y - xdim);
ctx.lineTo(x + xdim, y + xdim);
ctx.moveTo(x + xdim, y - xdim);
ctx.lineTo(x - xdim, y + xdim);
ctx.strokeStyle = '#FF5000';
ctx.stroke();
}
The problem is in the checkcollision function, mainly in this piece of code:
ctx.getImageData(checkx, checky, 16, 16);
I am looking for an optimal way to check all the space that is taken up by the "X" on the canvas.
There are more comments in the code i used.
Issues:
1. Sometimes, the X goes a little passed the border
2. Sometimes, the X doesn't get close, letting a few pixels between it and the border.
function checkcollision(checkx, checky){
var collision = 0;
/*
User presses an arrow key
Calculate where that would move the square to.
Look at all of the pixels on the maze image that the square will cover
in its new position (our square is 15x15 so we look at 225 pixels)
Are any of the pixels black (the maze is white with black borders.
If a pixel is black that means the square would collide with a border)
YES: then do not move
NO: then move
*/
var imgd = ctx.getImageData(checkx, checky, 15, 15);
pix = imgd.data;
for (var i = 0; n = pix.length, i < n; i += 4){
if (pix[i] == 0) {
collision = 1;
} else if(pix[i] == 255 && pix[i + 1] == 0){
winner = "yes";
}
}
return collision;
}
I believe the getImageData gets a rectangle which starts from the x,y position in a corner.
So maybe there is a way to draw it using the checkx and checky as the coordinates for the center and retrieve a square 16 pixels wide
Wow, writing the question made it clear:
var imgd = ctx.getImageData(checkx - xdim, checky - xdim, 16, 16);
Ty guys
I need to turn a click location into a polar coordinate.
This is my current algorithm. Location is the location on the canvas of the click ({x:evt.clientX, y:evt.clientY}), center is the offset of the origin from 0,0. For example, if the circle is centered on 250, 250, center is {x:250, y:250}. Scale is the scale of the radius. For example, if the radius of a circle from the center would normally be 50 and the scale is .5, the radius becomes 25. (it's for zooming in/out)
this.getPolarLocation = function(location){
var unscaledFromCenter = {
x: location.x - center.x,
y: location.y - center.y
};
var angle = this.getAngleOnCircle(unscaledFromCenter);
var dist = Math.sqrt(unscaledFromCenter.x * unscaledFromCenter.x + unscaledFromCenter.y * unscaledFromCenter.y) * this.ds.scale;
return {
angle:angle,
dist:dist,
toString: function(){
return "Theta: ".concat(angle).concat("; dist: ").concat(dist);
}
};
}
this.getAngleOnCircle = function(location){
var x = location.x;
var y = location.y;
if(x == 0 && y > 0)
return Math.PI / 2;
if(x == 0 && y < 0)
return 3 * Math.PI / 2;
if(y == 0 && x > 0)
return 0;
if(y == 0 && x < 0)
return Math.PI;
var angle = Math.atan(y/x);
if(x > 0 && y > 0)
return angle;
if(x < 0)
return Math.PI + angle
return Math.PI * 2 + angle;
}
Screenshots of the issue. The left is what happens zoomed out (and is not supposed to happen). The right is zoomed in (scale >= 1), and is what is supposed to happen.
I'm under the impression that my center coordinates are being shifted slightly off. It seems to work fine for scale >= 1, but not for scale < 1
Source:
circos.html: http://pastie.org/private/cowsjz7mcihy8wtv4u4ag
circos.js: http://pastie.org/private/o9w3dwccmimalez9fropa
datasource.js: http://pastie.org/private/iko9bqq8eztbfh8xpvnoaw
Run in Firefox
So my question is: why doesn't this work?
For some reason, the program automagically works when I close firebug. It doesn't seem to work on Firefox 5, only the version I have (in the 3s somewhere). Either way, I'm scrapping the project for something more object oriented. There's no way the current algorithm could handle a genome. (which is exactly what I'm going to be mapping)
UPDATE:
I figured out the problem... I was measuring the distance from the top left of the page, not the top left of the canvas. Thus, when firebug was enabled, the screen was shifted, making the problems worse. The solution is the use canvas.offsetLeft and canvas.offsetTop to calculate the position on the canvas.
I've been banging my head on the keyboard for about one week now and I can't figure a proper solution for my problem. I think it's more Math related than HTML Canvas... hopefully someone can point me into the right direction.
I'm having an HTML Canvas where users can draw lines using they mouse and the very simple moveTo() and lineTo() functions. When the user is done I save the coords in a MongoDB. When the user hits the page later again I want to display his drawing BUT I don't want to load the entire picture with all stored coordinates at once, I want to return it in tiles (for better performance by caching each tile).
The tiles are 200x200 pixels (fixed offsets and width, starting at 0 -> 200-> 400 ->...).
Now, when the user draws a line from let's say 50,50(x/y) to 250,250(x/y) there's only one dot in each bounding box (tile). I need to split the lines and calculate the start and ending points of each line in each bounding box (tile). Otherwise I can't draw the image partially (in tiles). It get's even more complicated when a single line crosses multiple bounding boxes (tiles). For instance: 100,100 (x/y) -> -1234,-300 (x/y).
The lines can go from any point (+/-) to ANY direction for ANY distance.
Of course I looked at Bresenham's good old algorithm and it worked - partially, but it seems like the longest and most resource-hungry solution to me.
So, the reason I'm here is that I hope someone can point me into the right direction with (perhaps) another approach of calculating the start/ending points of my lines for each bounding box.
Code examples are very welcome in JavaScript or PHP.
Thank you for reading and thinking about it :)
tl;dr: Use planes, maths explained below. There's a canvas example at the bottom.
Given that all of your cells are axis-aligned bounding boxes, you could use the plane equation to find the intersection of your line with the edges.
Planes
You can think of your box as a set of four geometric planes. Each plane has a normal, or a vector of length one, indicating which direction is the "front" of the plane. The normals for the planes that make up your cell's sides would be:
top = {x: 0, y: -1};
bottom = {x: 0, y: 1};
left = {x: -1, y: 0};
right = {x: 1, y: 0};
Given a point on the plane, the plane has the equation:
distance = (normal.x * point.x) + (normal.y * point.y)
You can use this equation to calculate the distance of the plane. In this case, you know the top-left corner of your box (let's say x is 10 and y is 100) is on the top plane, so you can do:
distance = (0 * 10) + (-1 * 100)
distance = -100
Checking a point against a plane
Once you have the distance, you can reuse the equation to check where any point is, relative to the plane. For a random point p (where x is -50 and y is 90), you can do:
result = (normal.x * p.x) + (normal.y * p.y) - distance
result = (0 * -50) + (-1 * 90) - (-100)
result = 0 + (-90) - (-100)
result = -90 + 100
result = 10
There are two possible results:
if (result >= 0) {
// point is in front of the plane, or coplanar.
// zero means it is coplanar, but we don't need to distinguish.
} else {
// point is behind the plane
}
Checking a line against a plane
You can check both endpoints of a line from a to b in this way:
result1 = (normal.x * a.x) + (normal.y * a.y) - distance
result2 = (normal.x * b.x) + (normal.y * b.y) - distance
There are four possible results:
if (result1 >= 0 && result2 >= 0) {
// the line is completely in front of the plane
} else if (result1 < 0 && result2 < 0) {
// the line is completely behind the plane
} else if (result1 >= 0 && result2 < 0) {
// a is in front, but b is behind, line is entering the plane
} else if (result1 < 0 && result2 >= 0) {
// a is behind, but b is in front, line is exiting the plane
}
When the line intersects the plane, you want to find the point of intersection. It helps to think of a line in vector terms:
a + t * (b - a)
If t == 0, you are at the start of the line, and t == 1 is the end of the line. In this context, you can calculate the point of intersection as:
time = result1 / (result1 - result2)
And the point of intersection as:
hit.x = a.x + (b.x - a.x) * time
hit.y = a.y + (b.y - a.y) * time
Checking a line against the box
With that math, you can figure out the lines of intersection with your box. You just need to test the endpoints of your line against each plane, and find the minimum and maximum values of time.
Because your box is a convex polygon, there is an early out in this check: if the line is completely in front of any one plane in your box, it cannot intersect with your box. You can skip checking the rest of the planes.
In JavaScript, your result might look something like this:
/**
* Find the points where a line intersects a box.
*
* #param a Start point for the line.
* #param b End point for the line.
* #param tl Top left of the box.
* #param br Bottom right of the box.
* #return Object {nearTime, farTime, nearHit, farHit}, or false.
*/
function intersectLineBox(a, b, tl, br) {
var nearestTime = -Infinity;
var furthestTime = Infinity;
var planes = [
{nx: 0, ny: -1, dist: -tl.y}, // top
{nx: 0, ny: 1, dist: br.y}, // bottom
{nx: -1, ny: 0, dist: -tl.x}, // left
{nx: 1, ny: 0, dist: br.x} // right
];
for (var i = 0; i < 4; ++i) {
var plane = planes[i];
var nearDist = (plane.nx * a.x + plane.ny * a.y) - plane.dist;
var farDist = (plane.nx * b.x + plane.ny * b.y) - plane.dist;
if (nearDist >= 0 && farDist >= 0) {
// both are in front of the plane, line doesn't hit box
return false;
} else if (nearDist < 0 && farDist < 0) {
// both are behind the plane
continue;
} else {
var time = nearDist / (nearDist - farDist);
if (nearDist >= farDist) {
// entering the plane
if (time > nearestTime) {
nearestTime = time;
}
} else {
// exiting the plane
if (time < furthestTime) {
furthestTime = time;
}
}
}
}
if (furthestTime < nearestTime) {
return false;
}
return {
nearTime: nearestTime,
farTime: furthestTime,
nearHit: {
x: a.x + (b.x - a.x) * nearestTime,
y: a.y + (b.y - a.y) * nearestTime
},
farHit: {
x: a.x + (b.x - a.x) * furthestTime,
y: a.y + (b.y - a.y) * furthestTime
}
};
}
If this is still too slow, you can also do broadphase culling by dividing the world up into big rects, and assigning lines to those rects. If your line and cell aren't in the same rect, they don't collide.
I've uploaded a canvas example of this.
This looks like you'd have to figure out at what point each line intersects with the bounds of each tile.
Check out the answer to this question: Is there an easy way to detect line segment intersections?
The answers don't provide code, but it shouldn't be too hard to convert the equations to PHP or Javascript...
EDIT:
Why, exactly, do you want to split the lines? I understand you don't want to load all the lines at once, since that could take a while. But what's wrong with just loading and drawing the first few lines, and drawing the remainder later on?
Methinks that would be a lot simpler than having to cut up each line to fit in a specific tile. Tiling is a nice way of optimizing bitmap loading; I don't think it's very appropriate for vector-based drawings.
You could also consider sending an Ajax request, and start drawing the whole thing whenever it comes in; this would not interfere with the loading of the page.