I'm currently going through this tutorial on rendering shapes WebGL (specifically a sphere in this case) and I understand the math behind the generation of each point on the sphere. In the tutorial though, the author defines one method to find all of the vertices and another to generate all of the squares that will comprise the sphere.
A couple of things are unclear from what is done in the tutorial. First, how exactly are the vertices generated by the parametric equation being connected to the squares (triangle strips) being generated? I've made a bare bones program in plain javascript and HTML5 before doing the same thing just using the vertices generated so I'm not seeing how and why they have to be used in conjunction with the triangle strips. The other point of confusion is specifically regarding the function that generates the squares:
var indexData = [];
for (var latNumber = 0; latNumber < latitudeBands; latNumber++) {
for (var longNumber = 0; longNumber < longitudeBands; longNumber++) {
var first = (latNumber * (longitudeBands + 1)) + longNumber;
var second = first + longitudeBands + 1;
indexData.push(first);
indexData.push(second);
indexData.push(first + 1);
indexData.push(second);
indexData.push(second + 1);
indexData.push(first + 1);
}
}
To generate the first point of each square (point on the top left corner) the following is done: var first = (latNumber * (longitudeBands + 1)) + longNumber;
I'm not sure why the number of the lattitude line needs to be multiplied by the total number of longitude lines (plus 1 to fully wrap around) at each step.
The code for both functions is toward the bottom of the tutorial. A general explanation of the use of triangle strips in a case like this could also be helpful, thanks.
how exactly are the vertices generated by the parametric equation being connected to the squares (triangle strips) being generated?
A: Vertices are basically points. So its basically generating points using math. Quote from tutorial :
"for a sphere of radius r, with m latitude bands and n longitude bands, we can generate values for x, y, and z by taking a range of values for θ by splitting the range 0 to π up into m parts, and taking a range of values for φ by splitting the range 0 to 2π into n parts, and then just calculating:
x = r sinθ cosφ
y = r cosθ
z = r sinθ sinφ"
how and why they have to be used in conjunction with the triangle strips
A: they are not triangle STRIPS as in the primitive type gl.TRIANGLE_STRIP, but merely regular triangles defined with 3 points.
regarding the function that generates the squares
A: They are not generation squares per se, but using the points generated from the parametric equation to create triangles for the GPU to render. The code you shown in the OP basically divides a square into 2 triangles.
Related
I originally wanted to use four points (as a bezier Curve is defined with 4 points), but that forces me to brute force the position, so I tried a different approach i now need help with:
I have a start point P0, an end point P1 and slopes m0 and m1 which are supposed to give me the start/end slope to calculate a Bezier Curve inbetween them.
The Curve is supposed to be in the form of a function (3rd degree), since I need to get the height y of a given point x.
Using the HTML5Canvas i can draw a bezier curve no problem and using this function
that allows me to calculate any given point given a percentage of the way i can get the center point of the curve. But I don't need it depending on t but rather the y depending on x, so not halfway of the curve but halfway of the x distance between P0 and P1.
Image to visualize:
Left is what i can calculate, right is what i need.
I've been trying to calculate the cubic function given the two points P0, P1 as well as the slopes m0, m1, which results into four equations which i can't seem to be able to solve with only variable inputs. I've also tried to use the above function to calculate the t using the x value (which is known), but no dice there either.
I need to avoid using approximations or costly loops for these calculations as they are performed many times a second for many objects, thus this answer is not feasible for me.
Any help is appreciated.
I've encountered the same problem in a project I'm working on. I don't know of a formula to get the y coordinate from the x, and I suspect you'll have trouble with that route because a bezier curve can have up to 3 points that all have the same x value.
I would recommend using the library BezierEasing, which was designed for this use case and uses various performance enhancing techniques to make lookups as fast as possible: https://github.com/gre/bezier-easing
To solve this problem, you need to rewrite Bezier equation in power polynomial form
X(t) = t^3 * (P3.X-3*P2.X+3*P1.X-P0.X) +
t^2 * (3*P0.X + 6*P1.X+3*P2.X) +
t * (3*P1.X - 3P2.X) +
P0.X
if X(t) = P0.X*(1-ratio) + P3.X*ratio
then
let d = ratio * (P0.X - P3.X)
and solve cubic equation for unknown t
a*t^3 + b*t^2 + c*t + d = 0
JS code here
Then apply calculated t parameter (there might be upto three solutions) to Y-component and get point coordinates. Note that formulas are close (no loops) and should work fast enough
Thank you to everyone that answered before, those are generally great solutions.
In my case I can be 100% sure that I can convert the curve into a cubic function, which serves as the approximation of the bezier curve using the result of this calculation.
Since i have control over my points in my case, I can force the P0 to be on x=0, which simplifies the linear system calculations and thus allows me to calculate the cubic function much easier like this:
let startPoint: Utils.Vector2 = new Utils.Vector2(0, 100);
let endPoint: Utils.Vector2 = new Utils.Vector2(100, 100);
let a: number, b: number, c: number, d: number;
function calculateFunction() {
let m0: number = 0;
let m1: number = 0;
a = (-endPoint.x * (m0 + m1) - 2 * startPoint.y + 2 * endPoint.y) / -Math.pow(endPoint.x, 3);
b = (m1 - m0 - 3 * a * Math.pow(endPoint.x, 2)) / (2 * endPoint.x);
c = m0;
d = startPoint.y;
}
I'm trying to generate random, convex, non-self-intersecting polygons - quadrilaterals specifically.
I've gotten as far as:
function randquad(){
var bodyDef = new b2BodyDef,
fixDef = new b2FixtureDef,
x=0, y=0;
fixDef.density = Math.random();
fixDef.friction = Math.random();
fixDef.restitution = Math.random()*0.3;
bodyDef.type = b2Body.b2_dynamicBody;
fixDef.shape = new b2PolygonShape;
fixDef.shape.SetAsArray([
new b2Vec2(x, y),
new b2Vec2(x+=Math.random()*2, y+=Math.random()*2*(Math.random()<0.5?1:-1)),
new b2Vec2(x+=Math.random()*2*(Math.random()<0.5?1:-1), y+=Math.random()*2),
new b2Vec2(x-=Math.random()*2, y+=Math.random()*2*(Math.random()<0.5?1:-1))
]);
bodyDef.position.x = Math.random() * 10;
bodyDef.position.y = 0;
world.CreateBody(bodyDef).CreateFixture(fixDef);
}
I'm working with Box2D web; however, this question is not limited to it, or javascript - its a broader one that is more mathematical.
I want to get 4 points that, in a sequence, make the shape I'm looking for.
With the code I've written I often get the desired shape; however, I do still get self-intersecting shapes. At the moment I'm not too fussed about the shapes being convex.
The reason I need such shapes is because it seems they're the only ones box2d web likes.
Any 4 points can make a non-self-intersecting quadrilateral right?
Should I generate 4 initially, and sort them into a sequence that works?
Whats the best/most efficient manner in which to approach this?
-- Original Question Title: Random Quadrilateral Generation
You could start from the fact that if you take 3 random points, they will always form a convex, non-intersecting triangle (provided the points are all different). Then you can just pop out one of the edges by adding a fourth point a random distance along the last edge, and pushing it away from the center of the triangle by a random amount.
Start off by generating a random triangle by generating 3 random points: A, B, C
Choose a random number between 0 and 1, and use it to calculate a point D somewhere along the line between C and A
Calculate the centroid of the triangle by averaging A, B, C
Calculate another random number between 1 and n (say 2)
Calculate the vector from the centroid to D (subtract the centroid from D), and multiply it by this second random number, then add back to the centroid. This will produce your fourth point.
I'm not sure if you have any aesthetic requirements (e.g. no excessively pointy shapes) but you could play around with the random number generation a bit to get the results you like. For example in the second step you could choose a random number between 0.2 and 0.8, for example, so the points aren't too close together. You can also change the value of n in the fourth step.
Another issue is winding. With this method, half the quads will have a clockwise winding and have will be counter-clockwise. If you need a particular winding you can do a winding test (sign of dot product of BC with normal of AB), and if it's not the way you want then just reflect all the points on one axis.
If you take your points from the perimeter of a circle all the vertices will be convex.
Knowing this we can program a loop to branch out to a non-existant circle and generate corners that'll always produce a convex shape.
angles=[];
for (var i = 4; i--;) angles.push(Math.random()*2*Math.PI);
Now the angles need to be sorted so when the shape is created, clockwise, they're chosen in order of angle size.
This will stop points crossing / self intersection of the shape.
angles.sort()
Now you can create the shape using the triganometric functions cos and sin, these will convert the angle to actual co-ordinates.
polygon([
vector(Math.cos(angles[0]), Math.sin(angles[0])),
vector(Math.cos(angles[1]), Math.sin(angles[1])),
vector(Math.cos(angles[2]), Math.sin(angles[2])),
vector(Math.cos(angles[3]), Math.sin(angles[3]))
]);`
I'm trying to plot this type of "binary matrix" graphic:
Disregard the two colors from the sample image; I want to either color a dot blue for, let's say, "complete" values or leave it uncolored/gray for "incomplete" values as a way to track daily task completion for a certain amount of dots/days. The dots represent a day where a task was completed or not completed. Showing the full amount of dots/days gives perspective on % of completion as days go by.
I would like to use a combination of HTML/Javascript and PHP + MySQL. But the hardest part for me is figuring out a good algorithm to render this visualization. Thanks for your help.
Just treat each dot like it's a pixel. Also, imagine that the image has been rotated 90° CCW. Then, you simply draw a square that takes up less room that is allocated to it - this way, you get the separating lines.
Here'e a quick something to have a play with.
A few notes:
0) I just halved your image dimensions
1) 4 pixels and 5 pixels were chosen arbitrarily
2) I didn't bother with setting the colour of the dot - you can
easily do this.
3) I've simply treated the drawing area like a normal top-bottom
bitmap, while your image seems to show that all of the Y values will
be used before the next X value is needed. (This is like a 90° CCW
rotation).
4) I'm addressing the pixels with an X and a Y - perhaps you'd be
more interested in addressing them with a single number? If so, you
could easily write a function that would map two coords to a single
number - the pixels index, if you like.
I.e if an image is 100 x 100, there are 10,000 pixels. You could address them by specifying a number from 0 - 9,999
E.g
function 10k_to_100x100(index)
{
var x = index % 100;
var y = (index / 100).toFixed(0);
plotPixelDot(x, y);
}
X is simply the remainder when dividing by the width
Y is the whole number answer when dividing by the width
Here's a snippet you can try right here on the page:
function byId(id){return document.getElementById(id);}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded()
{
var x, y;
for (y=0; y<20; y++)
{
for (x=0; x<100; x++)
{
drawDot(x, y, 'output');
}
}
}
function drawDot(xPos, yPos, canvasId)
{
var actualX=xPos*5, actualY=yPos*5;
var ctx = byId(canvasId).getContext('2d');
ctx.fillRect(actualX, actualY, 4, 4);
}
<canvas width=558 height=122 id='output'></canvas>
Background
I have a pet project that I love to overthink from time to time. The project has to do with an RC aircraft control input device. People familiar with that hobby are probably also familiar with what is known as "stick expo", which is a common feature of RC transmitters where the control sticks are either more or less sensitive near the neutral center position and become less or more sensitive as the stick moves closer to its minimum or maximum values.
I've read some papers that I don't fully understand. I clearly don't have the math background to solve this, so I'm hoping that perhaps one of you might.
Problem
I have decided to approximate the curve by taking a pre-determined number of samples and use linear interpolation to determine output values for any input values between the sample points. I'm trying to find a way to determine the most optimal set of sample points.
If you look at this example of a typical growth curve for this application, you will notice that some sections are more linear (straighter), and some are less linear (more curvy).
These samples are equally distant from each other, but they don't have to be. It would be smart to increase the sample density where there is more change and thereby increasing the resolution in the curvy segments by borrowing redundant points from the straight segments.
Is it possible to quantify the degree of error? If it is, then is it also possible to determine the optimal set of samples for a given function and a pre-determined number of samples?
Reference Code
Snippet from the class that uses a pre-calculated set of points to approximate an output value.
/* This makes the following assumptions:
* 1. The _points[] data member contians at least 2 defined Points.
* 2. All defined Points have x and y values between MIN_VALUE and MAX_VALUE.
* 3. The Points in the array are ordered by ascending values of x.
*/
int InterpolatedCurve::value( int x ) {
if( _points[0].x >= x ) { return _points[0].y; }
for( unsigned int i = 1; i < _point_count; i++ ) {
if( _points[i].x >= x ) {
return map(x, _points[i-1].x, _points[i].x,
_points[i-1].y, _points[i].y);
}
}
// This is an error condition that is not otherwise reported.
// It won't happen as long as the points are set up correctly.
return x;
}
// Example map function (borrowed from Arduino site)
long map( long x, long x1, long x2, long y1, long y2 ) {
return (x - x1) * (y2 - y1) / (x2 - x1) + y1;
}
Although my project is actually in C++, I'm using a Google spreadsheet to produce some numbers while I ponder this problem.
// x: Input value between -1 and 1
// s: Scaling factor for curve between 0 (linear) and 1 (maximum curve)
// c: Tunable constant
function expo_fn( x, s, c ) {
s = typeof s !== 'undefined' ? s : 1.0;
c = typeof c !== 'undefined' ? c : 4.0;
var k = c * ((c - 1.0) * s*s*s + s)/c + 1.0;
return ((k - 1.0) * x*x*x*x*x + x)/k;
};
The following creates a set of isometrically distributed (non-optimal) points between input values -1 and 1. These output values were expanded to integers between -16383 and 16383 for the above example spreadsheet. Factor is a value between 0 and 1 that determines the "curviness"--zero being a flat, linear curve and 1 being the least-linear curve I care to generate.
function Point( x, y ) {
this.x = x;
this.y = y;
};
function compute_points_iso( count, factor ) {
var points = [];
for( var i = 0; i < count; ++i ) {
var x = 2.0/(count - 1.0) * i - 1.0;
var y = expo_fn(x, factor);
points.push(new Point(x,y));
}
return points;
};
Relevant Academic Work
I have been studying this paper describing an algorithm for selecting significant data points, but my program doesn't quite work right yet. I will report back if I ever get this thing working.
The key here is to realize that you can bound the error on your linear interpolation in terms of the second derivative of the function. I.e. if we approximate f(x) \approx f(x_0) + f'(x_0)*(x-x_0), then the error in this approximation is less than abs[ 0.5*f''(x_0)(x-x_0)^2 ].
The outline of an iterative approach could look like this:
Construct an initial, e.g. uniformly spaced, grid
Compute the second derivative of the function on this grid.
Compute the bound on the error using the second-derivative and the inter-sample spacing
Move the samples closer together where the error is large; move them further apart where the error is small.
I'd expect this to be an iterative solution that loops on steps 2,3,4.
Most of the details are in step 4.
For a fixed number of sample points one could use the median of the error bounds to select
where finer/coarser sampling is required (i.e. those locations where the error is larger than the median error will have the sample points pulled closer together).
Let E_0 be this median of the error bounds; then we can, for each sample in the point, compute a new desired sample spacing (dx')^2=2*E_0/f''(x); then you'd need some logic to go through and change the grid spacing so that it is closer to these ideal spacings.
My answer is influenced by having used the "Self-Organizing Map" algorithm on data; this or related algorithms may be relevant for your problem. However, I can't recall ever
seeing a problem like yours where the goal is to make your estimates of the error uniform across the grid.
I've been working in JavaScript to code a line drawing system. I'd like the lines drawn to be selectable, so I've been attempting to implement line-highlighting. As you can see in the image below, I have a line (in black) with known coordinates and an equation in slope-intercept (y=mx+b). How can I calculate the corners' (circled in green) coordinates, knowing the box's radius?
This is easiest to think of in terms of vectors.
Start off by defining the point at the end of the line as A, and the other end as B
var A = new Vector(1, 1)
var B = new Vector(5, 3)
Now find the unit direction vector of the line (a vector of length 1 pointing from A to B), and its perpendicular:
var dir = B.minus(A).normalize();
var dir_perp = new Vector(dir.y, -dir.x)
And extend them to be of length thickness:
dir = dir.times(thickness);
dir_perp = dir_perp.times(thickness)
The four corners are then:
[
A.minus(dir).plus(dir_perp),
A.minus(dir).minus(dir_perp),
B.plus(dir).minus(dir_perp),
B.plus(dir).plus(dir_perp)
]
This obviously assumes you have some sort of vector math library. Here's one I made earlier