How to calculate a geometric cross field inside an arbitrary polygon? - geometry

I'm having troubles finding a way to calculate a "cross-field" inside an arbitrary polygon.
A Cross field, as defined by one paper is the smoothest field that is tangential to the domain boundary (in this case the polygon) I find it a lot in quad re-topology papers but surprisingly not even in Wikipedia I can find the definition of a Cross field.
I have images but since I'm new here the system said I need at least 10 reputation points to upload images.
Any ideas?
I think it could be something along the lines of an interpolation? given an inner point determine the distance to each edge and integrate or weight sum the tangent and perpendicular vector of every edge by the distance? (or any other factor in fact)
But other simpler approaches may exist?
Thanks in advance!

//I've come up with something like this (for the 3D case), very raw, educational purposes
float ditance2segment(Vector3D p, Vector3D p0, Vector3D p1){
Vector3D v = p1 - p0;
Vector3D w = p - p0;
float c1 = v.Dot(w);
if (c1 <= 0)
return (p - p1).Length();
float c2 = v.Dot(v);
if (c2 <= c1)
return (p - p1).Length();
float b = c1 / c2;
Vector3D pb = p0 + b*v;
return (p - pb).Length();
}
void CrossFieldInterpolation(List<Vector3D>& Contour, List<Vector3D>& ContourN, Vector3D p, Vector3D& crossU, Vector3D& crossV){
int N = Contour.Amount();
for (int i=0; i < N; i++){
Vector3D u = Contour[(i + 1) % N] - Contour[i];
Vector3D n = 0.5*(ContourN[(i + 1) % N] + ContourN[i]);
Vector3D v = -Vector3D::Cross(u,n); //perpendicular vector
u = Vector3D::Normalize(u);
n = Vector3D::Normalize(n);
v = Vector3D::Normalize(v);
float dist = ditance2segment(p, Contour[i], Contour[(i+1)%N]);
crossU += u / (1+dist); //to avoid infinity at points over the segment
crossV += v / (1+dist);
}
crossU = Vector3D::Normalize(crossU);
crossV = Vector3D::Normalize(crossV);
}

You can check the OpenSource Graphite software that I'm developping, it implements the "Periodic Global Parameterization" algorithm [1] that was developed in my research team. You may be also interested in the following research articles with algorithms that we developed more recently [2],[3]
Graphite website:
http://alice.loria.fr/software/graphite
How to use Periodic Global Parameterization:
http://alice.loria.fr/WIKI/index.php/Graphite/PGP
[1] http://alice.loria.fr/index.php/publications.html?Paper=TOG_pgp%402006
[2] http://alice.loria.fr/index.php/publications.html?Paper=DGF#2008
[3] http://alice.loria.fr/index.php/publications.html?redirect=0&Paper=DFD#2008&Author=vallet

Related

What is the best algorithm for a non antialiased line and a aliased line

