I'm trying to wrap my head around using the Separating Axis Theorem in JavaScript to detect two squares colliding (one rotated, one not). As hard as I try, I can't figure out what this would look like in JavaScript, nor can I find any JavaScript examples. Please help, an explanation with plain numbers or JavaScript code would be most useful.
Update: After researching lots of geometry and math theories I've decided to roll out a simplified SAT implementation in a GitHub repo. You can find a working copy of SAT in JavaScript here: https://github.com/ashblue/canvas-sat
Transforming polygons
First you have to transform all points of your convex polygons (squares in this case) so they are all in the same space, by applying a rotation of angle.
For future support of scaling, translation, etc. I recommend doing this through matrix transforms. You'll have to code your own Matrix class or find some library that has this functionality already (I'm sure there are plenty of options).
Then you'll use code in the vain of:
var transform = new Matrix();
transform.appendRotation(alpha);
points = transform.transformPoints(points);
Where points is an array of Point objects or so.
Collision algorithm overview
So that's all before you get to any collision stuff. Regarding the collision algorithm, it's standard practice to try and separate 2 convex polygons (squares in your case) using the following steps:
For each polygon edge (edges of both polygon 0 and polygon 1):
Classify both polgyons as "in front", "spanning" or "behind" the edge.
If both polygons are on different sides (1 "in front" and 1 "behind"), there is no collision, and you can stop the algorithm (early exit).
If you get here, no edge was able to separate the polgyons: The polygons intersect/collide.
Note that conceptually, the "separating axis" is the axis perpendicular to the edge we're classifying the polygons with.
Classifying polygons with regards to an edge
In order to do this, we'll classify a polygon's points/vertices with regards to the edge. If all points are on one side, the polygon's on that side. Otherwise, the polygon's spanning the edge (partially on one side, partially on the other side).
To classify points, we first need to get the edge's normal:
// this code assumes p0 and p1 are instances of some Vector3D class
var p0 = edge[0]; // first point of edge
var p1 = edge[1]; // second point of edge
var v = p1.subtract(p0);
var normal = new Vector3D(0, 0, 1).crossProduct(v);
normal.normalize();
The above code uses the cross-product of the edge direction and the z-vector to get the normal. Ofcourse, you should pre-calculate this for each edge instead.
Note: The normal represents the separating axis from the SAT.
Next, we can classify an arbitrary point by first making it relative to the edge (subtracting an edge point), and using the dot-product with the normal:
// point is the point to classify as "in front" or "behind" the edge
var point = point.subtract(p0);
var distance = point.dotProduct(normal);
var inFront = distance >= 0;
Now, inFront is true if the point is in front or on the edge, and false otherwise.
Note that, when you loop over a polygon's points to classify the polygon, you can also exit early if you have at least 1 point in front and 1 behind, since then it's already determined that the polygon is spanning the edge (and not in front or behind).
So as you can see, you still have quite a bit of coding to do. Find some js library with Matrix and Vector3D classes or so and use that to implement the above. Represent your collision shapes (polygons) as sequences of Point and Edge instances.
Hopefully, this will get you started.
Related
I am using a Javascript library 'Tess2' to triangulate a series of contours.
https://github.com/memononen/tess2.js/blob/master/src/tess2.js
It generates a perfect 2d mesh of any shape consisting of multiple contours:
A contour consists of a series of points (in a negative winding order for solid fills, in a positive winding order for holes)
However, the resulting triangles output by the algorithm are no longer tied to a contour and its fill color.
How would I alter Tess2 (or any other javascript library that tesselates contours) to allow for the retention of color data in the resulting triangles?
I've tried looking everywhere and I cannot find a solution.
From what I've seen in the source code, the tessalation function contains a vertex indices in an returned object:
Tess2.tesselate = function(opts) {
...
return {
vertices: tess.vertices,
vertexIndices: tess.vertexIndices,
vertexCount: tess.vertexCount,
elements: tess.elements,
elementCount: tess.elementCount,
mesh: debug ? tess.mesh : undefined
};
You can create a new array with the colors for each vertex, and then use vertexIndices from the object to get a color of the vertex.
If you would like to have a color per face, you would just need to generate an array like above, which means putting the same vertex color for each vertex in a array. You would also like to wrap all of this data in some kind of convienent object or class.
[EDIT]
It turns out that the tesselation algorithm merges vertices in the same position, meaning that it reorganizes vertex array completely. There are a solution to explicitly not merge different contours with overlapping vertices:
Tess2.tesselate({ contours: yourContours, elementType: Tess2.BOUNDARY_CONTOURS });
That should preserve the original vertices, however not in an original order, use vertexIndices to get the original position of these.
After many failed attempts I finally got there.
I've been trying all this time to try and process a huge amount of contours all at once with a single tessellation pass.
I tried editing the tessellation library to make each half edge retain it original contour ID. I had several eureka moments when it finally seemed to work, only to be disappointed when I stress tested it and found it to be less than perfect.
But it turns out I've been incredibly daft...
All I had to do was group each contour with a particular fill, and then tesselate each group independently.
I didn't understand that for every interior contour fill, there would always be an opposite contour that effectively enclosed the outer contour loop.
For instance, to represent a red box with a blue box inside there would be 2 red contours and 1 blue. I thought it could be represented with only 1 blue contour and 1 red, where the blue would also represent the red contour's hole, and so processing each colour group of contours independently didn't make sense to me.
When I finally realised this, I figured it out.
I've published a solution on github but I'm not sure how much use it is to anyone:
https://github.com/hedgehog90/triangulate-contours-colour-example
I've included a pretty comprehensive exporter script for converting contours (including curves) into polygonal paths for Adobe Flash/Animate which might be useful for someone.
I will be writing an OBJ exporter on top of this shortly, so I can represent vector graphics in a 3D engine.
I'm letting the user click on two points on a sphere and I would then like to draw a line between the two points along the surface of the sphere (basically on the great circle). I've been able to get the coordinates of the two selected points and draw a QuadraticBezierCurve3 between the points, but I need to be using CubicBezierCurve3. The problem is is that I have no clue how to find the two control points.
Part of the issue is everything I find is for circular arcs and only deals with [x,y] coordinates (whereas I'm working with [x,y,z]). I found this other question which I used to get a somewhat-working solution using QuadraticBezierCurve3. I've found numerous other pages with math/code like this, this, and this, but I really just don't know what to apply. Something else I came across mentioned the tangents (to the selected points), their intersection, and their midpoints. But again, I'm unsure of how to do that in 3D space (since the tangent can go in more than one direction, i.e. a plane).
An example of my code: http://jsfiddle.net/GhB82/
To draw the line, I'm using:
function drawLine(point) {
var middle = [(pointA['x'] + pointB['x']) / 2, (pointA['y'] + pointB['y']) / 2, (pointA['z'] + pointB['z']) / 2];
var curve = new THREE.QuadraticBezierCurve3(new THREE.Vector3(pointA['x'], pointA['y'], pointA['z']), new THREE.Vector3(middle[0], middle[1], middle[2]), new THREE.Vector3(pointB['x'], pointB['y'], pointB['z']));
var path = new THREE.CurvePath();
path.add(curve);
var curveMaterial = new THREE.LineBasicMaterial({
color: 0xFF0000
});
curvedLine = new THREE.Line(path.createPointsGeometry(20), curveMaterial);
scene.add(curvedLine);
}
Where pointA and pointB are arrays containing the [x,y,z] coordinates of the selected points on the sphere. I need to change the QuadraticBezierCurve3 to CubicBezierCurve3, but again, I'm really at a loss on finding those control points.
I have a description on how to fit cubic curves to circular arcs over at http://pomax.github.io/bezierinfo/#circles_cubic, the 3D case is essentially the same in that you need to find out the (great) circular cross-section your two points form on the sphere, and then build the cubic Bezier section along that circle.
Downside: Unless your arc is less than or equal to roughly a quarter circle, one curve is not going to be enough, you'll need two or more. You can't actually model true circular curves with Bezier curves, so using cubic instead of quadratic just means you can approximate a longer arc segment before it starts to look horribly off.
So on a completely different solution note: if you have an arc command available, much better to use that than to roll your own (and if three.js doesn't support them, definitely worth filing a feature request for, I'd think)
I have recently started playing with canvas after seeing how easy it can be. My first project was just to keep a circle in its boundaries as it moves around. I made a few more things involving the movement of circles and now...
I'm currently working on bouncing two circles off of each other when they hit. You can see the example of that here: http://jsfiddle.net/shawn31313/QQMgm/7/
However, I would like to use a little more real world physics. At the moment, when the circles hit each other they just reverse their path.
As shown here:
// Dont be confused, this is just the Distance Formula
// We compare the distance of the two circles centers to the sum of the radii of the two
// circles. This is because we want to check when they hit each other on the surface
// and not the center.
var distance = Math.sqrt(Math.pow(c1.x - c2.x, 2) + Math.pow(c1.y - c2.y, 2));
var r1 = c1.rad;
var r2 = c2.rad;
if (distance < r1 + r2) {
// Change the slope of both circle
// I would like to figure out a more effecience way of bouncing the circles back
// However, I have no idea how to determine the angle the ball was struck,
// and with that information bounce it off at that angle
c1.xi = -c1.xi; // path is reversed
c1.yi = -c1.yi;
c2.xi = -c1.xi;
c2.yi = -c1.yi;
}
However, I would like the circles to go in opposite direction determined by the point and angle of intersection.
I am only in the 9th grade and not sure how the formula for something like this would look. But I know that it is possible because this kind of physics is present in many games. An example would be an 8-ball game. When the balls hit each other, they move across the table according to how the balls hit each other.
I would appreciate any tips on how to do this or if I should wait until I have a stronger understanding of Physics and Math in general.
too bad we can't draw a very simple scheme.
As far as physics is concerned, you know that the total momentum is conserved, see
http://en.wikipedia.org/wiki/Momentum
There is a good illustration and formulas here http://en.wikipedia.org/wiki/Elastic_collision#Two-_and_three-dimensional
You can simplify formulas if the two object have the same weight.
so now, let's consider the reference frame in which c2 is fixed and center in (0,0).
c1 velocity in this reference would be :
c1.xfi=c1.xi-c2.xi
c1.yfi=c1.yi-c2.yi
Now you have a collision when the distance between the two is the sum of radius. Consider the tangent plane of the two circles.
You now have to decompose the velocity of c1 into a tangent component, which is conserved, and a perpendicular (following the line between c1 and c2), which is transfered to c2.
Then you need to go back to your original reference frame.
(sorry i didn't give you the exact formulas but they are on the links I provided)
If I were doing this myself, I would implement the motion using Newtons law of restitution. Essentially this is a coefficient that relates approach and separation speed of 2 particles before/after impact and it has a value that depends on the material properties of your particles.
Your analysis will essentially amount to identifying the point of impact, then breaking down the approach velocities into components that are parallel and perpendicular to the line of centres of the circle at the point of impact.
The momentum of the particles is conserved perpendicular to the line of centres (so the velocities in that direction remain unchanged by the collision) and the law of restitution applies to the velocities parallel to the line of centres. Thus if you fix the coefficient of restitution (it has to be between 0 and 1) to some value of your choice you can use this law to calculate the separation speeds along the line of centres of your particles after collision using the value of the approach speeds.
If your particles are all of the same mass and radius then the calculations become simpler. You can model elastic collisions by setting the coefficient to 1 (this indicates that separation speed of the particles is the same as the approach speed) which is probably the easiest place to start. By changing the value you will see different behaviour between particles after collisions.
Sorry not to be able to write this all down in formula for you, but this is not really the appropriate place for it. Living in the UK I have no idea what "9th grade" is so I can't assess if the above is too advanced for your current level of education. Here in the UK this type of problem would typically be covered at A-level mathematics education level.
Hopefully though it will give you an indication of the terms and topics that you can teach yourself/ research in order to achieve your goal.
I'm currently trying to build a kind of pie chart / voronoi diagram hybrid (in canvas/javascript) .I don't know if it's even possible. I'm very new to this, and I haven't tried any approaches yet.
Assume I have a circle, and a set of numbers 2, 3, 5, 7, 11.
I want to subdivide the circle into sections equivalent to the numbers (much like a pie chart) but forming a lattice / honeycomb like shape.
Is this even possible? Is it ridiculously difficult, especially for someone who's only done some basic pie chart rendering?
This is my view on this after a quick look.
A general solution, assuming there are to be n polygons with k vertices/edges, will depend on the solution to n equations, where each equation has no more than 2nk, (but exactly 2k non-zero) variables. The variables in each polygon's equation are the same x_1, x_2, x_3... x_nk and y_1, y_2, y_3... y_nk variables. Exactly four of x_1, x_2, x_3... x_nk have non-zero coefficients and exactly four of y_1, y_2, y_3... y_nk have non-zero coefficients for each polygon's equation. x_i and y_i are bounded differently depending on the parent shape.. For the sake of simplicity, we'll assume the shape is a circle. The boundary condition is: (x_i)^2 + (y_i)^2 <= r^2
Note: I say no more than 2nk, because I am unsure of the lowerbound, but know that it can not be more than 2nk. This is a result of polygons, as a requirement, sharing vertices.
The equations are the collection of definite, but variable-bounded, integrals representing the area of each polygon, with the area equal for the ith polygon:
A_i = pi*r^2/S_i
where r is the radius of the parent circle and S_i is the number assigned to the polygon, as in your diagram.
The four separate pairs of (x_j,y_j), both with non-zero coefficients in a polygon's equation will yield the vertices for the polygon.
This may prove to be considerably difficult.
Is the boundary fixed from the beginning, or can you deform it a bit?
If I had to solve this, I would sort the areas from large to small. Then, starting with the largest area, I would first generate a random convex polygon (vertices along a circle) with the required size. The next area would share an edge with the first area, but would be otherwise also random and convex. Each polygon after that would choose an existing edge from already-present polygons, and would also share any 'convex' edges that start from there (where 'convex edge' is one that, if used for the new polygon, would result in the new polygon still being convex).
By evaluating different prospective polygon positions for 'total boundary approaches desired boundary', you can probably generate a cheap approximation to your initial goal. This is quite similar to what word-clouds do: place things incrementally from largest to smallest while trying to fill in a more-or-less enclosed space.
Given a set of voronio centres (i.e. a list of the coordinates of the centre for each one), we can calculate the area closest to each centre:
area[i] = areaClosestTo(i,positions)
Assume these are a bit wrong, because we haven't got the centres in the right place. So we can calculate the error in our current set by comparing the areas to the ideal areas:
var areaIndexSq = 0;
var desiredAreasMagSq = 0;
for(var i = 0; i < areas.length; ++i) {
var contrib = (areas[i] - desiredAreas[i]);
areaIndexSq += contrib*contrib;
desiredAreasMagSq += desiredAreas[i]*desiredAreas[i];
}
var areaIndex = Math.sqrt(areaIndexSq/desiredAreasMagSq);
This is the vector norm of the difference vector between the areas and the desiredAreas. Think of it like a measure of how good a least squares fit line is.
We also want some kind of honeycomb pattern, so we can call that honeycombness(positions), and get an overall measure of the quality of the thing (this is just a starter, the weighting or form of this can be whatever floats your boat):
var overallMeasure = areaIndex + honeycombnessIndex;
Then we have a mechanism to know how bad a guess is, and we can combine this with a mechanism for modifying the positions; the simplest is just to add a random amount to the x and y coords of each centre. Alternatively you can try moving each point towards neighbour areas which have an area too high, and away from those with an area too low.
This is not a straight solve, but it requires minimal maths apart from calculating the area closest to each point, and it's approachable. The difficult part may be recognising local minima and dealing with them.
Incidentally, it should be fairly easy to get the start points for the process; the centroids of the pie slices shouldn't be too far from the truth.
A definite plus is that you could use the intermediate calculations to animate a transition from pie to voronoi.
I have used the code in the following :
http://www.amphibian.com/blogstuff/collision.html. in the html test file I have changed the the first triangle to
triangle1.addPoint({"x":-20, "y":-20});
triangle1.addPoint({"x":-20, "y":20});
triangle1.addPoint({"x":20, "y":20});
triangle1.addPoint({"x":20, "y":10});
triangle1.addPoint({"x":10, "y":10});
triangle1.addPoint({"x":10, "y":-20});
now when I move the other triangle with inside this shape before crossing it gives me wrongly intersection. Any idea where could be the problem?
All right, I set up a fiddle for anyone else wanting to play with this. Here are the results:
The script makes use of the Separating Axis Theorem or (as Wikipedia calls it) Hyperplane separation theorem, as explained in the source of polygon.js:
/*
* To detect intersection with another Polygon object, this
* function uses the Separating Axis Theorem. It returns false
* if there is no intersection, or an object if there is. The object
* contains 2 fields, overlap and axis. Moving the polygon by overlap
* on axis will get the polygons out of intersection.
*/
Polygon.prototype.intersectsWith = function(other) {
This theorem only applies to convex polygons. Your shape is not convex as it has a "dent" in it. That's why the script incorrectly reports that the shapes are intersecting. If you need to make it work with concave shapes, you'll have to make it first split the concave shape up in separate convex parts and then apply the theorem to all individual parts. Obviously, this makes the script more complex as you need to iterate over the cross product of the concave parts of two shapes.
Here is my not too complicated implementation of finding the intersection polygon of two polygons.
It works for convex and concave polygons, but not for complex (self-intersecting) polygons.
Algorithm is fairly similar to the one presented in Margalit & Knott.
Its complexity is about 4*n1*n2, where n1 and n2 are numbers of vertices in polygons whose intersection is being calculated.
It is a single standalone .js file. A 'Polygon' is regarded any javascript array of 2D Points. A 'Point' is any javascript object with x and y numeric properties.
Implementing the Union functionality on top of existing one should not be a problem and I will do it probably soon.
Intersection of 2D polygons