Best path between two curves - javascript

My aim is to find a smooth best fit line between this two circuit curvy shapes.
Is there any algorithm betten than mine that can find a set of points (or a curve) between two lines like this example?
The algorithm I have so far takes the inner part and for each point finds the closest, however this doesnt work because (look at the first corner).
(Red is the inner part, green is the outer part, blue is the optimised dots I have found)
Here is my jsfiddle:
http://jsfiddle.net/STLuG/
This is the algorithm:
for (i = 0; i < coords[0].length; i++) {
var currentI = coords[0][i];
j = 0;
var currentJ = coords[0][j];
currentDist = dist(currentI,currentJ);
for (j=1; j < coords[1].length; j++) {
possibleJ = coords[1][j];
possibleDist = dist(currentI, possibleJ);
if (possibleDist < currentDist) {
currentJ = possibleJ;
currentDist = possibleDist;
} else {
}
}
b_context.fillRect(
(currentI.x+currentJ.x)/2+maxX,
(currentI.y+currentJ.y)/2+maxY,
1, 1);
}
Thanks

I would try least-squares-algorithm.
You have a number of points: y0 and x0 and y1 and x1 for the first and the second curve respectively.
You want to find a curve y(t) and x(t) which is smooth and in-between the two given curves.
So there is the distance between the first curve(x0(t), y0(t)) to your to be calculated curve(x(t), y(t)):
S0=SumOverAllT(x0(t)-x(t))^2 + (y0(t) - y(t))^2
The same for the second curve:
S1=SumOverAllT(x1(t)-x(t))^2 + (y1(t) - y(t))^2
The sum of both sums:
S=S0+S1
You will have a set of parameters which you want to determine.
E.g. if you use polynomials:
x(t)=ax+bx*t+cx*t^2+dx*t^3....
y(t)=ay+by*t+cy*t^2+dy*t^3....
You will then calculate
dS/dax, dS/dbx, dS/dcx, ....
for all parameters to be calculated
and set these derivatives to zero:
dS/dax==0
dS/dbx==0
....
This will give you a set of linear equations which can be attacked by the gauss algorithm or whatever method to solve a system of linear equations.
If you're using polynomials it might happen that the curve calculated oscillates strongly.
In this case I would suggest to try to minimize the integral of the square of the second derivative:
I=integral((d^2x/dt^2)^2 + (d^2y/dt^2)^2, dt)
you would calculate the differential of I vs. some additional parameters which you did not use for above system of equation -- adding a parameter rx and calculating dI/drx==0 -- thus you have one more parameter and one more equation.
Anybody with a PHD in mathematics please advice me on any stupidity I mentioned above.
Also search the internet for:
Curve fitting
Spline approximation
A better approach would be to use splines -- piecewise continuous polynomials, so that
the 0 derivative
the first derivative
the second derivative
is continuous.
Look up or buy Numerical recipes to find code which does exactly this.
For spline approximation you would have a set of polynomials:
x0(t)=a0x + b0x*(t - t0) + c0x*(t-t0)^2 + d0x*(t - t0)^3....
x1(t)=a1x + b1x*(t - t0) + c1x*(t-t0)^2 + d1x*(t - t0)^3....
Every polynomial would only be used to cover the matching t=t0..t1 between two given points.
You would then add equations to make certain that the value, first and second derivatives are identical for two neighboring polynomials.
And set the 2 derivative for the first and last polynomial to zero.
Potentially you could calculate two splines -- one for every of the two input curves you have:
x0(t)
y0(t)
x1(t)
y1(t)
And then you could derive the middle of the two splines:
x(t)=(x0(t) + (x1(t)-x0(t))/2
y(t)=(y0(t) + (y1(t)-y0(t))/2
make certain that the distance between any of the given curves and you resulting curve is never zero so that they don't cross each other
To make certain, that your calculated line does not cross one of the given lines, you could minimize (sum(sum(1/(x0-x)^2)) + sum(sum(1/(x1-x)^2)))

Related

I need to make my function return a more organic collection of results

Whatever it is I'm doing, I don't know what it's called, but I need help because I know it can be done with math. This is for a simulation I'm building, and the role it plays is very difficult to explain, but it has something to do with defining the properties of an object.
Here is my JavaScript: https://jsfiddle.net/vdocnmzu/
DM.prototype.get = function(coords){
var dist;
val = 0;
for(var j,i = 0; i < this.distortions.length; i += 1){
dist = 0;
for(j = 0; j < coords.length; j += 1){
dist += Math.pow( coords[j] - this.distortions[i].coords[j], 2);
}
dist = Math.pow(dist,.5);
if( dist <= this.distortions[i].range){
val += Math.cos( (dist/this.distortions[i].range) * Math.PI/2 ) * this.distortions[i].amp;//;
}
}
return val;
}
What's happening is this: I have this 3D cube, where I can pick x & y, and get Z(the grayscale pixel color). In this sample code, I'm picking a grid of points across the entire x,y plane of the cube. The "bubbles" you see (you may need to refresh a few times) are multiple points being picked and creating that image.
What I'm trying to do is not have bubbles, but rather, organic flows between bubbles.
Right now, the z value comes from these "distortion points" that each of these 3DCubes have. It can have any amount of these points.
These "distortion points" don't have to be points. They can be sets of points, or lines, or any type of base geometry to define the skeleton of some type of distance function.
I think that distance function is what I'm struggling with, because I only know how to do it with points. I feel like lines would still be too rigid. What's the math associated with doing this with curves? Distance to a curve? Are there more approaches to this? If there's not a good single 1 to pick, it's okay to have a collection as well.
Your question is very complicated to understand. The overall feeling is that your expectations are too high. Some advanced math 101 might help (feel free to google buzzwords):
Defining a curve is an very hard problem that challenged the brightest mathematicians of the history. From the naive approach of the greeks, through the calculus of Newton and Leibniz, passing by Euler and Gauss, to the mathematical analysis of Weisstreiss, the word curve changed meaning several times. The accepted definition nowadays says that curves are continous functions in two variables, where continous is a very special word that has an exact meaning coined in the 19th century (naively is a function without jumps from one value to another). Togheter with the notion of continuity, came the notions of connected, compact, differentiable (and so on) curves, which defined new conditions for special curves. The subject developed to what is now known as topology and mathematical analysis.
Mathematicians usually uses definitions to reproduce a class of ideas that can be brought and thought togheter. To their surprise, the definition of continuity did include really weird functions to be curves: space-filling-curves, fractals!!! They called them monsters at the time.
After this introduction, lets go back to your question. You need a geometrical object to calculate distances from a point. Lets avoid weird curves and go from continous to differentiable. Now it's better. A (conected compact) differentiable function can be expanded in Taylor series, for example, which means that all functions of this class can be written as an infinite sum of polynomial functions. In two dimensions, you need to calculate matrices involved in this expansion (Calculus in many variables is a pre-requisite). Another step further is truncating this expansion in some degree, lets say 3. Then the general curve in this case is: ax + by + cx^2 + dy^2 + ex^3 + fy^3 + gx^2y + hxy^2 + ixy + j = 0 (ab...j are free parameters). Oh! This is reasonable, you might think. Well, actually there is a name for this kind of curve: algebraic curve of deggre 3. This is an active research theme of algebraic geometry, which is a very hard field even among mathematicians. Generally speaking, there are milestone theorems about the general behavior of those curves, which involves singularities and intersection points that are allowed in the general case.
In essence, what you are looking for does not exist, and is a very hard subject. Your algorithm works with points (really cool pictures by the way) and you should baby step it into a straight line. This step already requires you to think about how to calculate distance between a point and a straight line. This is another subject that was developed in general in the 19th century, togheter with mathematical analysis: metric spaces. The straightfoward answer to this question is defining the distance between a point and a line to be the smallest distance from the point to all line points. In this case, it can be shown that the distance is the modulus of the vector that connects the point to the line in a 90 degrees angle. But this is just one definition among infinte possible ones. To be considered a distance (like the one I just described and the euclidean distance) there is a set of axioms that needs to be verified. You can have hyperbolic metrics, discrete metrics, metrics that count words, letters, LotsOfFamousPeople metric spaces... the possibilities are infinite.
So, baby steps. Do it with straight lines and euclidean minimum distance metric. Play around with other metrics you find on google. Understand the axioms and make your own!!! Going to second degree polynomials is already a big challenge, as you have to understand everything that those curves can make (they can really do weird unexpect stuff) and define a distance to it (metric space).
Well thats it! Good luck with your project. Looks really cool!

Dealing with the inverted Y axis while graphing in Javascript?

I am using Javascripts built in canvas feature to draw a graph showing home loan payments, loan balance, and equity based on user input. I am not able to use any other form of graphing package, as the code is part of an assessment.
My graph is drawn by converting data to X and Y coordinates. When a loan price is input, some home loan payment equations calculate the total amount payed, which is divided by the canvas width to get a spacing variable. This spacing variable is used to convert dollar amounts into pixels on the canvas. A similar setup is used to get the years and months spacing pixels.
The problem I am having is that the Y axis on Javascript's canvas is inverted, with 0 being the top of the canvas and 280, my canvas height, being at the bottom. So far, I have been able to work around this, simply by swapping "+" and "-" operators, however, I am currently creating the code that draws the Loan Balance line on the graph, and the inversion is causing issues that I can't seem to solve. It may be something simple that I'm just not seeing, or it may be a more complex problem that needs to be solved, but either way, I can't figure it out.
X = 0; // same as before, iterators both set back to 0 for the new line.
iterator = 0;
c.beginPath // this next line is for loan balance, it starts at 300000 and goes down with each payment made, then back up with each bit of interest accrued.
// due to the fact that the y axis begins at the top, this means that the pixels for payments is added to the pixel count, and the interest accrued is taken away.
c.moveTo(0, loanLocation) // set starting point to x=0 y= loanLocation
while (X <= 510)// loan balance loop
{
X = X + 0.001; // iterates X by .001 each time, allowing an accurate subpixel resolution loop, see above for why this is needed.
iterator = iterator + 0.001;
if (iterator >= monthSpacing)
{
loanBalance = loanBalance - monthlyPayment + (monthlyInterest * loanBalance);
//alert(loanBalance);
//interestY =
//alert(interestY);
//alert(X + " " + monthSpacing);
loanY = loanY + paymentY - (loanY * monthlyInterest);
//alert(loanY);
//loanY = loanBalance * paySpacing;
c.lineTo(X, loanY);
iterator = 0;
}
}
c.strokeStyle = "black"
c.stroke(); // there is no fill for this line, so it is just left as a stroke.
This is the set of code which draws the line, above it are a few variables which are being used here:
var X = 0;
var iterator = 0;
var monthSpacing = yearSpacing / 12;
//alert(yearSpacing);
//alert(monthSpacing);
var monthlyInterest = interest/1200; // this gives the montly interest rate, the monthly interest pixel amount is below
//alert(monthlyInterest);//debugging, comment out.
var paymentY = monthlyPayment * paySpacing;
var interestY = monthlyInterest * paySpacing; // this is inaccurate, the interestY needs to be gotten by multiplying the remaining loan balance by the
//monthly interest each month.
//var interestY; // will be used further down, must be calculated monthly so cannot be set outside of the line drawing loops.
var totalY = 280;
var equityY = 280;
var loanBalance = loan;
var loanY = loanLocation;
When run I get a strange inversion of the desired outcome, I want the loan balance line to curve down towards zero, but instead, the curve is happening in the opposite direction, I have tried two different ways to get the coordinates, the loanBalance way, which involved working with dollar values and converting that to pixels, and the loanY way, which involved working with pixel values directly.
loanBalance provided a line which was the exact inverse of the desired line, it began at the loan value, and curved upwards in the exact opposite direction to what I want, I am confident that the math I'm using for the loanBalance method is accurate, I simply cannot think of a way to convert that dollar value into pixels due to the inverted nature of the Y axis.
loanY provides a line which is headed "down", but is curving downwards at an increasingly shortened rate, this leads me to believe that while the subtraction (addition due to the inversion) of monthly repayments is accurately being calculated, the addition (subtraction) of monthly interest is being calculated incorrectly. Multiplication cannot be simply replaced with division like addition and subtraction can, so converting this value to pixels is proving difficult. The line drawn by the loanY way is definitely being affected by the inversion, but is not a perfect inverse of the desired line, the math being used for that way is clearly very wrong.
Ideally, I'd like to find a way to use the loanY way, it is consistent with the rest of the program, and can be used when not working with such obvious values as dollars. If I have to though, I will use the loanBalance way.
If you aren't entirely certain what I'm asking, or what the code being used is, I can post the program in it's entirety if that would help. I've not done that yet as I don't want to clutter the question more than I already have.
You can change to a Cartesian coordinate system like this:
// get a reference to your canvas element (eg it might have id='myCanvas')
var canvas=document.getElementById('myCanvas');
// get the context for the canvas
var context=canvas.getContext('2d');
// vertically flip the canvas so its Y origin is at the bottom
context.setTransform(1,0,0,-1,0,canvas.height);
This makes y==0 at the bottom of the canvas and increases upward.
If you're using other transformations, then put this transformation before the others.

How to calculate bezier curve control points that avoid objects?

Specifically, I'm working in canvas with javascript.
Basically, I have objects which have boundaries that I want to avoid, but still surround with a bezier curve. However, I'm not even sure where to begin to write an algorithm that would move control points to avoid colliding.
The problem is in the image below, even if you're not familiar with music notation, the problem should still be fairly clear. The points of the curve are the red dots
Also, I have access to the bounding boxes of each note, which includes the stem.
So naturally, collisions must be detected between the bounding boxes and the curves (some direction here would be good, but I've been browsing and see that there's a decent amount of info on this). But what happens after collisions have been detected? What would have to happen to calculate control points locations to make something that looked more like:
Bezier approach
Initially the question is a broad one - perhaps even to broad for SO as there are many different scenarios that needs to be taken into consideration to make a "one solution that fits them all". It's a whole project in its self. Therefor I will present a basis for a solution which you can build upon - it's not a complete solution (but close to one..). I added some suggestions for additions at the end.
The basic steps for this solutions are:
Group the notes into two groups, a left and a right part.
The control points are then based on the largest angle from the first (end) point and distance to any of the other notes in that group, and the last end point to any point in the second group.
The resulting angles from the two groups are then doubled (max 90°) and used as basis to calculate the control points (basically a point rotation). The distance can be further trimmed using a tension value.
The angle, doubling, distance, tension and padding offset will allow for fine-tuning to get the best over-all result. There might be special cases which need additional conditional checks but that is out of scope here to cover (it won't be a full key-ready solution but provide a good basis to work further upon).
A couple of snapshots from the process:
The main code in the example is split into two section, two loops that parses each half to find the maximum angle as well as the distance. This could be combined into a single loop and have a second iterator to go from right to middle in addition to the one going from left to middle, but for simplicity and better understand what goes on I split them into two loops (and introduced a bug in the second half - just be aware. I'll leave it as an exercise):
var dist1 = 0, // final distance and angles for the control points
dist2 = 0,
a1 = 0,
a2 = 0;
// get min angle from the half first points
for(i = 2; i < len * 0.5 - 2; i += 2) {
var dx = notes[i ] - notes[0], // diff between end point and
dy = notes[i+1] - notes[1], // current point.
dist = Math.sqrt(dx*dx + dy*dy), // get distance
a = Math.atan2(dy, dx); // get angle
if (a < a1) { // if less (neg) then update finals
a1 = a;
dist1 = dist;
}
}
if (a1 < -0.5 * Math.PI) a1 = -0.5 * Math.PI; // limit to 90 deg.
And the same with the second half but here we flip around the angles so they are easier to handle by comparing current point with end point instead of end point compared with current point. After the loop is done we flip it 180°:
// get min angle from the half last points
for(i = len * 0.5; i < len - 2; i += 2) {
var dx = notes[len-2] - notes[i],
dy = notes[len-1] - notes[i+1],
dist = Math.sqrt(dx*dx + dy*dy),
a = Math.atan2(dy, dx);
if (a > a2) {
a2 = a;
if (dist2 < dist) dist2 = dist; //bug here*
}
}
a2 -= Math.PI; // flip 180 deg.
if (a2 > -0.5 * Math.PI) a2 = -0.5 * Math.PI; // limit to 90 deg.
(the bug is that longest distance is used even if a shorter distance point has greater angle - I'll let it be for now as this is meant as an example. It can be fixed by reversing the iteration.).
The relationship I found works good is the angle difference between the floor and the point times two:
var da1 = Math.abs(a1); // get angle diff
var da2 = a2 < 0 ? Math.PI + a2 : Math.abs(a2);
a1 -= da1*2; // double the diff
a2 += da2*2;
Now we can simply calculate the control points and use a tension value to fine tune the result:
var t = 0.8, // tension
cp1x = notes[0] + dist1 * t * Math.cos(a1),
cp1y = notes[1] + dist1 * t * Math.sin(a1),
cp2x = notes[len-2] + dist2 * t * Math.cos(a2),
cp2y = notes[len-1] + dist2 * t * Math.sin(a2);
And voila:
ctx.moveTo(notes[0], notes[1]);
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, notes[len-2], notes[len-1]);
ctx.stroke();
Adding tapering effect
To create the curve more visually pleasing a tapering can be added simply by doing the following instead:
Instead of stroking the path after the first Bezier curve has been added adjust the control points with a slight angle offset. Then continue the path by adding another Bezier curve going from right to left, and finally fill it (fill() will close the path implicit):
// first path from left to right
ctx.beginPath();
ctx.moveTo(notes[0], notes[1]); // start point
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, notes[len-2], notes[len-1]);
// taper going from right to left
var taper = 0.15; // angle offset
cp1x = notes[0] + dist1*t*Math.cos(a1-taper);
cp1y = notes[1] + dist1*t*Math.sin(a1-taper);
cp2x = notes[len-2] + dist2*t*Math.cos(a2+taper);
cp2y = notes[len-1] + dist2*t*Math.sin(a2+taper);
// note the order of the control points
ctx.bezierCurveTo(cp2x, cp2y, cp1x, cp1y, notes[0], notes[1]);
ctx.fill(); // close and fill
Final result (with pseudo notes - tension = 0.7, padding = 10)
FIDDLE
Suggested improvements:
If both groups' distances are large, or angles are steep, they could probably be used as a sum to reduce tension (distance) or increase it (angle).
A dominance/area factor could affect the distances. Dominance indicating where the most tallest parts are shifted at (does it lay more in the left or right side, and affects tension for each side accordingly). This could possibly/potentially be enough on its own but needs to be tested.
Taper angle offset should also have a relationship with the sum of distance. In some cases the lines crosses and does not look so good. Tapering could be replaced with a manual approach parsing Bezier points (manual implementation) and add a distance between the original points and the points for the returning path depending on array position.
Hope this helps!
Cardinal spline and filtering approach
If you're open to use a non-Bezier approach then the following can give an approximate curve above the note stems.
This solutions consists of 4 steps:
Collect top of notes/stems
Filter away "dips" in the path
Filter away points on same slope
Generate a cardinal spline curve
This is a prototype solution so I have not tested it against every possible combination there is. But it should give you a good starting point and basis to continue on.
The first step is easy, collect points representing the top of the note stem - for the demo I use the following point collection which slightly represents the image you have in the post. They are arranged in x, y order:
var notes = [60,40, 100,35, 140,30, 180,25, 220,45, 260,25, 300,25, 340,45];
which would be represented like this:
Then I created a simple multi-pass algorithm that filters away dips and points on the same slope. The steps in the algorithm are as follows:
While there is a anotherPass (true) it will continue, or until max number of passes set initially
The point is copied to another array as long as the skip flag isn't set
Then it will compare current point with next to see if it has a down-slope
If it does, it will compare the next point with the following and see if it has an up-slope
If it does it is considered a dip and the skip flag is set so next point (the current middle point) won't be copied
The next filter will compare slope between current and next point, and next point and the following.
If they are the same skip flag is set.
If it had to set a skip flag it will also set anotherPass flag.
If no points where filtered (or max passes is reached) the loop will end
The core function is as follows:
while(anotherPass && max) {
skip = anotherPass = false;
for(i = 0; i < notes.length - 2; i += 2) {
if (!skip) curve.push(notes[i], notes[i+1]);
skip = false;
// if this to next points goes downward
// AND the next and the following up we have a dip
if (notes[i+3] >= notes[i+1] && notes[i+5] <= notes[i+3]) {
skip = anotherPass = true;
}
// if slope from this to next point =
// slope from next and following skip
else if (notes[i+2] - notes[i] === notes[i+4] - notes[i+2] &&
notes[i+3] - notes[i+1] === notes[i+5] - notes[i+3]) {
skip = anotherPass = true;
}
}
curve.push(notes[notes.length-2], notes[notes.length-1]);
max--;
if (anotherPass && max) {
notes = curve;
curve = [];
}
}
The result of the first pass would be after offsetting all the points on the y-axis - notice that the dipping note is ignored:
After running through all necessary passes the final point array would be represented as this:
The only step left is to smoothen the curve. For this I have used my own implementation of a cardinal spline (licensed under MIT and can be found here) which takes an array with x,y points and smooths it adding interpolated points based on a tension value.
It won't generate a perfect curve but the result from this would be:
FIDDLE
There are ways to improve the visual result which I haven't addressed, but I will leave it to you to do that if you feel it's needed. Among those could be:
Find center of points and increase the offset depending on angle so it arcs more at top
The end points of the smoothed curve sometimes curls slightly - this can be fixed by adding an initial point right below the first point as well at the end. This will force the curve to have better looking start/end.
You could draw double curve to make a taper effect (thin beginning/end, thicker in the middle) by using the first point in this list on another array but with a very small offset at top of the arc, and then render it on top.
The algorithm was created ad-hook for this answer so it's obviously not properly tested. There could be special cases and combination throwing it off but I think it's a good start.
Known weaknesses:
It assumes the distance between each stem is the same for the slope detection. This needs to be replaced with a factor based comparison in case the distance varies within a group.
It compares the slope with exact values which may fail if floating point values are used. Compare with an epsilon/tolerance

Create easy function 40% off set

I have animation follows this timing function: cubic-bezier(0.25, 0.1, 0.25, 1.0)
I want to mod this function so i just get the ending 40% of it. To make things easy lets just say I want the end 50% of the function. How can I do this.
So graphically this is what it is:
https://developer.mozilla.org/files/3429/cubic-bezier,ease.png
and I want to to make a cubic-bezier with parameters such that graphically we only see the top portion, so what we see from 0.5 to 1 (on the yaxist) porition of this graph, i want to make that same line but from 0 to 1.
Please help me how to make this function.
If you want only a section of a cubic curve, with t from 0 to 1, there are "simple" formulae to determine what the new coordinates need to be. I say simple because it's pretty straight forward to implement, but if you also want to know why the implementation actually works, that generally requires diving into maths, and some people consider that scary.
(The end result of the section on matrix splitting pretty much gives you the new coordinates for an arbitrary split-point without needing to read the explanation of why that works)
Let's take your example curve: first, we need to figure out what the curve's original coordinates are. We go with a guess of (0,0)-(0.4,0.25)-(0.2,1)-(1,1). We then want to split that curve up at t=0.4, so we ignore all of section 7 except for the final bit that tells us how to derive new coordinates. For any splitting point t=z (where z is somewhere between 0 and 1` we'll have two new sets of coordinates. One for the curve "before" the splitting point, and one for "after" the splitting point. We want the latter, so we pick:
So we just plug in 0.4 for z and off we go. Our new first point is 0.064 * P4 - 3 * 0.096 * P3 + 3 * 0.144 * P2 + 0.216 * P1 = 0.2944 (which we need to evaluate twice. Once for our x values, and one for our y values). We do the same for P2, P3 and P4 (although our fourth point is of course still the same so we don't need to bother. It was (1,1) and is still (1,1) after the split).
So, let's implement that in javascript:
function split(options) {
var z = options.z,
cz = z-1,
z2 = z*z,
cz2 = cz*cz,
z3 = z2*z,
cz3 = cz2*cz,
x = options.x,
y = options.y;
var left = [
x[0],
y[0],
z*x[1] - cz*x[0],
z*y[1] - cz*y[0],
z2*x[2] - 2*z*cz*x[1] + cz2*x[0],
z2*y[2] - 2*z*cz*y[1] + cz2*y[0],
z3*x[3] - 3*z2*cz*x[2] + 3*z*cz2*x[1] - cz3*x[0],
z3*y[3] - 3*z2*cz*y[2] + 3*z*cz2*y[1] - cz3*y[0]];
var right = [
z3*x[3] - 3*z2*cz*x[2] + 3*z*cz2*x[1] - cz3*x[0],
z3*y[3] - 3*z2*cz*y[2] + 3*z*cz2*y[1] - cz3*y[0],
z2*x[3] - 2*z*cz*x[2] + cz2*x[1],
z2*y[3] - 2*z*cz*y[2] + cz2*y[1],
z*x[3] - cz*x[2],
z*y[3] - cz*y[2],
x[3],
y[3]];
return { left: left, right: right};
}
Done deal. This function will give us two subcurves (called left and right, both Number[8] arrays in x1/y1/x2/y2/... ordering) that are mathematically identical to our original curve if taken together, except modeled as two new t=[0,1] intervals, for any splitting point t=z with z between 0 and 1. Our work is done forever.

What is the math behind this ray-like animation?

I have unobfuscated and simplified this animation into a jsfiddle available here. Nevertheless, I still don't quite understand the math behind it.
Does someone have any insight explaining the animation?
Your fiddle link wasn't working for me due to a missing interval speed, should be using getElementById too (just because it works in Internet Explorer doesn't make it cross-browser).
Here, I forked it, use this one instead:
http://jsfiddle.net/spechackers/hJhCz/
I have also cleaned up the code in your first link:
<pre id="p">
<script type="text/javascript">
var charMap=['p','.'];
var n=0;
function myInterval()
{
n+=7;//this is the amount of screen to "scroll" per interval
var outString="";
//this loop will execute exactly 4096 times. Once for each character we will be working with.
//Our display screen will consist of 32 lines or rows and 128 characters on each line
for(var i=64; i>0; i-=1/64)
{
//Note mod operations can result in numbers like 1.984375 if working with non-integer numbers like we currently are
var mod2=i%2;
if(mod2==0)
{
outString+="\n";
}else{
var tmp=(mod2*(64/i))-(64/i);//a number between 0.9846153846153847 and -4032
tmp=tmp+(n/64);//still working with floating points.
tmp=tmp^(64/i);//this is a bitwise XOR operation. The result will always be an integer
tmp=tmp&1;//this is a bitwise AND operation. Basically we just want to know if the first bit is a 1 or 0.
outString+=charMap[tmp];
}
}//for
document.getElementById("p").innerHTML=outString;
}
myInterval();
setInterval(myInterval,64);
</script>
</pre>
The result of the code in the two links you provided are very different from one another.
However the logic in the code is quite similar. Both use a for-loop to loop through all the characters, a mod operation on a non-integer number, and a bitwise xor operation.
How does it all work, well basically all I can tell you is to pay attention to the variables changing as the input and output change.
All the logic appears to be some sort of bitwise cryptic way to decide which of 2 characters or a line break to add to the page.
I don't quite follow it myself from a calculus or trigonometry sort of perspective.
Consider that each line, as it sweeps across the rectangular area, is actually a rotation of (4?) lines about a fixed origin.
The background appears to "move" according to optical illusion. What actually happens is that the area in between the sweep lines is toggling between two char's as the lines rotate through them.
Here is the rotation eq in 2 dimensions:
first, visualize an (x,y) coordinate pair in one of the lines, before and after rotation (motion):
So, you could make a collection of points for each line and rotate them through arbitrarily sized angles, depending upon how "smooth" you want the animation.
The answer above me looks at the whole plane being transformed with the given formulae.
I tried to simplify it here -
The formula above is a trigonometric equation for rotation it is more simply solved
with a matrix.
x1 is the x coordinate before the the rotation transformation (or operator) acts.
same for y1. say the x1 = 0 and y1 = 1. these are the x,y coordinates of of the end of the
vector in the xy plane - currently your screen. if you plug any angle you will get new
coordinates with the 'tail' of the vector fixes in the same position.
If you do it many times (number of times depends on the angle you choose) you will come back to 0 x = 0 and y =1.
as for the bitwise operation - I don't have any insight as for why exactly this was used.
each iteration there the bitwise operation acts to decide if the point the plane will be drawn or not. note k how the power of k changes the result.
Further reading -
http://en.wikipedia.org/wiki/Linear_algebra#Linear_transformations
http://www.youtube.com/user/khanacademy/videos?query=linear+algebra

Categories

Resources