Vectorize Cubic Bezier for 2D WebGL - javascript

I am building a 2D cad-like application in Javascript using WebGL and need to allow users to draw cubic bezier curves. My problem is that, as far as I know, WebGL doesn't have any easy way to draw anything but lines and filled triangles.
What makes it more complicated is that I want 'X' number of pixels per segment, and thus will not be able to just iterate through every 1% along the line.
I imagine that this would go something like:
Calculate the total length of the bezier curve
Divide that number by the segments per pixel
Iterate through the bezier curve by the previous number
This is an extremely high performance situation (hundreds of curves at a time), so I can't afford to use a constant number of segments for every curve.
So, my questions are:
Is there any native way to draw a cubic bezier in WebGL?
If not, can anyone help me with the calculations mentioned above, particularly the total length of a cubic bezier curve?

There's no direct way to tell WebGL to "draw a Curve". Just triangles (and lines).
So I think your general approach (Estimate length, divide for desired smoothness, walk the curve) will be good.
You could use a Vertex Shader to do some of the calculations, though. Depending on your data set, and how much it changes, it might be a win.
In WebGL, the vertex shader takes a list of points as input, and produces a same-sized list of points as output, after some transformation. The list cannot change size, so you'll need to figure out the number of subdivisions up in JS land.
The vertex shader could calculate the curve positions, if you assigned each point an attribute "t" between 0 and 1 for the parametric version of the Bezier. Might be handy.
From wikipedia,
As for Bezier length, if we describe it as (p0, p1, p2, p3) where p0 and p3 are the end points and p1 and p2 are the control points, we can quickly say that the Bezier length is at least dist(p0,p3), and at most dist(p0,p1)+dist(p1,p2)+dist(p2,p3).
Could make a fast-guess based on that.
A more thorough discussion for numerical solution is at https://math.stackexchange.com/questions/338463/length-of-bezier-curve-with-simpsons-rule.
There's no closed form solution.
Possibly of interest, I rendered a little Bezier animation for a blog post

I just wanted to add that clearly, we've been rasterizing Bézier curves for a long time, so in addition to the steve.hollasch.net link (http://steve.hollasch.net/cgindex/curves/cbezarclen.html) I pulled from a linked page in #davidvanbrink's answer, I figured there ought to be other resources for this...obviously WebGL/OpenGL adds another dimensional component into finding the appropriate resolution, but this cannot be something that hasn't been attempted before. I think the following links might prove useful.
http://en.wikipedia.org/wiki/NURBS (Non-uniform rational B-spline)
http://antigrain.com/research/adaptive_bezier/index.html (Adaptive Subdivision of Bezier Curves: An attempt to achieve perfect result in Bezier curve approximation)
http://www.neuroproductions.be/experiments/nurbs/
http://threejs.org/examples/webgl_geometry_nurbs.html

Related

Is it possible to convert svg path with stroke to svg path without any styles and properties? [duplicate]

