I am looking at this example and one part of this does not make sense.
The code creates an array of values like this:
for (i = 0; i < 84; i++) {
data.push(i * 10 / 84);
}
It then uses this array to get both the x and y values for the graph where d is an element of the array:
sine
.x(function (d, i) { return xScale(d); })
.y(function (d, i) { return yScale(Math.sin(d - time)); });
Is 84 just an arbitrary number for the available width remaining for the graph or is there any particular reason of where this came from?
I think it is a number of points per circle... compromise between accuracy and speed. I usually use 36 for small circles and 90 for big. ... and few thousand for huge ones ... so the idea is to use as low count as possible while the circle still looks like circle (in max zoom) and not like polygon.
You can also compute this algebraically ...
da=2.0*M_PI/n
e=r-(r*cos(0.5*da))
where n is the number of line segments per circumference and e is the max distance from desired circle shape. if you set it to desired error in pixels (and radius r is in pixels) then:
n=M_PI/acos((r-e)/r)
Hopefully I did not make any mistake while deriving the equations directly in SO editor. So if you want really precise circle set e=0.4 [pixels] and you should be fine
[edit1] sin wave
The for loop creates list wit these properties:
d(i) = < 0.0 , 10.0 )
i = { 0,1,2,...83 }
Then the sinwave is rendered:
x(i) = xscale * d(i)
y(i) = yscale * sin(d(i)-time)
Which gives you:
x(i) = < 0.0 , xscale )
y(i) = < -yscale , +yscale )
So the sinwave renders 10/(2*PI)= ~ 1.59 periods. The half overlap is cut off by the view. So in theory you could use 6.28/84 -> 7/84 instead of 10/84 but it is maybe just safety value to handle different aspect ratio seettings of the rendering (I do not code in that platform so this is just speculation on my side) But as I said in the comments the sinwave is scaled so the PI period x size is equal to PI*circle_radius so the 84 most likely comes from the circle (my original answer).
This is possibly just a magic number, that is, completely arbitrary. In fact, as you said, the first thing I thought was that it is related to the width of the graph.
Here is a fiddle: https://jsfiddle.net/1nboube9/1/
You can tweak the number and see what happens. It seems to me that any number above 44 does the trick.
for (i = 0; i < 44; i++) {
data.push(i * 10 / 84);
}
But, of course, the path is not the same if you change the denominator as well:
for (i = 0; i < 44; i++) {
data.push(i * 10 / 44);
}
This creates a very different path. And, so, I tried this:
for (i = 0; i < someNumber; i++) {
data.push(i);
}
And it creates a very unpleasant path. So, I believe that this is what happened: the designer first created data.push(i * 10 / 84); to make the path more circular, and then changed the loop accordingly. Maybe I'm completely wrong, but that's my bet.
Related
This is a little tricky to explain, so bare with me. I'm attempting to design a 2D projection matrix that takes 2D pixel coordinates along with a custom world-space depth value, and converts to clip-space.
The idea is that it would allow drawing elements based on screen coordinates, but at specific depths, so that these elements would interact on the depth buffer with normal 3D elements. However, I want x and y coordinates to remain the same scale at every depth. I only want depth to influence the depth buffer, and not coordinates or scale.
After the vertex shader, the GPU sets depth_buffer=z/w. However, it also scales x/w and y/w, which creates the depth scaling I want to avoid. This means I must make sure my final clip-space w coordinate ends up being 1.0, to avoid those things. I think I could also adopt to scale x and y by w, to cancel out the divide, but I would rather do the former, if possible.
This is the process that my 3D projection matrix uses to convert depth into clip space (d = depth, n = near distance, f = far distance)
z = f/(f-n) * d + f/(f-n) * -n;
w = d;
This is how I would like to setup my 2D projection matrix. Compared to the 3D version, it would divide both attributes by the input depth. This would simulate having z/w encoded into just the z value.
z = ( f/(f-n) * d + f/(f-n) * -n ) / d;
w = d / d;
I think this turns into something like..
r = f/(f-n); // for less crazy math
z = r + ( r * -n ) / d;
w = 1.0;
However, I can't seem to wrap my math around the values that I would need to plug into my matrix to get this result. It looks like I would need to set my matrix up to perform a division by depth. Is that even possible? Can anyone help me figure out the values I need to plug into my matrix at m[2][2] and m[3][2] (m._33 and m._43) to make something like this happen?
Note my 3D projection matrix uses the following properties to generate the final z value:
m._33 = f / (f-n); // depth scale
m._43 = -(f / (f-n)) * n; // depth offset
Edit: After thinking about this a little more, I realized that the rate of change of the depth buffer is not linear, and I'm pretty sure a matrix can only perform linear change when its input is linear. If that is the case, then what I'm trying to do wouldn't be possible. However, I'm still open to any ideas that are in the same ball park, if anyone has one. I know that I can get what I want by simply doing pos.z /= pos.w; pos.w = 1; in the vertex shader, but I was really hoping to make it all happen in the projection matrix, if possible.
In case anyone is attempting to do this, it cannot be done. Without black magic, there is apparently no way to divide values with a matrix, unless of course the diviser is a constant or etc, where you can swap out a scaler with 1/x. I resorted to performing the operation in the shader in the end.
I wanna to produce a Pie Chart on a Hexagon. There are probably several solutions for this. In the picture are my Hexagon and two Ideas:
My Hexagon (6 vertices, 4 faces)
How it should look at the end (without the gray lines)
Math: Can I get some informations from the object to dynamically calculate new vertices (from the center to each point) to add colored faces?
Clipping: On a sphere a Pie-Chart is easy, maybe I can clip the THREE Object (WITHOUT SVG.js!) so I just see the Hexagon with the clipped Chart?
Well the whole clipping thing in three.js is already solved here : Object Overflow Clipping Three JS, with a fiddle that shows it works and all.
So I'll go for the "vertices" option, or rather, a function that, given a list of values gives back a list of polygons, one for each value, that are portions of the hexagon, such that
they all have the centre point as a vertex
the angle they have at that point is proportional to the value
they form a partition the hexagon
Let us suppose the hexagon is inscribed in a circle of radius R, and defined by the vertices :
{(R sqrt(3)/2, R/2), (0,R), (-R sqrt(3)/2, R/2), (-R sqrt(3)/2, -R/2), (0,-R), (R sqrt(3)/2, -R/2)}
This comes easily from the values cos(Pi/6), sin(Pi/6) and various symmetries.
Getting the angles at the centre for each polygon is pretty simple, since it is the same as for a circle. Now we need to know the position of the points that are on the hexagon.
Note that if you use the symmetries of the coordinate axes, there are only two cases : [0,Pi/6] and [Pi/6,Pi/2], and you then get your result by mirroring. If you use the rotational symmetry by Pi/3, you only have one case : [-Pi/6,Pi/6], and you get the result by rotation.
Using rotational symmetry
Thus for every point, you can consider it's angle to be between [-Pi/6,Pi/6]. Any point on the hexagon in that part has x=R sqrt(3)/2, which simplifies the problem a lot : we only have to find it's y value.
Now we assumed that we know the polar coordinate angle for our point, since it is the same as for a circle. Let us call it beta, and alpha its value in [-Pi/6,Pi/6] (modulo Pi/3). We don't know at what distance d it is from the centre, and thus we have the following system :
Which is trivially solved since cos is never 0 in the range [-Pi/6,Pi/6].
Thus d=R sqrt(3)/( 2 cos(alpha) ), and y=d sin(alpha)
So now we know
the angle from the centre beta
it's distance d from the centre, thanks to rotational symmetry
So our point is (d cos(beta), d sin(beta))
Code
Yeah, I got curious, so I ended up coding it. Sorry if you wanted to play with it yourself. It's working, and pretty ugly in the end (at least with this dataset), see the jsfiddle : http://jsfiddle.net/vb7on8vo/5/
var R = 100;
var hexagon = [{x:R*Math.sqrt(3)/2, y:R/2}, {x:0, y:R}, {x:-R*Math.sqrt(3)/2, y:R/2}, {x:-R*Math.sqrt(3)/2, y:-R/2}, {x:0, y:-R}, {x:R*Math.sqrt(3)/2, y:-R/2}];
var hex_angles = [Math.PI / 6, Math.PI / 2, 5*Math.PI / 6, 7*Math.PI / 6, 3*Math.PI / 2, 11*Math.PI / 6];
function regions(values)
{
var i, total = 0, regions = [];
for(i=0; i<values.length; i++)
total += values[i];
// first (0 rad) and last (2Pi rad) points are always at x=R Math.sqrt(3)/2, y=0
var prev_point = {x:hexagon[0].x, y:0}, last_angle = 0;
for(i=0; i<values.length; i++)
{
var j, theta, p = [{x:0,y:0}, prev_point], beta = last_angle + values[i] * 2 * Math.PI / total;
for( j=0; j<hexagon.length; j++)
{
theta = hex_angles[j];
if( theta <= last_angle )
continue;
else if( theta >= beta )
break;
else
p.push( hexagon[j] );
}
var alpha = beta - (Math.PI * (j % 6) / 3); // segment 6 is segment 0
var d = hexagon[0].x / Math.cos(alpha);
var point = {x:d*Math.cos(beta), y:d*Math.sin(beta)};
p.push( point );
regions.push(p.slice(0));
last_angle = beta;
prev_point = {x:point.x, y:point.y};
}
return regions;
}
I am using a Flot graph and I am setting up various interactive elements.
One of these elements is one in which the user inputs any x value (It really could be either an x or y value depending on the situation, but for simplicity, let's assume it is always an x-axis value) and I need to output the corresponding y coordinate on the line I have graphed. I feel like this should be kind of simple, so I apologize if the answer is an obvious one. Note that the input value is probably not going to be a "point" in the array which flot is using to create the line (although it could).
You could also imagine a vertical line at x = [user input, not necessarily a whole number] intersecting another line series at some point. I would need to find the point of intersection. I tried uploading a photo, but I don't have enough reputation points.
How's your algebra?
There's actually an example of this buried in flot's examples here. If you view the source to that page you'll see this (I've added explanation comments):
// Find the nearest points, x-wise
// loop the series data until you find the point
// immediately after your x value of interest (pos.x in this code)
for (j = 0; j < series.data.length; ++j) {
if (series.data[j][0] > pos.x) {
break;
}
}
// Now Interpolate
// Here's the algebra fun!
var y,
p1 = series.data[j - 1], // point before your x
p2 = series.data[j]; // point after your x
if (p1 == null) {
y = p2[1]; // if no point before just get y-value
} else if (p2 == null) {
y = p1[1]; // if no point after just get y-value
} else {
y = p1[1] + (p2[1] - p1[1]) * (pos.x - p1[0]) / (p2[0] - p1[0]);
// here's the algebra bit, see below
}
In that final else the equation used is this interpolation between two points. Ain't math grand?
I have a circle with a rotation. See images below for example. The circle is divided into segments of varying degrees, for this example I've divided the circle into three equal 120 degree segments.
Given a point of impact (a point on the exterior radius of the circle) I calculate the degree between the center of the circle and the point of impact. I then need to determine which segment was impacted.
My current solution went something like this:
var circleRotation = 270;
var segments = [120, 120, 120];
function segmentAtAngle(angle) {
var sumTo = circleRotation;
for (var i = 0, l = segments.length; l > i; i++) {
if (sumTo <= angle && sumTo + segments[i] >= angle) {
// return the segment
return i;
}
sumTo += segments[i];
}
}
My solution does not work in all cases, given a large offset of say 270 and when requesting the segment at impact degree 45 I currently faultily provide nothing.
Note: Provided angle to segmentAtAngle and circleRotation will also never be negative or above 360. I standardize the degrees by { degrees = degrees % 360; if (degrees < 0) degrees += 360; return degrees; }
What would be the proper way to calculate the hit segment of a circle given an offset rotation?
A simple ad-hoc solution would be duplicating your lists of segments. Then you have the whole range from 0° to 2·360°=720° covered. If angle and circleRotation is between 0° and 360°, as you say they are, then their sum will be between 0° and 720°, and having twice the list of segments will yield a match in all cases. If the resulting index is greater or equal to the length of the original unduplicated list, you can subtract that length to obtain an index from that original list.
First, the conditions of your for loop looks kind of weird. l will always be larger than zero, so the loop will never execute at all. Secondly, you should probably standardize sumTo each time you add to it. Third, you return angle within the loop, which never changes. Do you want to return the index of the impacted segment?
var circleRotation = 270;
var segments = [120, 120, 120];
function standardize(degrees){
degrees = degrees % 360;
if (degrees < 0) degrees += 360;
return degrees;
}
function segmentAtAngle(angle) {
var sumTo = circleRotation;
for (var i = 0; i<segments.length; i++) {
if (sumTo <= angle && sumTo + segments[i] >= angle) {
return i;
}
sumTo = standardize(sumTo + segments[i]);
}
}
The function atan2(DY, DX) will give you the angle from the center to any point. This angle will be in range -pi to +pi. For the sake of the discussion, let us convert this to the -180..+180° range.
Now consider the delimiting angles of your segments, as if obtained by the same function: they will correspond to the ranges [-120..0], [0..120] and [120, -120]. All is fine, except that the third interval straddles the discontinuity, and it should be split into [120..180] and [-180..-120].
In the end, you should consider this list of bounds, with corresponding sectors:
-180 -120 0 120 180
Yellow | Red | Green | Yellow
With N colors, you will need to consider N+1 intervals and compare to N bounds (no need to check against the extreme values, they are implicitly fulfilled). You will do this by linear or dichotomic search (or simple rescaling in case of equidistant bounds).
I'm trying to find a way to calculate the area of a polygon using lat long coordinates in a Flex 3 site. Hong007 on Google Maps for Flash group was cool enough to post the following function:
private function GetPolygonArea (polygon : Polygon):Number
{
var nVer : int = polygon.getOuterVertexCount();
var sz : Number =0;
var s : Number =0;
var x : Number =0;
var y0 : Number =0;
var y1 : Number =0;
var Maplatlng:LatLng;
if (nVer>=3){
for (var i:int=0; i<nVer; i++){
Maplatlng = polygon.getOuterVertex(i);
x = Maplatlng.lng();
if (i>0){
Maplatlng = polygon.getOuterVertex(i-1);
y0 = Maplatlng.lat();
}
else{
Maplatlng = polygon.getOuterVertex(nVer-1);
y0 = Maplatlng.lat();
};
if (i<(nVer-1)){
Maplatlng = polygon.getOuterVertex(i+1);
y1 = Maplatlng.lat();
}
else{
Maplatlng = polygon.getOuterVertex(0);
y1 = Maplatlng.lat();
};
s = x * (y0-y1);
sz+=s;
};
//경위도시 1도의 m값을 곱한다(대략 면적 환산)
Maplatlng = polygon.getOuterVertex(0);
var Maplatlng1:LatLng = new
com.google.maps.LatLng(Maplatlng.lat()+1, Maplatlng.lng()+1);
var TempDISTANCE:Number =
Maplatlng.distanceFrom(Maplatlng1) / Math.sqrt(2);
return Math.abs((sz/2.0) * Math.pow(TempDISTANCE, 2));
};
return 0.0;
}
I was also playing around with the area calculator at http://www.freemaptools.com/area-calculator.htm .
These functions produce slightly different results. I'm trying to figure out which one is more accurate. It seems that hong007's function produces results that are on average slightly larger than freemaptools' function. However, I don't know which one is more accurate. Any advice?
I added some string to this algorithm.
For google maps experimentally I found this numbers:
are = area-(area*0.2187);
and it works for me for maximum (scale = 5 meters) and minimum (500 km) zoom levels.
The method implemented here is pretty quick and dirty. It makes a couple of assumptions that can lead to incorrect results.
The first thing to know is that Lat/Long space is non-uniformly scaled with respect to measured distance on the ground. This means that a vector of length one meter has a different length in lat/long space depending on if the vector is pointing roughly east-west or north-south. Also, the magnitude of the difference between how the lat/long axes map to ground units changes depending on where you are on the globe (it's much more different at the poles than at the equator.)
The algorithm above does a very quick and dirty workaround for this, which is to generate a scale value based on the distance calculated for the hypotenuse of a unit right triangle. This tries to basically average the scales of the two lat/long axes for that point on the globe.
There are a couple of problems with this. If the polygon is very large (multiple geocells), then this average scale value will be off because it's only calculated for the local geocell around the 0 vertex. Secondly, the average scale approximation is really very coarse and will break down significantly if you have polygons that vary greatly in one dimension but not the other (a long skinny polygon oriented along one of the axes). This is because the scale will be computed as an average of the scale of the two axes but one of the axes should have very little influence because of the distribution of the vertices.
I didn't look at the other area calculator but I would guess if you're seeing discrepancies that this code is the less accurate version.