I'm using the following code to generate a bezier curve for a workflow design:
group.append("svg:path")
.attr("d","m " +(300*k) +",0 q "+(z*150)+",-300,"+z*300+", 0")
.attr("fill","blue")
.style("stroke", "blue").attr("stroke-width",2)
.attr("marker-end", "url(#arrow)").attr("fill","none").append("text");
I want to achieve this animation in the curve. Any suggestion might help.
Here is an example of animated Bezier curves. You can inspect the code that he is using on that page, but, basically the trick is as they say...
For a second-order or quadratic Bézier curve, first we find two intermediate points that are t along the lines between the three control points. Then we perform the same interpolation step again and find another point that is t along the line between those two intermediate points. Plotting this last point yields a quadratic Bézier curve. The same steps can be repeated for higher orders.
Related
I have a large svg path which has quadratice bezier curves.
The path data is used for drawing maps.
How to reduce the number of points of the bezier curve without distorting the overall shape?
You don't say whether you want to do this offline (pre-prepared paths) or online (on the fly). If offline is fine, use a tool like Inkscape.
If you want to calculate the simplified curve yourself, then the typical algorithm used to do this is also the same one that has been used for drawing bezier curves. The algorithm is called "flattening".
Basically the idea is to convert the bezier curves to a series of straight line segments. Because you don't want the the flatness of the line segments to be visible, you have to take into account the scale of the drawing and how curvy the bezier is. If the bezier is very curvy you have to use more line segments than if it is fairly straight.
What you typically do is divide each bezier into two using De Casteljau's algorithm. Then look at each of the two half bezier curves. If a bezier is straight enough to meet a flatness limit you decide on, then stop dividing. Otherwise, divide in half and try again.
At the end of the process you should get a polyline that is indistinguishable from the bezier version. Or if you use a "flatness test" that is a bit courser than that, you will get a rougher approximation of the shape. In your case a map.
If you google bezier flattening you can find a number of papers on the technique. And a few pages which describe how to do it in a more friendly accessible way. For example this one, which is about generating offset curves, but starts out by describing how to flatten a curve:
https://seant23.wordpress.com/2010/11/12/offset-bezier-curves/
Checking if two cubic Bézier curves intersect gives a link to http://cagd.cs.byu.edu/~557/text/ch7.pdf .. sounds readable on first pass.. but it is not code.
I am wondering if someone has actually implemented this algorithm in any common programming language. I would be interested in some Javascript code (other languages OK) that can implement the algorithm using two cubic Bézier curves, or a Bézier curve and a straight line.
For a cubic Bézier curves and a straight line it probably easiest to use section 7.3 Intersection of a Parametric Curve and an Implicit Curve. You can write the straight line as a x+b y+c=0. If you Bézier curves in given by cubic p(t) and q(t) for x and y coords you can substitute those into the line equation. This gives a cubic in t which you can solve by your favourite root finding algorithm.
The similar question Checking if two cubic Bézier curves intersect has some good answers. In particular the first answer mentions the asymptote library which has code for all these. You can see the relevant source code at http://sourceforge.net/p/asymptote/code/HEAD/tree/trunk/asymptote/path.cc including code for finding roots of a cubic.
Here's what I am trying to solve. I have bezier curves that contain 3 points (x1, y1), (x2, y2), (x3, y3) (that are in a 2-dimensional plane). What I am trying to figure out is if a fourth point is clicked by a user on the bezier curves whether the click point lies between points 1 and 2 or if lies between points 2 and 3. The click point is only recorded when the line is directly clicked so it must lie between either points 1 and 2 or points 2 and 3.
The lines are randomly created at compile time and can start and end from any (x, y) position.
The 3 sets of points that make up the line are the start point, curve point and the end point. These three points are the control points of the line. A line object is then created from the control points. The control points are randomly created during each run of the program, making the spline different every time.
Is there any specific algorithm that should be followed for this problem. I am coding this in javascript but any c++ or java like pseudo code is fine. Thanks for the help.
Build a LUT (lookup table) for your curve, so that when the user clicks on it, you can resolve the (x,y) coordinate they clicked to the curve's t value (or whatever you called your control variable, of course). Rather than evaluating based on (x,y) coordinates, which is virtually impossible, resolve all four coordinates to t values, and it becomes really simple:
With (x1,y1) being t=0, (x2,y2) being some t=T and (x3,y3) being t=1, if a user clicks anywhere on the curve, we get a new t value. If that value is less than T, the point lies between points 1 and 2, and if it's greater than T, it lies between points 2 and 3.
Build the lookup table should be a one-time operation per curve, run the first time you draw the curve, since that's when you're already mapping t values to (x,y) coordinates, so you can build the reverse mapping "for free". If you don't control the draw code, then you'll have to run your own when you create the curve.
That has one problem: the curve you've given here, defined by three on-curve points, is not the usual way to define a Bezier curve. For Bezier curves, the control points define the curve "hull"; for a quadratic curve (with three points), that means points 1 and 3 are on the curve, but point 2 is very much not. To find the true Bezier curve based on these three points (i.e. the curve that goes through those three points) you'd need to run the algorithm that turns three points into a true curve.
(Full code to tell you how to do that is pretty much beyond the scope of this answer, but I explain it in a long article on Bezier curves, culminating in http://pomax.github.io/bezierinfo/#pointcurves for forming true curves based on three points)
If I have a line (possibly with some spline interpolation specified), can I extract the interpolated y-value at a given x-value? Thanks!
It's possible, but there's not yet a built-in facility for doing so. All of D3’s splines are implemented as piecewise quadratic or cubic Bézier curves (because they are rendered to SVG path elements). You can use de Casteljau's algorithm for computing the xy-coordinates for a given parameter t in [0,1].
It's a bit harder to compute y for a given x because it's possible to have multiple y values for the same x, depending on the curve. For that, I recommend looking at this Bézier curves primer which describes an algorithm for computing a curve-line intersection; this could be simplified for a vertical line (constant x).
I don't think there's a good answer to this, but I'd like to find out if there's a better way to do this.
I need to plot a mathematical function, which is nearly flat at one end of the display, and nearly vertical at the other end. The bottom left quadrant of a circle would be a good model. I can auto-generate as many points as required.
The problem is, I can't do this without all sorts of artefacts.
I haven't tried Bezier fitting; I don't think this would be even close. My understanding is that Bezier is for one-off manually-constructed pretty graphics, and not for real curve-fitting.
That leaves polylines. There are only 2 things I can do with polylines - I can select the line length (in other words, the number of points I auto-generate), and I can disable anti-aliasing (setAttributeNS(null, "shape-rendering", "crisp-edges").
If I generate lots of points, then I get jaggies everywhere, and the result is unusable. It can also look very much like it's oscillating, which makes it appear that I've incorrectly calculated the function. The anti-aliasing doesn't make any difference, since it doesn't operate across point boundaries.
The only solution I've got is to draw fewer points, so that it's obvious that I'm drawing segments. It's no longer smooth, but at least there are no jaggies or oscillation. I draw this with the default anti-aliasing.
Any ideas?
Edit:
It seems like the only answer to this is actually Bezier curve fitting. You have to preprocess to find the parameters of the required segments, and then plot the results. Google comes up with a number of hits on curve fitting with Beziers.
You have the mathematical function, and can therefore generate as many points as you need.
I assume the problem is that because you do not know the output resolution (SVG is device independent) you do not know how many points to generate. Otherwise you could just create a polyline where each line is approximately 1 pixel long.
Fitting your mathematical function to a bezier curve is (probably) not going to get a perfect match - just like a circle cannot be matched perfectly by a cubic bezier curve. And I think the task of fitting your function to a bezier curve would not be trivial (I've never done this).
Could you rather output your mathematical function to a canvas element? Then you could write some javascript code to plot your mathematical function dependant on the output resolution. Similar to how a graphics system renders a Bezier curve.
Do you know how graphics systems render Bezier curves? They approximate the bezier curve with a polyline, and then measure the error difference between the polyline and the bezier curve. If the difference is greater than a certain tolerance - where the tolerance is determined by the output resolution - the bezier is subdivided and the process repeated for each bezier curve. When the difference between beziers and polylines is below the tolerance, the polylines are drawn. http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Computer_graphics
I suppose you want to draw y=f(x) over a certain interval [a,b]
A classical solution is to take N points uniformly distributed over [a,b], to compute f over these points and draw lines (or polynoms).
It of course doesn't work in your case, since y is nearly vertical in certain area. But why don't you take more points in these areas (and less points where the function is nearly horizontal) ?
You can compute the derivative of your function (or approximate this derivative with (f(x+h)-f(x))/h and h small) and determine the step between two successive points with this derivative