This question already has answers here:
svg: generate 'outline path'
(2 answers)
Closed 5 years ago.
I want to convert a stroked path to a filled object. (Programmatically, in JavaScript.)
The line is just a simple curved line, a sequence of coordinates. I can render this line as a path, and give it a stroke of a certain thickness... but I'm trying to get a filled shape rather than a stroked line, so that I can do further modifications on it, such as warping it, so the resulting 'stroke' might vary in thickness or have custom bits cut out of it (neither of these things are possible with a real SVG stroke, as far as I can tell).
So I'm trying to manually 'thicken' a line into a solid shape. I can't find any function that does this – I've looked through the docs of D3.js and Raphaël, but no luck. Does anyone know of a library/function that would do this?
Or, even better: if someone could explain to me the geometry theory about how I would do this task manually, by taking the list of line coordinates I have and working out a new path that effectively 'strokes' it, that would be amazing. To put it another way, what does the browser do when you tell it to stroke a path – how does it work out what shape the stroke should be?
There has been a similar question recently:
svg: generate 'outline path'
All in all, this is a non-trivial task. As mentioned in my answer to the linked question, PostScript has a command for generating paths that produce basically the same output as a stroke, called strokepath. If you look at what Ghostscript spits out when you run the code I posted at the linked question, it's pretty ugly. And even Inkscape doesn't really do a good job. I just tried Path => Outline stroke in Inkscape (I think that's what the English captions should say), and what came out didn't really look the same as the stroked path.
The "simplest" case would be if you only have non-self-intersecting polylines, polygons or paths that don't contain curves because in general, you can't draw exact "parallel" Bézier curves to the right and the left of a non-trivial Bézier curve that would delimit the stroked area - it's mathematically non-existent. So you would have to approximate it one way or the other. For straight line segments, the exact solution can be found comparatively easily.
The classic way of rendering vector paths with curves/arcs in them is to approximate everything with a polyline that is sufficiently smooth. De Casteljau's Algorithm is typically used for turning Bézier curves into line segments. (That's also basically what comes out when you use the strokepath command in Ghostscript.) You can then find delimiting parallel line segments, but have to join them correctly, using the appropriate linejoin and miterlimit rules. Of course, don't forget the linecaps.
I thought that self-intersecting paths might be tricky because you might get hollow areas inside the path, i.e. the "crossing area" of a black path might become white. This might not be an issue for open paths when using nonzero winding rule, but I'd be cautious about this. For closed paths, you probably need the two "delimiting" paths to run in opposite orientation. But I'm not sure right now whether this really covers all the potential pitfalls.
Sorry if I cause a lot of confusion with this and maybe am not of much help.
This page has a fairly good tutorial on bezier curves in general with a nice section on offset curves.
http://pomax.github.io/bezierinfo/
A less precise but possibly faster method can be found here.
http://seant23.wordpress.com/2010/11/12/offset-bezier-curves/
There is no mathematical answer, because the curve parallel to a bezier curve is not generally a bezier curve. Most methods have degenerate cases, especially when dealing with a series of curves.
Think of a simple curve as one with no trouble spots. No cusps, no loops, no inflections, and ideally a strictly increasing curvature. Chop up all the starting curves into these simple curves. Find all the offset curves of these simple curves. Put all the offset curves back together dealing with gaps and intersections. Quadratic curves are much more tractable if you have the option to work with them.
I think most browsers do something similar to processingjs, as they have degenerate cases even with quadratic curves. For example, look at the curve 200,300 719,301 500,300 with a thickness of 100 or more.
The standard method is the Tiller-Hanson algorithm (Offsets of Two-Dimensional Profiles, 1984, which irritatingly is not on line for free) which creates a good approximation. The idea is that because the control points of each Bezier curve lie on lines tangent to the start and end of the curve, a parallel curve will have the same property. So we offset the start and the end of the curve, then find new control points using these intersections. However, that gives very bad results for sharp curves, so the first step is to bisect the original curve, which is very easy to do to Bezier curves, until it turns through a sufficiently small angle.
Other refinements are needed to deal with (i) intersections between the parallels, on the inside of each vertex; (ii) inserting an arc of a circle to fill the gap on the outside of each vertex; and (iii) adding end-caps - square, butt or circular.
Tiller-Hanson is difficult to implement, but there's a good open-source implementation in the FreeType library, in ftstroke.c (http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/src/base/ftstroke.c).
I'm sorry to say that it can be quite difficult to integrate this code, but I have used it successfully, and it works well.

Reduce 2d irregular shapes to regular primatives