I'm new to shaders and I have been messing about with the website shadertoy. I'm trying to understand graphics (and the graphics pipeline) such as drawing lines, interpolation, rasterization, etc... I've written two line functions that return a color if the pixel processed is on the line. This is the shadertoy code here using fragment shaders
struct Vertex {
vec2 p;
vec4 c;
};
vec4 overlay(vec4 c1, vec4 c2) {
return vec4((1.0 - c2.w) * c1.xyz + c2.w * c2.xyz, 1.0);
}
vec4 drawLineA(Vertex v1, Vertex v2, vec2 pos) {
vec2 a = v1.p;
vec2 b = v2.p;
vec2 r = floor(pos);
vec2 diff = b - a;
if (abs(diff.y) < abs(diff.x)) {
if (diff.x < 0.0) {
Vertex temp1 = v1;
Vertex temp2 = v2;
v1 = temp2;
v2 = temp1;
a = v1.p;
b = v2.p;
diff = b - a;
}
float m = diff.y / diff.x;
float q = r.x - a.x;
if (floor(m * q + a.y) == r.y && a.x <= r.x && r.x <= b.x) {
float h = q / diff.x;
return vec4((1.0 - h) * v1.c + h * v2.c);
}
} else {
if (diff.y < 0.0) {
Vertex temp1 = v1;
Vertex temp2 = v2;
v1 = temp2;
v2 = temp1;
a = v1.p;
b = v2.p;
diff = b - a;
}
float m = diff.x / diff.y;
float q = r.y - a.y;
if (floor(m * q + a.x) == r.x && a.y <= r.y && r.y <= b.y) {
float h = q / diff.y;
return vec4((1.0 - h) * v1.c + h * v2.c);
}
}
return vec4(0,0,0,0);
}
vec4 drawLineB(Vertex v1, Vertex v2, vec2 pos) {
vec2 a = v1.p;
vec2 b = v2.p;
vec2 l = b - a;
vec2 r = pos - a;
float h = dot(l,r) / dot (l,l);
vec2 eC = a + h * l;
if (floor(pos) == floor(eC) && 0.0 <= h && h <= 1.0 ) {
return vec4((1.0 - h) * v1.c + h * v2.c);
}
return vec4(0,0,0,0);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
float t = iTime;
float r = 300.0;
Vertex v1 = Vertex(vec2(400,225), vec4(1,0,0,1));
Vertex v2 = Vertex(vec2(400.0 + r*cos(t) ,225.0 + r*sin(t)), vec4(0,1,0,1));
vec4 col = vec4(0,0,0,1);
col = overlay(col,drawLineA(v1, v2, fragCoord));
col = overlay(col,drawLineB(v1, v2, fragCoord));
// Output to screen
fragColor = col;
}
However, the lines that I have been using are not fast or using antialiasing. Which is the fastest algorithm for both antialiasing and aliasing lines, and how should I implement it thanks.
A fragment shader is really not the right approach for this, a lot on shadertoy is really just a toy / code-golfing showing solutions overcoming the limitations of the platform which are terribly inefficient in real-world scenarios.
All graphics APIs provide dedicated interfaces for drawing line segments just search for "API_NAME draw line" e.g. "webgl draw line". In cases where those do not suffice triangle strips with either MSAA or custom in-shader AA are used.
If you're really just looking for an efficient algorithm the wikipedia page has you covered on that.
As the other answer says shaders are not very good for this.
Line rasterization is done behind the scenes with HW interpolators on the gfx card these days. The shaders are invoked for each pixel of rendered primitive which in your case means its called for every pixel of screen and this all is invoked for each line you render which is massively slower than native way.
If you truly want to learn rasterization do this on CPU side instead. The best algo for lines depends on the computation HW architecture you are using.
For sequentional processing it is:
DDA this one is with subpixel precision
In the past Bresenham was faster but that is not true IIRC since x386 ...
For parallel processing you just compute distance of pixel to the line (more or less like you do now).
So if you insist on using shaders for this You can speed up things using geometry shader and process only fragment (pixels) that are near your line. See:
cubic curves rendering in GLSL
So simply you create OOBB around your line and render it by emitting 2 triangles per line then in fragment you compute the distance to line and set the color accordingly ...
For antialiasing you simply change the color for pixels on the last pixel edge distance. So if your line has half width w and distance of fragment to line is d then:
if (d>w) discard; // fragment too far
d=(w-d)/pixel_size; // distance from edge in pixels
frag_color = vec4(r,g,b,min(1.0,d)); // use transparency/blending
As you can see anti aliasing is just rendering with blending modulated by subpixel position/distance of pixel relative to rasterized object) the same technique can be used with DDA.
There are also ray tracing methods of rendering lines but they are pretty much the same as finding distance to line ... however instead of 2D pixel position you checking against 3D ray which slightly complicates the math.

Find intersection point ray/triangle in a right-hand coordinate system

