Calculate rectangles angle knowing the outline positions (x/y) - javascript
I got an matrix of ones and zeros like this (& I hope you can get the rectangle of ones):
And I'd like to calculate the rectangles rotation angle.
So what I'm starting with: I now all of the positions (x/y-coordinates) of the rectangles outline (symbolized with ' ' (spaces) in the image above) and got them in an array like this:
var outline_positions = [[120,22],[122,22],...,[94,119],[93,119]]
So now my question: How is it possible to calculate the rectangles rotation-angle to the X-axis, knowing that the long side of the rectangle at angle 0° is parallel to the X-asis?
Because I got now idea how to start this code is all that I got so far:
var positions = [[120,22],[122,22],[120,22],[121,22],[122,22],[119,23],[125,23],[119,24],[127,24],[118,25],[129,25],[118,26],[131,26],[117,27],[132,27],[117,28],[134,28],[117,29],[137,29],[116,30],[139,30],[115,31],[141,31],[115,32],[142,32],[114,33],[142,33],[113,34],[142,34],[113,35],[142,35],[112,36],[141,36],[111,37],[140,37],[111,38],[140,38],[110,39],[139,39],[109,40],[139,40],[109,41],[138,41],[108,42],[138,42],[108,43],[137,43],[106,44],[137,44],[106,45],[136,45],[105,46],[136,46],[105,47],[135,47],[104,48],[135,48],[103,49],[134,49],[103,50],[134,50],[103,51],[133,51],[102,52],[132,52],[101,53],[132,53],[100,54],[131,54],[100,55],[131,55],[99,56],[130,56],[98,57],[129,57],[97,58],[128,58],[97,59],[128,59],[96,60],[127,60],[95,61],[127,61],[95,62],[126,62],[94,63],[126,63],[94,64],[125,64],[93,65],[124,65],[92,66],[124,66],[92,67],[124,67],[91,68],[123,68],[91,69],[122,69],[90,70],[121,70],[89,71],[121,71],[89,72],[120,72],[88,73],[120,73],[87,74],[119,74],[87,75],[118,75],[86,76],[118,76],[86,77],[116,77],[85,78],[116,78],[85,79],[116,79],[83,80],[115,80],[83,81],[115,81],[82,82],[115,82],[81,83],[114,83],[81,84],[113,84],[80,85],[113,85],[80,86],[112,86],[79,87],[112,87],[78,88],[112,88],[78,89],[111,89],[78,90],[110,90],[77,91],[110,91],[76,92],[109,92],[76,93],[109,93],[75,94],[108,94],[74,95],[107,95],[74,96],[107,96],[73,97],[106,97],[73,98],[105,98],[72,99],[105,99],[72,100],[104,100],[71,101],[104,101],[70,102],[103,102],[70,103],[103,103],[69,104],[102,104],[70,105],[102,105],[72,106],[101,106],[73,107],[101,107],[75,108],[100,108],[76,109],[100,109],[77,110],[99,110],[80,111],[98,111],[81,112],[98,112],[83,113],[97,113],[84,114],[96,114],[86,115],[96,115],[88,116],[96,116],[90,117],[95,117],[91,118],[94,118],[93,119],[94,119],[94,119],[93,119]]
Array.prototype.calculate_rotation = function() {
var array=this
var max_x = array.filter(e => e[0] === Math.max(... array.map(e => e[0])))[0];
var min_x = array.filter(e => e[0] === Math.min(... array.map(e => e[0])))[0]
return max_x[1]-min_x[1]
}
console.log(positions.calculate_rotation());
One approach could be as follows:
function calculate_rotation(arr){
var N = arr.length;
if(!N) return;
var xmin = arr[0][0];
var ymin = arr[0][1];
var A = arr[0];
var P = arr[0];
for(var i = 0; i < N; ++i){
var x = arr[i][0];
var y = arr[i][1];
if(x < xmin || (x == xmin && y < A[1])){
xmin = x;
A = [x, y];
}
if(y < ymin || (y == ymin && x > P[0])){
ymin = y;
P = [x, y];
}
}
return Math.atan2(P[1] - A[1], P[0] - A[0])*180/Math.PI;
}
The idea is to identify the left-most (A) and bottom-most (P) points. The rotation angle is then calculated with respect to the point A. Alternatively, one could do a second pass through the array, identify all points which are on the line segment connecting A and P and do a linear fit in order to obtain the slope.
Related
Calculate volume of FOV and loop inside
I want to improve performances in my game and for that, I need to loop inside the visible volume. I know that this is very slow to calculate, but I really think that it'll be better that today. I have these values: Camera position: x, y, z Camera rotation: rx, ry, rz Render distance: d Render function: r I've tried this code but r is never executed: const MAP_LIMIT = [150,25,150]; // Limit of the map (x,y,z) (x=0,y=0,z=0,d=10,ox=0,oy=0,oz=0,r) => { x = Int(x); // Int = Math.round y = Int(y); z = Int(z); const RoundToMapLimit = (n=0,t=0) => n <= 0 ? 0 : (n >= MAP_LIMIT[t] ? MAP_LIMIT[t] : n); const obj = new THREE.Object3D(); obj.position.set(x,y,z); obj.rotation.set(ox,0,0); obj.translateX(d); const xRange = [RoundToMapLimit(x),RoundToMapLimit(obj.position.x)]; obj.translateX(-d); obj.rotation.set(0,oy,0); obj.translateY(d); const yRange = [RoundToMapLimit(y,1),RoundToMapLimit(obj.position.y,1)]; obj.translateY(-d); obj.rotation.set(0,0,oz); obj.translateZ(d); const zRange = [RoundToMapLimit(z,2),RoundToMapLimit(obj.position.z,2)]; for (let y2 = Math.min(...yRange); y2 < Math.max(...yRange); ++y2) { for (let x2 = Math.min(...xRange); x2 < Math.max(...xRange); ++x2) { for (let z2 = Math.min(...zRange); z2 < Math.max(...zRange); ++z2) { r([x2,y2,z2]); } } } }; Can you please help me to fix this function ? Thank you
polygon weird redrawing by adding & dragging dynamic points (flickering)
I have co-ordinates for the points by taking which I draw a polygon. I can add points dynamically on the edges of the polygon and when I drag any point it should drag only the connected lines. As points can be added later on the edges so the point co-ordinates need to be ordered/sorted and the polygon should be redrawn by taking the ordered/sorted points so that on dragging any point the lines connected to the dragged point only should be dragged/updated. So to order/sort the points I am sorting the co-ordinates(2D-points) clockwise using Graham Scan/ sorting by polar angle. My sorting code is I find the center of the polygon like function findCenter(points) { let x = 0, y = 0, i, len = points.length; for (i = 0; i < len; i++) { x += Number(points[i][0]); y += Number(points[i][1]); } return { x: x / len, y: y / len }; // return average position } Then I sort the points by finding angles of each point from the center like function findAngle(points) { const center = findCenter(points); // find angle points.forEach((point) => { point.angle = Math.atan2(point[1] - center.y, point[0] - center.x); }); } //arrVertexes is the array of points arrVertexes.sort(function (a, b) { return a.angle >= b.angle ? 1 : -1; }); But the problem I am facing is if I drag any point more towards opposite side and add a new point on the edges afterward and drag the newly added point the sorting of co-ordinates is not ordered exactly because of which there is a flickering while dragging. Here is a pictorial view of the problem I am facing for quick understanding. Initially my svg looks like After this I add a point and dragged like Then I added one more point like once I drag the added point towards down, it redraws the polygon something like (is not it weird ?) Actually It should be like NOTE: I really don't know what logic should I apply to get the desire functionality. Seeking help from the community leads. Demo App So I am looking for a solution that won't give me weird redrawing of the lines. Only the connected lines to the dragged point should be dragged. EDIT I came up with MUCH BETTER solution. The only problem with this approach is, When I try to add a new point on left-vertical line and If I try to move it, that newly added point moves to top-horizontal line Updated-Demo
I've fixed this bug with left line. Take a look: codepen. I changed getClosestPointOnLines function (actually refactored a little): as I understood, the result here is to get i - the index for the new point in array, so I moved the algorithm to new function getI I changed getI to use not only n (current index), but just 2 any indexes: n1 and n2: const getI = (n1, n2) => { So all your aXys[n] is now a1 and aXys[n - 1] is now a2. the result of getI is return i; - this is what we want from this function I added new function-helper updateI. It calls getI and check if there any positive result. const updateI = (n1, n2) => { const newI = getI(n1, n2); if (newI !== undefined) { i = newI; return true; } }; So your loop over points is now: for (let n = 1; n < aXys.length; n++) { updateI(n, n - 1); } But we need to check "left" line separately (because it connects begin and end of the array): if (updateI(aXys.length - 1, 0)) i = aXys.length; Sorry, but I disabled part of your code. I did not check where do you use it: if (i < aXys.length) { let dx = aXys[i - 1][0] - aXys[i][0]; let dy = aXys[i - 1][1] - aXys[i][1]; x = aXys[i - 1][0] - dx * fTo; y = aXys[i - 1][1] - dy * fTo; } So the final version of getClosestPointOnLines now looks like this: function getClosestPointOnLines(pXy, aXys) { var minDist; var fTo; var fFrom; var x; var y; var i; var dist; if (aXys.length > 1) { const getI = (n1, n2) => { let i; const a1 = aXys[n1]; const a2 = aXys[n2]; if (a1[0] != a2[0]) { let a = (a1[1] - a2[1]) / (a1[0] - a2[0]); let b = a1[1] - a * a1[0]; dist = Math.abs(a * pXy[0] + b - pXy[1]) / Math.sqrt(a * a + 1); } else dist = Math.abs(pXy[0] - a1[0]); // length^2 of line segment let rl2 = Math.pow(a1[1] - a2[1], 2) + Math.pow(a1[0] - a2[0], 2); // distance^2 of pt to end line segment let ln2 = Math.pow(a1[1] - pXy[1], 2) + Math.pow(a1[0] - pXy[0], 2); // distance^2 of pt to begin line segment let lnm12 = Math.pow(a2[1] - pXy[1], 2) + Math.pow(a2[0] - pXy[0], 2); // minimum distance^2 of pt to infinite line let dist2 = Math.pow(dist, 2); // calculated length^2 of line segment let calcrl2 = ln2 - dist2 + lnm12 - dist2; // redefine minimum distance to line segment (not infinite line) if necessary if (calcrl2 > rl2) dist = Math.sqrt(Math.min(ln2, lnm12)); if (minDist == null || minDist > dist) { if (calcrl2 > rl2) { if (lnm12 < ln2) { fTo = 0; //nearer to previous point fFrom = 1; } else { fFrom = 0; //nearer to current point fTo = 1; } } else { // perpendicular from point intersects line segment fTo = Math.sqrt(lnm12 - dist2) / Math.sqrt(rl2); fFrom = Math.sqrt(ln2 - dist2) / Math.sqrt(rl2); } minDist = dist; i = n1; } return i; }; const updateI = (n1, n2) => { const newI = getI(n1, n2); if (newI !== undefined) { i = newI; return true; } }; for (let n = 1; n < aXys.length; n++) { updateI(n, n - 1); } if (updateI(aXys.length - 1, 0)) i = aXys.length; if (i < aXys.length) { let dx = aXys[i - 1][0] - aXys[i][0]; let dy = aXys[i - 1][1] - aXys[i][1]; x = aXys[i - 1][0] - dx * fTo; y = aXys[i - 1][1] - dy * fTo; } } console.log(aXys[i - 1]); return { x: x, y: y, i: i, fTo: fTo, fFrom: fFrom }; } Working example on codepen.
You should not allow any point to be added that is not close to a line. When the user clicks, use the distance from a point to a line algorithm to check each line to see if the click is within an acceptable distance of the line. Perhaps a few pixels. If more than one line is within an acceptable distance, perhaps choose the one that is closest. You now know where in the array to insert the new point. It will be between the first and second points of the line that just matched. If you do that, the shape drawing should just work.
Biggest distance between different points based on x-values and y-values?
I got a bunch of nodes which are stored in an array arr. Each node has a x and y value which repesents the position on the screen. Now, i created the middle element of arr and save it in middle. Now, my goal is, to find out the distance between middle and all other nodes and also find out the one with the maximum distance. For the distance I use the Pythagorean theorem a^2 + b^2 = c^2, that means sqrt(a^2 + b^2) = c or in my case sqrt(x^2 + y^2) = distance between 2 nodes. For example to create the distance between (10,10) and (20,30) I create the difference of the x-scale and the y-scale, that means x = 20-10 = 10 and y = 30-10 = 20. The result is, that the distance between those nodes is sqrt( 10^2 + 20^2) = 22,3. In my code, I check with the if-loop, which x-value and y-value is bigger to avoid negative values. But something I made is wrong. Maybe someone can help? var middle = arr[Math.floor(arr.length / 2)]; var arrayForDistance = []; var distance = []; for(i = 0; i != arr[middle] & i< arr.length; i++ ) { if(arr[i].x > arr[middle].x) { var newX = arr[i].x - arr[middle].x; var newY = arr[i].y - arr[middle].y; } else if ( arr[i].x < arr[middle].x) { var newX = arr[middle].x - arr[i].x; var newY = arr[middle].y - arr[i].y; }} distance = sqrt( newX^2 + newY^2) arrayForDistance.push(distance[i]); } var maxDistance = Math.max.apply(null, arrayForDistance)
First of all you dont need to worry about negatives since you are squareing them, they will cancel out. secondly your for loop is wrong it should be var middle = arr[Math.floor(arr.length / 2)]; var arrayForDistance = []; var distance ; for(i = 0; i< arr.length; i++ ) { if (i != Math.floor(arr.length / 2)){ var newX = arr[i].x - arr[middle].x; var newY = arr[i].y - arr[middle].y; distance = sqrt( newX^2 + newY^2) arrayForDistance.push(distance); } } var maxDistance = Math.max.apply(null, arrayForDistance)
Point in Polygon falsely detected
Derived from this: How to tackle diagonally stacked, rounded image background element hovers? I made imagemap areas and transformed them for my case, but, now there is a problem with point in polygon hit detection. It appears that only the bottom right quadrant is always correct, but, only if looking outside the ring - inside the detection might be still be incorrect. Other quadrants, outside the ring, occasionally report a positive hit where it should be false. Fiddle: http://jsfiddle.net/psycketom/9J4dx/1/ The red lines are drawn from the polygon that's generated from data-map. The blue line represents the polygon we're currently checking. The point in polygon function comes from: https://github.com/substack/point-in-polygon var pointInPolygon = function(point, vs) { // ray-casting algorithm based on // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html var x = point[0], y = point[1]; var inside = false; for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) { var xi = vs[i][0], yi = vs[i][1]; var xj = vs[j][0], yj = vs[j][1]; var intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if (intersect) inside = !inside; } return inside; }; I cannot seem to understand what's the problem here.
Your mapToPolygon function doesn't convert the parsed points from string to number. Because of this, the pointInPolygon function ends up comparing the strings of the coordinates, not the actual coordinates. Using a parseInt on line 31 of the fiddle fixes the problem.
Create an off-screen canvas and use the context's .isPointInPath(x, y) function. Loop through all of your polygons (in your example you would loop through them in reverse because you have smallest last. The smallest would be the highest level / greatest z-index). On you get a hit (isPointInPath returns true) stop. Something like... var offcanvas = document.createElement("canvas"); ... var x = e.pageX - $ages.offset().left; var y = e.pageY - $ages.offset().top; revlayers.each(function() { var $elm = $(this); var poly = $elm.data("polygon"); var ctx = offcanvas.getContext("2d"); if(poly.length > 0) { ctx.beginPath(); ctx.moveTo(poly[0][0], poly[0][1]); for(var i=1; i<poly.length; i++) { ctx.lineTo(poly[i][0], poly[i][1]); } if(ctx.isPointInPath(x, y)) { hit.text($elm.attr("href")); return false; // end the .each() loop } } })
Why is Firefox 30 times slower than Chrome, when calculating Perlin noise?
I have written a map generator in javascript, using classical perlin noise scripts I have found in various places, to get the functionality I want. I have been working in chrome, and have not experienced any problems with the map. However, when I tested it in firefox, it was incredibly slow - almost hanging my system. It fared better in the nightly build, but still 30 times slower than Chrome. You can find a test page of it here: http://jsfiddle.net/7Gq3s/ Here is the html code: <!DOCTYPE html> <html> <head> <title>PerlinMapTest</title> </head> <body> <canvas id="map" width="100" height="100" style="border: 1px solid red">My Canvas</canvas> <script src="//code.jquery.com/jquery-2.0.0.min.js"></script> <script> $(document).ready(function(){ //Log time in two ways var startTime = new Date().getTime(); console.time("Map generated in: "); var canvas = $("#map")[0]; var ctx = canvas.getContext("2d"); var id = ctx.createImageData(canvas.width, canvas.height); var noiseMap = new PerlinNoise(500); var startx = 0; var starty = 0; var value = 0; for(var i = startx; i < canvas.width; i++){ for(var j = starty; j < canvas.height; j++){ value = noiseMap.noise(i,j, 0, 42); value = linear(value,-1,1,0,255); setPixel(id, i, j, 0,0,0,value); } } ctx.putImageData(id,0,0); var endTime = new Date().getTime(); console.timeEnd("Map generated in: "); alert("Map generated in: " + (endTime - startTime) + "milliseconds"); }); function setPixel(imageData, x, y, r, g, b, a) { index = (x + y * imageData.width) * 4; imageData.data[index+0] = r; imageData.data[index+1] = g; imageData.data[index+2] = b; imageData.data[index+3] = a; } //This is a port of Ken Perlin's "Improved Noise" //http://mrl.nyu.edu/~perlin/noise/ //Originally from http://therandomuniverse.blogspot.com/2007/01/perlin-noise-your-new-best-friend.html //but the site appears to be down, so here is a mirror of it //Converted from php to javascript by Christian Moe //Patched the errors with code from here: http://asserttrue.blogspot.fi/2011/12/perlin-noise-in-javascript_31.html var PerlinNoise = function(seed) { this._default_size = 64; this.seed = seed; //Initialize the permutation array. this.p = new Array(512); this.permutation = [ 151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 ]; for (var i=0; i < 256 ; i++) { this.p[256+i] = this.p[i] = this.permutation[i]; } }; PerlinNoise.prototype.noise = function(x,y,z,size) { if (size == undefined) { size = this._default_size; } //Set the initial value and initial size var value = 0.0; var initialSize = size; //Add finer and finer hues of smoothed noise together while(size >= 1) { value += this.smoothNoise(x / size, y / size, z / size) * size; size /= 2.0; } //Return the result over the initial size return value / initialSize; }; //This function determines what cube the point passed resides in //and determines its value. PerlinNoise.prototype.smoothNoise = function(x, y, z){ //Offset each coordinate by the seed value x += this.seed; y += this.seed; z += this.seed; var orig_x = x; var orig_y = y; var orig_z = z; var X = Math.floor(x) & 255, // FIND UNIT CUBE THAT Y = Math.floor(y) & 255, // CONTAINS POINT. Z = Math.floor(z) & 255; x -= Math.floor(x); // FIND RELATIVE X,Y,Z y -= Math.floor(y); // OF POINT IN CUBE. z -= Math.floor(z); var u = this.fade(x), // COMPUTE FADE CURVES v = this.fade(y), // FOR EACH OF X,Y,Z. w = this.fade(z); var A = this.p[X ]+Y, AA = this.p[A]+Z, AB = this.p[A+1]+Z, // HASH COORDINATES OF B = this.p[X+1]+Y, BA = this.p[B]+Z, BB = this.p[B+1]+Z; // THE 8 CUBE CORNERS, return this.lerp(w, this.lerp(v, this.lerp(u, this.grad(this.p[AA ], x , y , z ), // AND ADD this.grad(this.p[BA ], x-1, y , z )), // BLENDED this.lerp(u, this.grad(this.p[AB ], x , y-1, z ), // RESULTS this.grad(this.p[BB ], x-1, y-1, z ))),// FROM 8 this.lerp(v, this.lerp(u, this.grad(this.p[AA+1], x , y , z-1 ), // CORNERS this.grad(this.p[BA+1], x-1, y , z-1 )), // OF CUBE this.lerp(u, this.grad(this.p[AB+1], x , y-1, z-1 ), this.grad(this.p[BB+1], x-1, y-1, z-1 )))); }; PerlinNoise.prototype.fade = function(t) { return t * t * t * ( ( t * ( (t * 6) - 15) ) + 10); }; PerlinNoise.prototype.lerp = function(t, a, b) { //Make a weighted interpolaton between points return a + t * (b - a); }; PerlinNoise.prototype.grad = function(hash, x, y, z) { h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE u = h<8 ? x : y; // INTO 12 GRADIENT DIRECTIONS. v = h<4 ? y : (h==12||h==14 ? x : z); return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v); }; PerlinNoise.prototype.scale = function(n) { return (1 + n)/2; }; function linear(int, s1, s2, t1, t2) { t = [t1, t2]; s = [s1, s2]; rangeS = s1 - s2; rangeT = t1 - t2; if((s1 < s2 && t1 > t2) || (s1>s2 && t1<t2)) { interpolated = ((int - s1) / rangeS*rangeT) + t1; } else { interpolated = ((int - s1) / rangeS)*rangeT + t1; } if(interpolated > Math.max.apply(Math, t)) { interpolated = Math.max.apply(Math, t); } if(interpolated < Math.min.apply(Math, t)) { interpolated = Math.min.apply(Math, t); } return interpolated; } </script> </body> </html> I get 33 ms on Chrome, and 1051ms on Firefox 24 Nightly The results are inconsistent though. Sometimes the Nightly results is as fast as chrome... Do you know why there is so much variation in this particular instance? I don't know enough about the theory of perlin noise to try optimizing the code, so don't know what to do.
I have found the culprit. The slowdown occurs when I have Firebug enabled. That extension must weigh it down.