I'm looking for an algorithm or tried & tested method for analyzing irregular polgygons and reducing them to primitives (squares, rectangles, trapeziums). A way of recurrsively looking over shapes to determine the best fit for regular polygons.
see image blow
The black shapes are the irregular polygons and the blue depicts the desired regualar, which fits inside. The left example should be straight forward but that's because it's a case of finding the a rectangle that can fit in the largest shape. The polygons will be of an undetermined size (but let's say they have less then 32 sides) What I'm hoping for is to be able to break up polys into several regualar ones - which is where I'm a little lost.
Sadly, I've no code at this point as I'm stuck to know the best way forward. The script will be done in pure JavasScript. This isn't homework :)
First of all, you need to check whether your polygon is convex or concave. If it is the latter, then you need to view it as several convex polygons "put together" and handle them separately. (It is easy to your mind to imagine a large pair of scissors which cuts the polygon into several smaller polygons). When this is done, you either have a single polygon, or several polygons.
For each polygon, calculate the gravity point of the polygon and (P(i), P((i + 1) mod n), G) will form a trivial form, a triangle. These triangles will solve your problem.
If you need shapes with four angles, then four consecutive points form a shape of four angles. But this approach might leave you with a smaller polygon with lesser number of angles in the middle of the main polygon, which will have to be handled.

Finding dimensions of a Path2D

In a project of mine I'm drawing a bunch of custom Path2D shapes onto a Canvas. Since I need it to be extensible, to be able to do stuff like perform hit detection on them, and interact with other elements, I need to be able to detect the size of a given Path2D.
As far as I can tell there's no way to do this through Path2D's interface (yet, it's still experimental), does anyone know how I might achieve this, short of requiring the user to specify the width and length themselves when subclassing my class?
Thanks.
For hit detection, you can use context.isPointInPath.
You're correct about Path2D total lengths & bounding boxes...
There's no native Path2D (or html5 canvas) method like SVG's .getTotalLength or .getPointAtLength. Therefore, to calculate total length or bounding boxes you'll have to calculate points along each component of the path.
You probably know (or can Google) that the line has a simple geometric solution to calculate length & bounds given the starting and ending points on the line segment. And the arc has an equally simple geometric arc-length calculation and a simple trigonometric solution to plot points given the center point, radius and the beginning / ending arc angles. Hint: an arc's bounds can be calculated by finding the minimum & maximum x,y values of: the arc's centerpoint and any existing point on the arc at 0, 90, 180 & 270 degrees.
Bezier curves are a bit harder to plot, so here's a hint: Cubic Bezier curves can be plotted using De Casteljau's algorithm. Quadratic Bezier curves can also be plotted using De Casteljau's algorithm--just set the 2 middle control points equal to each other and a Cubic Bezier curve becomes a Quadratic Bezier curve. This is a "brute force" method but a fairly accurate measurement can be obtained by sampling as few as 20 intervals along the curve.
If your design requirements allow more rough approximates, then you will find that cubic Bezier curves are always contained within their control points.
If you're more mathematically inclined, you can also use first derivative roots to more directly calculate the bounds of Bezier curves. For more information, there is an excellent treatise on Bezier Curves here: http://pomax.github.io/bezierinfo/

Auto adjusting the length of a Bézier curve to meet a predefined length without moving its end points

I'm working on an SVG graphics program where the user can draw a Bézier curve and then define its length with text. By default, when the user defines its new length, I want the endpoints to stay in the same position, and for the curvature of the line to adjust to accommodate the new length (for example, for a cubic bezier curve with points P0,...P3, I want P0 and P3 to stay stationary while the control points move, here's a supporting wikipedia image: http://upload.wikimedia.org/wikipedia/commons/thumb/d/d0/Bezier_curve.svg/600px-Bezier_curve.svg.png).
I am already able to calculate the current length of the curve with svg.js, and have set up a function that will scale length of each control line (P0 -> P1, and P3 -> P2) by a constant to achieve the desired length. This approach has mostly given me the results I'm looking for, but it seems like my scaling factor is off. Based on reading A Primer on Bézier Curves, I have tried using newLength/oldLength as the scaling factor, and I have also tried (newLength/oldLength)^2, both of which got me closer to the user entered length, however I had to do this process 4-5 times to get me closer to the answer I was looking for. I also recognize that this approach (control length scaling) is not full proof, and that there are several edge cases here that this doesn't account for, but I want to conditionally scale the length of this line based on whether the angle and/or length of the control lines is fixed.
I should add that the number of control points that the Bézier curve has is not necessarily fixed, but right now I'm only looking at the cubic case. Higher precision and accuracy is ideal, however it's fine if it's off by a few pixels.

Draw good-looking Bezier Curve through random points

I'm using javascript with RaphaelJS to draw a smooth line through random points with output to SVG.
The line goes strictly horizontally, without returning back in axis X.
Currently, I'm using cubic Bezier curves to draw the line from one point to another.
Problem is, the line doesn't look good enough. Two curves have an unsightly join at a point, where one curve ends and another curve starts, with quite random angle in the joint.
How do I get the previous curve to transform into next one smoothly, while still keeping the line passing through given point?
Catmull-Rom curves work well for going through points. http://schepers.cc/getting-to-the-point
Cubic splines
If you want to draw lines through points, then you want interpolation. Beziers are cubic curves with off-curve control nodes, but a cubic spline is a set of cubic curves through n points, with smooth changes from each to the next. See the Wikipedia article for more detail on the maths.
To construct a cubic spline through a set of points, you unfortunately have to perform an iterative procedure; you are essentially making n-1 cubic curves and matching their parameters together, which gives you n+1 simultaneous equations to solve. Once you have done that once, as you move the points you can use your previous solution as a starting point.
To do this in Raphael you'll need to generate the cubic spline, then calculate the equivalent Bezier control values for each segment.
There are pieces of javascript out there to calculate cubic splines for you, for example
Cubic splines in JavaScript (via CoffeeScript).
Piecewise polynomial
An alternative to cubic splines is to fit a cubic (or higher) polynomial to each set of a few points; e.g. cubic to each 4 points, including overlaps. So points 10-13 make the cubic for the line from 11 to 12. It will not be as smooth as a cubic spline, but it should be much closer. This is pretty similar to cubic spline, but without equation solve for the curve parameters to make everything smooth.
The problem with piecewise polynomial is that it uses higher order polynomials, which are unstable, and you can get large kinks and wiggles when the points don't lie on polynomial lines or when the points are close together.
To draw this in Raphael you are probably best just sampling the line more frequently and using straight lines to draw it.
Line shape
One big consideration is what kind of line you want to draw. If you just want a smooth line, do cubic spline. But if you are drawing statistics or some other specific kind of line you may be better off looking into gaussian decomposition or other things: Cubic splines are cubic polynomials (ax3 + bx2 + cx + d = 0), so you cannot approximate sine curves very well (audio/signals), or Gaussians (signals/statistics), or exponentials (decay curves, long tail statistics).
What about averaging the angles of the tangents at your points? This gets rid of the 'unsightly joint'.
I recommend you pull out your Foley and van Dam Fundamentals of Interactive Computer Graphics and take a look at the Hermite form for a parametric curve. The Hermite curve is defined by two end-points (which the curve passes through) and two tangent vectors controlling the direction of the curve as it passes through those points. It is readily convertible into Bezier form with a few matrix multiplications, but the advantage is: for smooth joins, adjacent sections of the curve will use exactly the same tangents at coinciding points, whereas with Beziers you have to force three points to be collinear.

Categories