I would like to get the intersection point of a line (defined by a vector and origin) on a triangle.
My engine use right handed coordinate system, so X pointing forward, Y pointing left and Z pointing up.
---- Edit ----
With Antares's help, I convert my points to engine space with:
p0.x = -pt0.y;
p0.y = pt0.z;
p0.z = pt0.x;
But I don't know how to do the same with the direction vector.
I use the function from this stackoverflow question, original poster use this tutorial.
First we look for the distance t from origin to intersection point, in order to find its coordinates.
But I've got a negative t, and code return true when ray is outside the triangle. I set it outside visualy.
It return sometime false when I'm in the triangle.
Here is the fonction I use to get the intersection point, I already checked that it works, with 'classic' values, as in the original post.
float kEpsilon = 0.000001;
V3f crossProduct(V3f point1, V3f point2){
V3f vector;
vector.x = point1.y * point2.z - point2.y * point1.z;
vector.y = point2.x * point1.z - point1.x * point2.z;
vector.z = point1.x * point2.y - point1.y * point2.x;
return vector;
}
float dotProduct(V3f dot1, V3f dot2){
float dot = dot1.x * dot2.x + dot1.y * dot2.y + dot1.z * dot2.z;
return dot;
}
//orig: ray origin, dir: ray direction, Triangle vertices: p0, p1, p2.
bool rayTriangleIntersect(V3f orig, V3f dir, V3f p0, V3f p1, V3f p2){
// compute plane's normal
V3f p0p1, p0p2;
p0p1.x = p1.x - p0.x;
p0p1.y = p1.y - p0.y;
p0p1.z = p1.z - p0.z;
p0p2.x = p2.x - p0.x;
p0p2.y = p2.y - p0.y;
p0p2.z = p2.z - p0.z;
// no need to normalize
V3f N = crossProduct(p0p1, p0p2); // N
// Step 1: finding P
// check if ray and plane are parallel ?
float NdotRayDirection = dotProduct(N, dir); // if the result is 0, the function will return the value false (no intersection).
if (fabs(NdotRayDirection) < kEpsilon){ // almost 0
return false; // they are parallel so they don't intersect !
}
// compute d parameter using equation 2
float d = dotProduct(N, p0);
// compute t (equation P=O+tR P intersection point ray origin O and its direction R)
float t = -((dotProduct(N, orig) - d) / NdotRayDirection);
// check if the triangle is in behind the ray
//if (t < 0){ return false; } // the triangle is behind
// compute the intersection point using equation
V3f P;
P.x = orig.x + t * dir.x;
P.y = orig.y + t * dir.y;
P.z = orig.z + t * dir.z;
// Step 2: inside-outside test
V3f C; // vector perpendicular to triangle's plane
// edge 0
V3f edge0;
edge0.x = p1.x - p0.x;
edge0.y = p1.y - p0.y;
edge0.z = p1.z - p0.z;
V3f vp0;
vp0.x = P.x - p0.x;
vp0.y = P.y - p0.y;
vp0.z = P.z - p0.z;
C = crossProduct(edge0, vp0);
if (dotProduct(N, C) < 0) { return false; }// P is on the right side
// edge 1
V3f edge1;
edge1.x = p2.x - p1.x;
edge1.y = p2.y - p1.y;
edge1.z = p2.z - p1.z;
V3f vp1;
vp1.x = P.x - p1.x;
vp1.y = P.y - p1.y;
vp1.z = P.z - p1.z;
C = crossProduct(edge1, vp1);
if (dotProduct(N, C) < 0) { return false; } // P is on the right side
// edge 2
V3f edge2;
edge2.x = p0.x - p2.x;
edge2.y = p0.y - p2.y;
edge2.z = p0.z - p2.z;
V3f vp2;
vp2.x = P.x - p2.x;
vp2.y = P.y - p2.y;
vp2.z = P.z - p2.z;
C = crossProduct(edge2, vp2);
if (dotProduct(N, C) < 0) { return false; } // P is on the right side;
return true; // this ray hits the triangle
}
My problem is I get t: -52.603783
intersection point P : [-1143.477295, -1053.412842, 49.525799]
This give me, relative to a 640X480 texture, the uv point: [-658, 41].
Probably because my engine use Z pointing up?
My engine use right handed coordinate system, so X pointing forward, Y pointing left and Z pointing up.
You have a slightly incorrect idea of a right handed coordinate system... please check https://en.wikipedia.org/wiki/Cartesian_coordinate_system#In_three_dimensions.
As the name suggests, X is pointing right (right hand's thumb to the right), Y is pointing up (straight index finger) and Z (straight middle finger) is pointing "forward" (actually -Z is forward, and Z is backward in the camera coordinate system).
Actually... your coordinate components are right hand sided, but the interpretation as X is forward etc. is unusual.
If you suspect the problem could be with the coordinate system of your engine (OGRE maybe? plain OpenGL? Or something selfmade?), then you need to transform your point and direction coordinates into the coordinate system of your algorithm. The algorithm you presented works in camera coordinate system, if I am not mistaken. Of course you need to transform the resulting intersection point back to the interpretation you use in the engine.
To turn the direction of a vector component around (e.g. the Z coordinate) you can use multiplication with -1 to achieve the effect.
Edit:
One more thing: I realized that the algorithm uses directional vectors as well, not just points. The rearranging of components does only work for points, not directions, if I recall correctly. Maybe you have to do a matrix multiplication with the CameraView transformation matrix (or its inverse M^-1 or was it the transpose M^T, I am not sure). I can't help you there, I hope you can figure it out or just do trial&error.
My problem is I get t: -52.603783
intersection point P : [-1143.477295, -1053.412842, 49.525799] This give me, relative to a 640X480 texture, the uv point: [-658, 41]
I reckon you think your values are incorrect. Which values do you expect to get for t and UV coordinates? Which ones would be "correct" for your input?
Hope this gets you started. GL, HF with your project! :)
#GUNNM: Concerning your feedback that you do not know how to handle the direction vector, here are some ideas that might be useful to you.
As I said, there should be a matrix multiplication way. Look for key words like "transforming directional vector with a matrix" or "transforming normals (normal vectors) with a matrix". This should yield something like: "use the transpose of the used transformation matrix" or "the inverse of the matrix" or something like that.
A workaround could be: You can "convert" a directional vector to a point, by thinking of a direction as "two points" forming a vector: A starting point and another point which lies in the direction you want to point.
The starting point of your ray, you already have available. Now you need to make sure that your directional vector is interpreted as "second point" not as "directional vector".
If your engine handles a ray like in the first case you would have:
Here is my starting point (0,0,0) and here is my directional vector (5,6,-7) (I made those numbers up and take the origin as starting point to have a simple example). So this is just the usual "start + gaze direction" case.
In the second case you would have:
Here is my start at (0,0,0) and my second point is a point on my directional vector (5,6,-7), e.g. any t*direction. Which for t=1 should give exactly the point where your directional vector is pointing to if it is considered a vector (and the start point being the origin (0,0,0)).
Now you need to check how your algorithm is handling that direction. If it does somewhere ray=startpoint+direction, then it interprets it as point + vector, resulting in a movement shift of the starting point while keeping the orientation and direction of the vector.
If it does ray=startpoint-direction then it interprets it as two points from which a directional vector is formed by subtracting.
To make a directional vector from two points you usually just need to subtract them. This gives a "pure direction" though, without defined orientation (which can be +t or -t). So if you need this direction to be fixed, you may take the absolute of your "vector sliding value" t in later computations for example (may be not the best/fastest way of doing it).

