I would like to rotate a polygon
I have array of polygon like this
[ [-17.999999999999986, 587.25], [-14, 197.25], [544, 169.25], [554, 551.25] ]
Fist step: I calculate the Centroid
function getCentroid(coord) {
var center = coord.reduce(function (x,y) {
return [x[0] + y[0]/coord.length, x[1] + y[1]/coord.length]
}, [0,0])
return center;
}
Seconde Step: Rotation:
function rotate(CX, CY, X, Y, angle) {
var rad = angle * (Math.PI / 180.0);
var nx = Math.cos(rad) * (X-CX) - Math.sin(rad) * (Y-CY) + CX;
var ny = Math.sin(rad) * (X-CX) + Math.cos(rad) * (Y-CY) + CY;
return [nx,ny];
}
the problem is every time I rotate the polygon it become biger.
Maybe I have a problem in the formula but this one is used by alot of programmers.
Thank you for your answers.
You tagged svg.js so I assume that you are using it.
So here is how I would do it.
// assuming that you have a div with the id "canvas" here
var canvas = SVG('canvas')
var angle = 20
// draw polygon
var polygon = canvas.polygon([ [-18, 587.25], [-14, 197.25], [544, 169.25], [554, 551.25] ])
.fill('none')
.stroke('black')
// we clone it so we have something to compare
var clone = polygon.clone()
// get center of polygon
var box = polygon.bbox()
var {cx, cy} = box
// get the values of the points
var rotatedPoints = polygon.array().valueOf().map((p) => {
// transform every point
var {x, y} = new SVG.Point(p)
.transform(new SVG.Matrix().rotate(angle, cx, cy))
return [x, y]
})
// update polygon with points
polygon.plot(rotatedPoints)
Fiddle: https://jsfiddle.net/Fuzzy/3qzubk5y/1/
Ofc you dont need to make a polygon first to rotate the points. You can go straight withy our array and call the same map function on it. But in this case you need to figure out the cx and cy yourself:
function getCentroid(coord) {
var center = coord.reduce(function (x,y) {
return [x[0] + y[0]/coord.length, x[1] + y[1]/coord.length]
}, [0,0])
return center;
}
var canvas = SVG("canvas")
var points = [ [-18, 587.25], [-14, 197.25], [544, 169.25], [554, 551.25] ]
var center = getCentroid(points)
var angle = 20
// polygon before rotation
canvas.polygon(points).fill('none').stroke('black')
// get the values of the points
var rotatedPoints = points.map((p) => {
// transform every point
var {x, y} = new SVG.Point(p)
.transform(new SVG.Matrix().rotate(angle, center[0], center[1]))
return [x, y]
})
// polygon after rotation
canvas.polygon(rotatedPoints).fill('none').stroke('black')
Fiddle: https://jsfiddle.net/Fuzzy/g90w10gg/
So since your centroid function seems to work, your mistake has to be somewhere in your rotate function. However, I prefer to use the capcabilities of the libraries when I have them there. No need to reinvent the weel :)
// EDIT:
I pimped your centroid function a bit so the variable naming is a bit more clear:
function getCentroid(coord) {
var length = coord.length
var center = coord.reduce(function (last, current) {
last.x += current[0] / length
last.y += current[1] / length
return last
}, {x: 0, y: 0})
return center;
}
Related
const coords = [
{
name: "Rijnstraat vervolg",
points: [
[695, 500],
[680, 480],
[580, 475],
[520, 460],
],
width: 10,
types: [types.car, types.truck, types.pedestrian, types.bike],
oneway: true,
},
...
]
I have an array that looks like the above and I want to make a function that generates a path (along the other paths, which are the black lines in the image) from a black or gray circle to another black or gray circle. So I want the function to take in a start and end point (black or gray circle) and return an array of points that follow the already existings paths. (Which are sort of like roads)
And the function can be described as someone who is trying to get to somewhere.
I already tried a recursive function that looks like this:
function calculatePathToShop(startPoint, shopPoint) {
const targetShopPoint = findClosestPointOnPath(shopPoint);
const targetPathIndex = findPathByPoint(targetShopPoint);
const connectedPaths = calculateConnectedPaths(targetPathIndex);
let startPathIndex = -1;
connectedPaths.forEach(path => {
const pathPoints = coords[path].points;
pathPoints.forEach(pathPoint => {
if (comparePoints(startPoint.point, pathPoint)) startPathIndex = path;
});
});
if (startPathIndex == -1) return false;
let startPathPoints = coords[startPathIndex].points;
let targetPathPoints = coords[targetPathIndex].points;
if (!comparePoints(startPoint.point, startPathPoints[0])) startPathPoints.reverse();
ctx.strokeStyle = "rgba(255, 0, 0, .05)";
}
This one generated a path (along the existing ones) to a shop point, which is almost the same as a gray point. But this worked for some starting points, but the rest would just straight up fail
So does anyone know an algorithm, or has a function/solution that I can use to generate the path that someone can walk along the road (the black lines in the image)
Full coords array, and part of my already existing code is found here: https://raw.githubusercontent.com/CodeFoxDev/people-simulation/main/func/paths.js
(The rest of the code is in the github repo itself)
Fixed step interpolation
To interpolate a line segment you divide the vector from the start pointing to the end by the number of steps.
EG
steps = 100;
start = {x: 50, y: 100}
end = {x: 150, y: 300}
step = {x: (end.x - start.x) / steps, y: (end.y - start.y) / steps};
Then loop that number of steps adding the vector to a position initialized to the start point.
points = []; // array of interpolated points
point = {...start} // set start position.
while (steps--) {
points.push({...point});
point.x += vec.x;
point.y += vec.y;
}
points.push({...end}); // last point at end
This will create different spacing for different line lengths.
Fixed distance interpolation
To get a constant spacing between points you will need to use the lines' length to get the number of steps.
pixelsPerStep = 2; // distance between points.
start = {x: 50, y: 100}
end = {x: 150, y: 300}
step = {x: end.x - start.x, y: end.y - start.y};
lineSteps = Math.hypot(step.x, step.y) / pixelsPerStep;
points = []; // array of interpolated points
for (i = 0; i < lineSteps ; i += 1) {
u = i / lineSteps;
points.push({x: start.x + step.x * u, y: start.y + step.y * u});
}
// check to add end point
Note that the last point may or may not be at the correct distance. Due to rounding errors in floating point numbers you will need to check if the last point is close to the correct spacing and whether or not to include it.
eg from code above
// add last point if within (0.01 * pixelsPerStep) pixels of correct spacing
if (Math.abs(lineSteps - i) < 0.01) {
points.push({...end});
}
Note Use the overflow lineSteps - i when interpolating many line segments, to carry the correct start offset to each subsequent line segment.
Example
The code below is an example of a constant spaced set of points interpolated from another set of points.
The example draws the new points in black dots. The original points are rendered in red.
Note that the distance between new points is constant and thus may not fall on the original (red) points.
Note that there is a check at the end to test if a last point should be added.
const ctx = canvas.getContext("2d");
const P2 = (x, y) => ({x, y});
const points = [
P2(100,90),
P2(300,210),
P2(350,110),
P2(50,10),
P2(6,219),
];
const interpolatedPoints = interpolatePath(points, 35);
drawPoints(interpolatedPoints, 2);
ctx.fillStyle = "RED";
drawPoints(points);
function drawPoints(points, size = 1) {
ctx.beginPath();
for (const p of points) {
ctx.rect(p.x - size, p.y - size, size * 2 + 1, size * 2 + 1);
}
ctx.fill();
}
function interpolatePath(path, pixelStep) {
const res = [];
var p2, i = 1, overflow = 0;
while (i < path.length) {
const p1 = path[i - 1];
p2 = path[i];
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
const len = Math.hypot(dx, dy) / pixelStep;
let j = overflow;
while (j < len) {
const u = j / len;
res.push(P2(p1.x + dx * u, p1.y + dy * u));
j++;
}
overflow = j - len;
i++;
}
// add last point if close to correct distance
if (Math.abs(overflow) < 0.01) {
res.push(P2(p2.x, p2.y));
}
return res;
}
<canvas id="canvas" width="400" height="400"></canvas>
I want to be able to calculate the surface area of a 2D polygon of any shape, given a set of 3D vertices. For example, what is the surface area of this figure?
var polygon = new Polygon([new Point(0,0,0), new Point(5,8,2), new Point(11,15,7)])
polygon.areaIfPolygonIs3D()
--> some predictable result, no matter how many vertices the polygon has...
Keep in mind that polygons only have one surface. They are flat but could be triangle shaped or trapezoid shaped or randomly shaped, and could be floating at a 3D angle... imagine them as pieces of paper turned any which way in 3D space.
What I've tried to do so far is rotate the thing flat, and then use a basic formula for calculating the area of a 2D irregular polygon which is currently working in my code (formula: http://www.wikihow.com/Calculate-the-Area-of-a-Polygon). I had such a hard figuring out how to rotate all the vertices so the polygon lays flat (all "z" values are 0) that I abandoned that path, though I'm open to trying it if someone can get there. (Perhaps there is a bug in Point.rotateBy().)
I can work with Points, and Edges (created with point.to(point)), and Edges have 'theta' (edge.theta()) and 'phi' (edge.phi()).
In any case, if someone can fill in what goes here and help me after a full days effort of trying to relearn all the geometry I forgot from high school, that would be much appreciated!
var locatorRho = function(x,y,z) {
return Math.sqrt(x*x + y*y + z*z);
}
var locatorTheta = function(x,y) {
return Math.atan2(y,x);
};
var locatorPhi = function(x,y,z) {
return z == 0 ? Math.PI_2 : Math.acos(z/locatorRho(x, y, z));
}
// rotates a point according to another point ('locator'), and their 2D angle ('theta') and 3D angle ('phi')
Point.prototype.rotateBy = function(locator, theta, phi) {
phi = (phi == undefined ? 0 : phi);
var relativeX = this.x() - locator.x();
var relativeY = this.y() - locator.y();
var relativeZ = this.z() - locator.z();
var distance = locatorRho(relativeX, relativeY, relativeZ);
var newTheta = locatorTheta(relativeX, relativeY) + theta;
var newPhi = locatorPhi(relativeX, relativeY, relativeZ) + phi;
this._x = locatorX(distance, newTheta, newPhi) + locator.x();
this._y = locatorY(distance, newTheta, newPhi) + locator.y();
this._z = locatorZ(distance, newPhi) + locator.z();
}
Polygon.prototype.signedArea = function() {
var vertices = this.vertices();
var area = 0;
for(var i=0, j=1, length=vertices.length; i<length; ++i, j=(i+1)%length) {
area += vertices[i].x()*vertices[j].y() - vertices[j].x()*vertices[i].y();
}
return 0.5*area
}
Polygon.prototype.areaIfPolygonIs2D = function() {
return Math.abs(rotatedFlatCopy.signedArea())
}
Polygon.prototype.areaIfPolygonIs3D = function() {
... help here I am so stuck ...
}
var vertices = [some number of Points, e.g., new Point(x,y,z)]
var polygon = new Polygon(vertices)
var polygon.areaIfPolygonIs3D()
--> result
If your polygon plane is not parallel to Z axis, you can calculate area projection with known approach using X and Y coordinates only, then divide result by cosine of angle between Z axis and normal N to that plane
Area = Sum[x1*y2-x2*y1 +...] ////shoelace formula
True_Area = Area / Cos(Angle between N and Z axis)) =
Area / DotProduct((N.x,N.y,N.z), (0,0,1)) =
Area / N.z
//// if N is normalized (unit)
Use the shoelace formula three times, on the 2D vertices (X, Y), (Y, Z) and (Z, X). The desired area is given by √Axy²+Ayz²+Azx² (provided the polygon is flat).
In easelJS, what is the best way to rotate an object around another? What I'm trying to accomplish is a method to rotate the crosshair around the circle pictured below, just like a planet orbits the sun:
I've been able to rotate objects around their own center point, but am having a difficult time devising a way to rotate one object around the center point of a second object. Any ideas?
Might make sense to wrap content in a Container. Translate the coordinates so the center point is where you want it, and then rotate the container.
To build on what Lanny is suggesting, there may be cases where you don't want to rotate the entire container. An alternative would be to use trigonometric functions and an incrementing angle to calculate the x/y position of the crosshair. You can find the x/y by using an angle (converted to radians) and Math.cos(angleInRadians) for x and Math.sin(angleInRadians) for y, the multiply by the radius of the orbit.
See this working example for reference.
Here's a complete snippet.
var stage = new createjs.Stage("stage");
var angle = 0;
var circle = new createjs.Shape();
circle.graphics.beginFill("#FF0000").drawEllipse(-25, -25, 50, 50).endFill();
circle.x = 100;
circle.y = 100;
var crosshair = new createjs.Shape();
crosshair.graphics.setStrokeStyle(2).beginStroke("#FF0000").moveTo(5, 0).lineTo(5, 10).moveTo(0, 5).lineTo(10, 5).endStroke();
stage.addChild(circle);
stage.addChild(crosshair);
createjs.Ticker.addEventListener("tick", function(){
angle++;
if(angle > 360)
angle = 1;
var rads = angle * Math.PI / 180;
var x = 100 * Math.cos(rads);
var y = 100 * Math.sin(rads);
crosshair.x = x + 100;
crosshair.y = y + 100;
stage.update();
});
Put another point respect to origin point with the same direction
var one_meter = 1 / map_resolution;
// get one meter distance from pointed points
var extra_x = one_meter * Math.cos(temp_rotation);
var extra_y = one_meter * Math.sin(-temp_rotation);
var new_x = mapXY.x + extra_x;
var new_y = mapXY.y + extra_y;
var home_point = new createjs.Shape().set({ x: new_x, y: new_y });
home_point.graphics.beginFill("Blue").drawCircle(0, 0, 10);
stage.addChild(home_point);
stage.update();
I have two hexagons which I am trying to make snap together when the edges hit a certain tolerance.
How can I find which edges are the closest?
Here is the code returning the two closest Hexagons:
Canvas.getClosestPiece = function(){
var current = {};
current.x = selection.MidPoint.X;
current.y = selection.MidPoint.Y;
smallestDistance = null;
closestHex = null;
hexagons.forEach(function(hexagon){
if(hexagon !== selection){
testPiece = {};
testPiece.x = hexagon.MidPoint.X;
testPiece.y = hexagon.MidPoint.Y;
if((lineDistance(current, testPiece) < smallestDistance) || smallestDistance === null){
smallestDistance = lineDistance(current, testPiece)
closestHex = hexagon
hexagons.forEach(function(hexagon){
hexagon.lineColor = 'grey'
})
hexagon.lineColor = 'red';
}
}
})
// console.log(smallestDistance)
return [selection, closestHex]
}
Distance between two hexagon midpoints:
function lineDistance( point1, point2 ){
var xs = 0;
var ys = 0;
xs = point2.x - point1.x;
xs = xs * xs;
ys = point2.y - point1.y;
ys = ys * ys;
return Math.sqrt( xs + ys );
}
And here is a standard point array for one of the hexagons that getClosestPiece returns:
Point {X: 658, Y: 284}
Point {X: 704, Y: 304}
Point {X: 704, Y: 354}
Point {X: 658, Y: 375}
Point {X: 613, Y: 354}
Point {X: 613, Y: 304}
If your have 2 points with their coordinate like p1(x1, y1) and p2(x2, y2). You can do this:
var disptance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
For calculating if to snap, see the other answers.
As to where to snap (which edges), which I think is your real question: calculate the relative angle using
atan2(midy1-midy2, midx1-midx2).
You get a value in radians, which describes the angle of the connection line between the hexes. 0 = horizontal line.
Calculate Math.floor(value*6/(2*pi)) --> you get a number between 0..5 denoting the edge pairing.
If your hexes are rotatable, you need to add/substract the rotatins (in rad) to/from value. (The signs are best figured out on a piece of paper).
edit: regarding your distance calculation, it is advisable to work with the square of the distance as long as possible (e.g. compare x^2+y^2 against threshold^2), to avoid the expensive Math.sqrt operation. Especially when testing distance against a multitude of other objects.
Use Euclian Distance formula
dist=sqrt((x2-xq)^2 + (y2-y1)^2)
to find which edges are the closest you hav to say us that how do you have information of the edge lines of each hexagon. here, i assume they are accessible through an array as a property of each hexagon. so we have 6 edges (edges[0] to edges[5]) for each hexagon. we can find closest edges by looping through them and measuring the distance between center of each two edges. a sample code will look like this:
var dMin=-1, iMin=-1, jMin=-1; //info about the min distance
for(var i=0; i<5; i++) //loop through hexagon1.edges
{
var p1 = midPointOfLine( hexagon1.edges[i] ); //center of this edge line
for(var j=0; j<5; j++) //loop through hexagon2.edges
{
var p2 = midPointOfLine( hexagon2.edges[j] ); //center of this edge line
var d = getDistance(p1, p2); //get distance of two points
if (d<dMin || dMin==-1) {dMin=d; iMin=i; jMin=j;} //store the info about the min distance
}
}
function midPointOfLine(edge) // return new point( X=(X1+X2)/2 , Y=(Y1+Y2)/2 )
{
var mp; //define a new point
mp.X = (edge.startPoint.X + edge.endPoint.X) / 2;
mp.Y = (edge.startPoint.Y + edge.endPoint.Y) / 2;
return mp;
}
function getDistance(p1, p2) //return sqrt( (X2-X1)^2 + (Y2-Y1)^2 )
{
return Math.sqrt( Math.pow(p2.X - p1.X, 2) + Math.pow(p2.Y - p1.Y, 2) );
}
In Summary:
Check distance between center of each edge of hexagon1 and center of
each edge of hexagon2.
The center of each edge is mid point of its
start and end points: ( (x1+x2)/2, (y1+y2)/2 ).
The distance of two points can be calculated from sqrt(dx*dx + dy*dy) formula.
What is the algorithm for storing the pixels in a spiral in JS?
http://www.mathematische-basteleien.de/spiral.htm
var Spiral = function(a) {
this.initialize(a);
}
Spiral.prototype = {
_a: 0.5,
constructor: Spiral,
initialize: function( a ) {
if (a != null) this._a = a;
},
/* specify the increment in radians */
points: function( rotations, increment ) {
var maxAngle = Math.PI * 2 * rotations;
var points = new Array();
for (var angle = 0; angle <= maxAngle; angle = angle + increment)
{
points.push( this._point( angle ) );
}
return points;
},
_point: function( t ) {
var x = this._a * t * Math.cos(t);
var y = this._a * t * Math.sin(t);
return { X: x, Y: y };
}
}
var spiral = new Spiral(0.3);
var points = spiral.points( 2, 0.01 );
plot(points);
Sample implementation at http://myweb.uiowa.edu/timv/spiral.htm
There are a couple of problems with this question. The first is that you're not really being specific about what you're doing.
1) Javascript isn't really a storage medium, unless you're looking to transmit the pixels using JSON, in which case you may want to rephrase to explicitly state that.
2) There's no mention of what you expect the spiral to look like - are we talking about a loose spiral or a tight spiral? Monocolor or a gradient or a series of colors ? Are you looking at a curved spiral or a rectangular one?
3) What is the final aim here? Are you looking to draw the spiral directly using JS or are you transmitting it to some other place?