Math.Net Exponential Moving Average

I'm using simple moving average in Math.Net, but now that I also need to calculate EMA (exponential moving average) or any kind of weighted moving average, I don't find it in the library.
I looked over all methods under MathNet.Numerics.Statistics and beyond, but didn't find anything similar.
Is it missing in library or I need to reference some additional package?
I don't see any EMA in MathNet.Numerics, however it's trivial to program. The routine below is based on the definition at Investopedia.
public double[] EMA(double[] x, int N)
{
// x is the input series
// N is the notional age of the data used
// k is the smoothing constant
double k = 2.0 / (N + 1);
double[] y = new double[x.Length];
y[0] = x[0];
for (int i = 1; i < x.Length; i++) y[i] = k * x[i] + (1 - k) * y[i - 1];
return y;
}
Occasionally I found this package: https://daveskender.github.io/Stock.Indicators/docs/INDICATORS.html It targets to the latest .NET framework and has very detailed documents.
Try this:
public IEnumerable<double> EMA(IEnumerable<double> items, int notationalAge)
{
double k = 2.0d / (notationalAge + 1), prev = 0.0d;
var e = items.GetEnumerator();
if (!e.MoveNext()) yield break;
yield return prev = e.Current;
while(e.MoveNext())
{
yield return prev = (k * e.Current) + (1 - k) * prev;
}
}
It will still work with arrays, but also List, Queue, Stack, IReadOnlyCollection, etc.
Although it's not explicitly stated I also get the sense this is working with money, in which case it really ought to use decimal instead of double.

Algorithm for drawing a 4-connected line

I'm looking for an algorithm (coded in Java would be nice, but anything clear enough to translate to Java is fine) to draw a 4-connected line. It seems that Bresenham's algorithm is the most widely used, but all the understandable implementations I've found are 8-connected. OpenCV's cvline function apparently has a 4-connected version, but the source code is, to me, as a mediocre and nearly C-illiterate programmer, impenetrable. Various other searches have turned up nothing.
Thanks for any help anyone can provide.
The following is a Bresenham-like algorithm that draws 4-connected lines. The code is in Python but I suppose can be understood easily even if you don't know the language.
def line(x0, y0, x1, y1, color):
dx = abs(x1 - x0) # distance to travel in X
dy = abs(y1 - y0) # distance to travel in Y
if x0 < x1:
ix = 1 # x will increase at each step
else:
ix = -1 # x will decrease at each step
if y0 < y1:
iy = 1 # y will increase at each step
else:
iy = -1 # y will decrease at each step
e = 0 # Current error
for i in range(dx + dy):
draw_pixel(x0, y0, color)
e1 = e + dy
e2 = e - dx
if abs(e1) < abs(e2):
# Error will be smaller moving on X
x0 += ix
e = e1
else:
# Error will be smaller moving on Y
y0 += iy
e = e2
The idea is that to draw a line you should increment X and Y with a ratio that matches DX/DY of the theoretic line. To do this I start with an error variable e initialized to 0 (we're on the line) and at each step I check if the error is lower if I only increment X or if I only increment Y (Bresenham check is to choose between changing only X or both X and Y).
The naive version for doing this check would be adding 1/dy or 1/dx, but multiplying all increments by dx*dy allows using only integer values and that improves both speed and accuracy and also avoids the need of special cases for dx==0 or dy==0 thus simplifying the logic.
Of course since we're looking for a proportion error, using a scaled increment doesn't affect the result.
Whatever is the line quadrant the two possibilities for the increment will always have a different sign effect on the error... so my arbitrary choice was to increment the error for an X step and decrement the error for an Y step.
The ix and iy variables are the real directions needed for the line (either +1 or -1) depending on whether the initial coordinates are lower or higher than the final coordinates.
The number of pixels to draw in a 4-connected line is obviously dx+dy, so I just do a loop for that many times to draw the line instead of checking if I got to the end point. Note that this algorithm draws all pixels except the last one; if you want also that final pixel then an extra draw_pixel call should be added after the end of the loop.
An example result of the above implementation can be seen in the following picture
For the Python-illiterate, here is a C version of 6502's code:
void drawLine(int x0, int y0, int x1, int y1) {
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
int sgnX = x0 < x1 ? 1 : -1;
int sgnY = y0 < y1 ? 1 : -1;
int e = 0;
for (int i=0; i < dx+dy; i++) {
drawPixel(x0, y0);
int e1 = e + dy;
int e2 = e - dx;
if (abs(e1) < abs(e2)) {
x0 += sgnX;
e = e1;
} else {
y0 += sgnY;
e = e2;
}
}
}

How do I convert the 2 control points of a cubic curve to the single control point of a quadratic curve?

Having searched the web, I see various people in various forums alluding to approximating a cubic curve with a quadratic one. But I can't find the formula.
What I want is this:
input: startX, startY, control1X, control1Y, control2X, control2Y, endX, endY
output: startX, startY, controlX, controlY, endX, endY
Actually, since the starting and ending points will be the same, all I really need is...
input: startX, startY, control1X, control1Y, control2X, control2Y, endX, endY
output: controlX, controlY
As mentioned, going from 4 control points to 3 is normally going to be an approximation. There's only one case where it will be exact - when the cubic bezier curve is actually a degree-elevated quadratic bezier curve.
You can use the degree elevation equations to come up with an approximation. It's simple, and the results are usually pretty good.
Let's call the control points of the cubic Q0..Q3 and the control points of the quadratic P0..P2. Then for degree elevation, the equations are:
Q0 = P0
Q1 = 1/3 P0 + 2/3 P1
Q2 = 2/3 P1 + 1/3 P2
Q3 = P2
In your case you have Q0..Q3 and you're solving for P0..P2. There are two ways to compute P1 from the equations above:
P1 = 3/2 Q1 - 1/2 Q0
P1 = 3/2 Q2 - 1/2 Q3
If this is a degree-elevated cubic, then both equations will give the same answer for P1. Since it's likely not, your best bet is to average them. So,
P1 = -1/4 Q0 + 3/4 Q1 + 3/4 Q2 - 1/4 Q3
To translate to your terms:
controlX = -0.25*startX + .75*control1X + .75*control2X -0.25*endX
Y is computed similarly - the dimensions are independent, so this works for 3d (or n-d).
This will be an approximation. If you need a better approximation, one way to get it is by subdividing the initial cubic using the deCastlejau algorithm, and then degree-reduce each segment. If you need better continuity, there are other approximation methods that are less quick and dirty.
The cubic can have loops and cusps, which quadratic cannot have. This means that there are not simple solutions nearly never. If cubic is already a quadratic, then the simple solution exists. Normally you have to divide cubic to parts that are quadratics. And you have to decide what are the critical points for subdividing.
http://fontforge.org/bezier.html#ps2ttf says:
"Other sources I have read on the net suggest checking the cubic spline for points of inflection (which quadratic splines cannot have) and forcing breaks there. To my eye this actually makes the result worse, it uses more points and the approximation does not look as close as it does when ignoring the points of inflection. So I ignore them."
This is true, the inflection points (second derivatives of cubic) are not enough. But if you take into account also local extremes (min, max) which are the first derivatives of cubic function, and force breaks on those all, then the sub curves are all quadratic and can be presented by quadratics.
I tested the below functions, they work as expected (find all critical points of cubic and divides the cubic to down-elevated cubics). When those sub curves are drawn, the curve is exactly the same as original cubic, but for some reason, when sub curves are drawn as quadratics, the result is nearly right, but not exactly.
So this answer is not for strict help for the problem, but those functions provide a starting point for cubic to quadratic conversion.
To find both local extremes and inflection points, the following get_t_values_of_critical_points() should provide them. The
function compare_num(a,b) {
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
function find_inflection_points(p1x,p1y,p2x,p2y,p3x,p3y,p4x,p4y)
{
var ax = -p1x + 3*p2x - 3*p3x + p4x;
var bx = 3*p1x - 6*p2x + 3*p3x;
var cx = -3*p1x + 3*p2x;
var ay = -p1y + 3*p2y - 3*p3y + p4y;
var by = 3*p1y - 6*p2y + 3*p3y;
var cy = -3*p1y + 3*p2y;
var a = 3*(ay*bx-ax*by);
var b = 3*(ay*cx-ax*cy);
var c = by*cx-bx*cy;
var r2 = b*b - 4*a*c;
var firstIfp = 0;
var secondIfp = 0;
if (r2>=0 && a!==0)
{
var r = Math.sqrt(r2);
firstIfp = (-b + r) / (2*a);
secondIfp = (-b - r) / (2*a);
if ((firstIfp>0 && firstIfp<1) && (secondIfp>0 && secondIfp<1))
{
if (firstIfp>secondIfp)
{
var tmp = firstIfp;
firstIfp = secondIfp;
secondIfp = tmp;
}
if (secondIfp-firstIfp >0.00001)
return [firstIfp, secondIfp];
else return [firstIfp];
}
else if (firstIfp>0 && firstIfp<1)
return [firstIfp];
else if (secondIfp>0 && secondIfp<1)
{
firstIfp = secondIfp;
return [firstIfp];
}
return [];
}
else return [];
}
function get_t_values_of_critical_points(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
c = p1x - c1x,
t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a,
tvalues=[];
Math.abs(t1) > "1e12" && (t1 = 0.5);
Math.abs(t2) > "1e12" && (t2 = 0.5);
if (t1 >= 0 && t1 <= 1 && tvalues.indexOf(t1)==-1) tvalues.push(t1)
if (t2 >= 0 && t2 <= 1 && tvalues.indexOf(t2)==-1) tvalues.push(t2);
a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
c = p1y - c1y;
t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
Math.abs(t1) > "1e12" && (t1 = 0.5);
Math.abs(t2) > "1e12" && (t2 = 0.5);
if (t1 >= 0 && t1 <= 1 && tvalues.indexOf(t1)==-1) tvalues.push(t1);
if (t2 >= 0 && t2 <= 1 && tvalues.indexOf(t2)==-1) tvalues.push(t2);
var inflectionpoints = find_inflection_points(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
if (inflectionpoints[0]) tvalues.push(inflectionpoints[0]);
if (inflectionpoints[1]) tvalues.push(inflectionpoints[1]);
tvalues.sort(compare_num);
return tvalues;
};
And when you have those critical t values (which are from range 0-1), you can divide the cubic to parts:
function CPoint()
{
var arg = arguments;
if (arg.length==1)
{
this.X = arg[0].X;
this.Y = arg[0].Y;
}
else if (arg.length==2)
{
this.X = arg[0];
this.Y = arg[1];
}
}
function subdivide_cubic_to_cubics()
{
var arg = arguments;
if (arg.length!=9) return [];
var m_p1 = {X:arg[0], Y:arg[1]};
var m_p2 = {X:arg[2], Y:arg[3]};
var m_p3 = {X:arg[4], Y:arg[5]};
var m_p4 = {X:arg[6], Y:arg[7]};
var t = arg[8];
var p1p = new CPoint(m_p1.X + (m_p2.X - m_p1.X) * t,
m_p1.Y + (m_p2.Y - m_p1.Y) * t);
var p2p = new CPoint(m_p2.X + (m_p3.X - m_p2.X) * t,
m_p2.Y + (m_p3.Y - m_p2.Y) * t);
var p3p = new CPoint(m_p3.X + (m_p4.X - m_p3.X) * t,
m_p3.Y + (m_p4.Y - m_p3.Y) * t);
var p1d = new CPoint(p1p.X + (p2p.X - p1p.X) * t,
p1p.Y + (p2p.Y - p1p.Y) * t);
var p2d = new CPoint(p2p.X + (p3p.X - p2p.X) * t,
p2p.Y + (p3p.Y - p2p.Y) * t);
var p1t = new CPoint(p1d.X + (p2d.X - p1d.X) * t,
p1d.Y + (p2d.Y - p1d.Y) * t);
return [[m_p1.X, m_p1.Y, p1p.X, p1p.Y, p1d.X, p1d.Y, p1t.X, p1t.Y],
[p1t.X, p1t.Y, p2d.X, p2d.Y, p3p.X, p3p.Y, m_p4.X, m_p4.Y]];
}
subdivide_cubic_to_cubics() in above code divides an original cubic curve to two parts by the value t. Because get_t_values_of_critical_points() returns t values as an array sorted by t value, you can easily traverse all t values and get the corresponding sub curve. When you have those divided curves, you have to divide the 2nd sub curve by the next t value.
When all splitting is proceeded, you have the control points of all sub curves. Now there are left only the cubic control point conversion to quadratic. Because all sub curves are now down-elevated cubics, the corresponding quadratic control points are easy to calculate. The first and last of quadratic control points are the same as cubic's (sub curve) first and last control point and the middle one is found in the point, where lines P1-P2 and P4-P3 crosses.
Conventions/terminology
Cubic defined by: P1/2 - anchor points, C1/C2 control points
|x| is the euclidean norm of x
mid-point approx of cubic: a quad that shares the same anchors with the cubic and has the control point at C = (3·C2 - P2 + 3·C1 - P1)/4
Algorithm
pick an absolute precision (prec)
Compute the Tdiv as the root of (cubic) equation sqrt(3)/18 · |P2 - 3·C2 + 3·C1 - P1|/2 · Tdiv ^ 3 = prec
if Tdiv < 0.5 divide the cubic at Tdiv. First segment [0..Tdiv] can be approximated with by a quadratic, with a defect less than prec, by the mid-point approximation. Repeat from step 2 with the second resulted segment (corresponding to 1-Tdiv)
0.5<=Tdiv<1 - simply divide the cubic in two. The two halves can be approximated by the mid-point approximation
Tdiv>=1 - the entire cubic can be approximated by the mid-point approximation
The "magic formula" at step 2 is demonstrated (with interactive examples) on this page.
Another derivation of tfinniga's answer:
First see Wikipedia Bezier curve
for the formulas for quadratic and cubic Bezier curves (also nice animations):
Q(t) = (1-t)^2 P0 + 2 (1-t) t Q + t^2 P3
P(t) + (1-t)^3 P0 + 3 (1-t)^2 t P1 + 3 (1-t) t^2 P2 + t^3 P3
Require these to match at the middle, t = 1/2:
(P0 + 2 Q + P3) / 4 = (P0 + 3 P1 + 3 P2 + P3) / 8
=> Q = P1 + P2 - (P0 + P1 + P2 + P3) / 4
(Q written like this has a geometric interpretation:
Pmid = middle of P0 P1 P2 P3
P12mid = midway between P1 and P2
draw a line from Pmid to P12mid, and that far again: you're at Q.
Hope this makes sense -- draw a couple of examples.)
In general, you'll have to use multiple quadratic curves - many cases of cubic curves can't be even vaguely approximated with a single quadratic curve.
There is a good article discussing the problem, and a number of ways to solve it, at http://www.timotheegroleau.com/Flash/articles/cubic_bezier_in_flash.htm (including interactive demonstrations).
I should note that Adrian's solution is great for single cubics, but when the cubics are segments of a smooth cubic spline, then using his midpoint approximation method causes slope continuity at the nodes of the segments to be lost. So the method described at http://fontforge.org/bezier.html#ps2ttf is much better if you are working with font glyphs or for any other reason you want to retain the smoothness of the curve (which is most probably the case).
Even though this is an old question, many people like me will see it in search results, so I'm posting this here.
I would probably draw a series of curves instead of trying to draw one curve using a different alg. Sort of like drawing two half circles to make up a whole circle.
Try looking for opensource Postcript font to Truetype font converters. I'm sure they have it. Postscript uses cubic bezier curves, whereas Truetype uses quadratic bezier curves. Good luck.

Resources