pomax / bezierjs Goto Github PK
View Code? Open in Web Editor NEWA nodejs and client-side library for (cubic) Bezier curve work
License: MIT License
A nodejs and client-side library for (cubic) Bezier curve work
License: MIT License
I'm wondering whether you can produce a cubic-to-quadratic conversion function toQuads([error])
. This is extremely useful for producing TTF fonts.
Having spent futile 4 hours trying to use this library in a typescript code base (ES6 module imports), I wonder if anyone has managed, and show how.
Seems like @danmarshall did some work on this, but none seem to work.
There's this typescript fork/port, which I couldn't install at all - it just throws errors when you run npm install
; it is also at 2.1.0 at the moment.
Then, there's the typings definitions, and an npm package for @types/bezier-js, but the devil will take me I only get compilation/run-time errors with these.
Anyone?
If a point is on-or-near-enough the curve, return all t values that yield the specified point.
This will allow things like:
getDistanceforPoint(p):
ts = getTforPoint(p)
ds = (ts: (t) => split(t).length)
yield ds
__normal3: function() { // see http://stackoverflow.com/questions/25453159 var r1 = this.derivative(t), r2 = this.derivative(t+0.01)
param 't' is not defined
This folder is referenced in package.json:
"scripts": {
"test": "npm run build && node test",
"test:manual": "http-server test/manual",
"build": "..."
}
But it isn't present in the repo. Have you renamed it to "autodraw", or simply forgotten?
In our app we've written a small utility that calculates the angle between two lines
export function lla(l1p1, l1p2, l2p1, l2p2) {
let angle1 = Math.atan2(l1p1.y - l1p2.y, l1p1.x - l1p2.x);
let angle2 = Math.atan2(l2p1.y - l2p2.y, l2p1.x - l2p2.x);
return angle1 - angle2;
}
This function is rather simple and can also be used to calculate an angle formed by three points: const angle = lla(A,B,B,C);
. I suggest replacing your implementation of angle by this implementation. I'm ready to open a PR and change code that uses this method if you think this could be useful.
Thank you in advance.
Shouldn't reduce()
in the "cannot reduce" case return the original unreduced curve? Y so destructive?
we can never form a reduction bezier.js:1152:13
TypeError: fcurves[0] is undefined[Learn More] bezier.js:1273:7
Bezier.prototype.outline file:///home/chuck/curve_test_js/bezier.js:1273:7
Bezier.prototype.outlineshapes file:///home/chuck/curve_test_js/bezier.js:1286:17
<anonymous> file:///home/chuck/curve_test_js/outlines.html:375:40
r.Callbacks/i file:///home/chuck/curve_test_js/jquery-3.1.1.min.js:2:27978
r.Callbacks/j.fireWith file:///home/chuck/curve_test_js/jquery-3.1.1.min.js:2:28749
A file:///home/chuck/curve_test_js/jquery-3.1.1.min.js:4:14201
.send/c/< file:///home/chuck/curve_test_js/jquery-3.1.1.min.js:4:16491
we all love npm, but having bower install bezierjs
would be as good.
The value 0xFFFFFFFFFFFFFFFF is used in utils.getminmax but not in utils.findbbox. I believe the same value should be used in both cases.
Am I correct? Should I send a PR?
that should be cleaned up
A function constructs quadratic/cubic/poly bezier curve through points given would be useful.
Nicer if we can specify tangent direction of start and end points.
Hi,
Thanks a lot for your excellent work, I want to know that can I get the base bezier class in C++ format? I remembered that I can get that code some days ago, but now I can not find that code. Can you share me a copy?
Thanks a lot.
Best,
Eric
I'm trying to write a simple library to get SVG paths properties.
I'm using bezierjs for the Bezier curves, and it works ok with lengths, but with the position at certain point I find differences from the getPointAtLength method.
In this image you can see the points that should be the same, marked in different colors. The functions called are like:
pathEl = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 300);
path = pathEl
.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("d","M200,300 Q400,50 600,300");
var curve = new Bezier(200,300,400,50,600,300);
console.info("Bezier-js", curve.get(0.2));
var browserLength = path.node().getTotalLength();
console.info("SVG", path.node().getPointAtLength(0.2 * browserLength));
But the results are different:
Bezier-js { x: 280.00000000000006, y: 220.00000000000003 }
SVG { x: 267.53448486328125, y: 229.83480834960938 }
Am I doing something wrong? In bezierjs the length is given from 0 to 1, and in the SVG function, from 0 to the length of the curve, but the positions should be the same...
More curves with the same problem can be found here.
Change the .length()
function so that it can be called as .length(t)
, setting the limits of integration to [0,t]
rather than the current [0,1]
.
length: function(t, undef) {
t = t===undef? 1 : t;
...
}
This should also make work around a "get t
for distance d
" function easier.
var test_bezier = [0, 1.74, .21, 1.67, .28, 1.32, .28, .86];
var test_line = { p1: { x: -.56, y: .95}, p2: { x: .56, y: .95 } };
var test_curve = new Bezier(test_bezier);
var intersections = test_curve.intersects(test_line);
if(intersections.length === 0) throw new Error('Expected at least one intersection');
The test curve and line above definitely intersect. I've checked and rechecked multiple times, including drawing the points (at various scales) onto a canvas. bezier.js fails to return any intersection points.
I've actually got a surfboard shape comprised of a set of bezier curves, and I'm using bezier.js to calculate around 150 slice points along the length of the board, but the intersections returned are very intermittent. I typically assume my code is at fault before assuming a library is at fault, but that test case above seems pretty clear.
.outline
with any number on the constructed bezierTypeError: p1.split is not a function
new Bezier(102,33, 102,33, 101,129, 132,173).outline(5)
bezier.js:1 Uncaught TypeError: t.split is not a function(…)(anonymous function) @ bezier.js:1p.reduce @ bezier.js:1p.outline @ bezier.js:1(anonymous function) @ VM675:2InjectedScript._evaluateOn @ VM90:878InjectedScript._evaluateAndWrap @ VM90:811InjectedScript.evaluate @ VM90:667
I'm in the process of writing a functional version of the lib, and I believe it would be great to have an .editorconfig and .eslint files to keep code consistent.
I can send a PR that adds those files and fix linting errors in the repo. Do you have an existing config you'd want to point me to, or an preferred config?
Get the point based on the length of the curve or return t based on the length of the curve from t =0
This would allow splitting the curve evenly based on length in x sections of the same line length (not t like in getLUT) and also place items on a curve based on distance from t0
Hello in video games need change the endpoint in some cases, how to ? enhancement
Hi Pomax,
Great work!
I want to use a minified version of your bezierjs and found following little issue.
It's here: https://github.com/Pomax/bezierjs/blob/gh-pages/bezier.js#L487
yi@localhost -> java -jar ../../scripts/compiler.jar --js bezierjs/bezier.js --js_output_file /dev/null --compilation_level SIMPLE_OPTIMIZATIONS
bezierjs/bezier.js:487: ERROR - Parse error. Internet Explorer has a non-standard intepretation of trailing commas. Arrays will have the wrong length and objects will not parse at all.
};
^
1 error(s), 0 warning(s)
In function utils.angle, vectors d1 and d2 should not be normalised between dot and cross product computation. Both products must be calculated either with normalised or non-normalised vectors so atan2 returns the right angle value
(thank you for your great library and giving me a chance to improve it! sorry for the dire-sounding headline on a very minor issue)
Passing multiple arguments doesn't work (as compared to an array of arguments, which does) because line 500-507 iterates coords rather than args. I can fix this with a few lines of changes, or remove it with something like
- var args = (coords && coords.forEach ? coords : arguments);
+ var args = coords;
Any preference which? I'm happy to send a pull request.
I noticed this because Chrome's profiler noted that it doesn't know how to optimize array-like uses of arguments
(see https://code.google.com/p/v8/issues/detail?id=3037 ), which keeps it from optimizing the entire function. Fixing this changed it from the slowest thing in my profile to something more reasonable. The workaround is apparently to iterate through arguments like var args = []; for(var i = 0; i !== arguments.length; i++) { args.push(arguments[i]); }
(as suggested in bevry/taskgroup#12 (comment) ). Less importantly, I'd probably check for multiple arguments by checking arguments.length
rather than coords && coords.forEach
.
based on #12 and the bezierinfo article.
The iteration should continue until it hits error resolution, after which any value is acceptable, and so rather than clusters we can use a final linear intersection check to find singles, instead.
Is there a way, given a point on the curve, to get the t value for that point? For non-degenerate quadratic curves it should be unique.
First of all thanks for that library, saved me a lot of time :-).
Apparently the reduce function fails as soon as a control point and the start/ end are the same. I did some further investigations and found out that this is connected to the extrema function yielding 0 two times and therefore "confusing" the split function (which seems to be broken for input 0,0 as well).
Minimal example below:
var curve = new Bezier(100,25 , 100,25 , 110,100 , 150,195);
var draw = function() {
drawSkeleton(curve);
drawCurve(curve);
setColor("red");
curve.extrema().values.forEach(function(t) {
drawCircle(curve.get(t),3);
console.log(t);
});
curve.reduce().forEach( (curve) => {
drawCurve(curve);
} );
}
When the roots function is called with 4 points (order == 3) it is possible that the variable 'd' will assume a ZERO value causing failure when it is used as a divisor. I believe this is the cubic term being used to 'normalize' the other terms for "Cardan's method". The cubic term being zero seems to indicate that, though having 4 points, the curve is actually quadratic in nature. This would seem rather common. Finding the roots using a quadratic method, in this case, seems to easily solve the problem.
if the point is on-or-near-enough the curve, determine the distance (on [0,1]) traveled along the curve to get to that point.
note: only possible for quadratic and cubic curves.
cannot be blank
I don't know if this is the right place to ask, or whether you've already addressed this problem here or elsewhere.
Anyway, it'd be nice if you could explore ways of optimizing bezier paths to reduce the number of curve segments while keeping the overall shape.
Any insight?
Thanks,
Cosimo
A picture is worth a thousand words:
What I'm actually trying to do is create a uniform inset for a larger closed shape, consisting of a number of bezier curves. Obviously there will be issues if the shape has any intersections on its path, but I don't care about that case too much. Can you recommend any information that would allow me to calculate a continuous non-self-intersecting offset path derived from a continuous set of bezier curves defining a closed shape?
This isn't an issue, but more of a question. Is there an easy way to use this library to find the t value that computes the point halfway along a bezier curve path? (i.e. the t value that splits the curve into two equal sized sub-curves)?
Hi @Pomax ,
I'm trying to understand the type of object returned by the split method of the Bezier class.
It seems that it can return either:
I'm guessing that the subsplit might only be for internal use by split itself?
See: http://www.visgraf.impa.br/sibgrapi96/trabs/pdf/a14.pdf
I might actually submit a PR for this one.
Would be great to expose boolean methods (AND, OR, NOT, XOR, ...) like you describe in http://pomax.github.io/bezierinfo/.
Hi Mike,
What is a good strategy for detecting intersection of a Bezier curve and a circle? Would you recommend using 4 curves, each approximating a quarter circle?
Possible implementation for cubic case.
Can probably be improved (possibly unstable for as curveP0T approaches 1)
likeCurve(curve) {
return curve.constructor == BezierCurve
&& this.p0.like(curve.p0)
&& this.p1.like(curve.p1)
&& this.p2.like(curve.p2)
&& this.p3.like(curve.p3)
}
/**
* Checks if this curve is colinear to the passed curve, i.e.
* for every t:number there exists a s:number with this.at(t) = curve.at(s)
* @param {BezierCurve} curve
* @returns {boolean}
*/
isColinearTo(curve) {
// first, find out where/if curve.p0 and curve.p3 are on this
// then split this at curve.p0 --> curve.p3 to compare points p1 and p2
var curveP0T, curveP3T
// assign in if condition to exploit short-circuit
if (isNaN(curveP0T = this.pointLambda(curve.p0)) || isNaN(curveP3T = this.pointLambda(curve.p3))) {
return false
}
var thisSplit
if (NLA.equals(1, curveP0T)) {
// this.split(curveP0T).right is degenerate in this case, so we need to handle it separately
// this.split(curveP3T): 0 --> curveP3T --> 1
// .right: curveP3T --> 1
// .reversed(): 1 --> curveP3T
thisSplit = this.split(curveP3T).right.reversed()
} else {
// curveP3T describes the point on this
// adjust it so it describes the same point on this.split(curveP0T).right
// this: 0 p0t p3t 1
// | | | |
// this.split(curveP0T).right: 0 p3tad 1
var curveP3Tadjusted = (curveP3T - curveP0T) / (1 - curveP0T)
thisSplit = this.split(curveP0T).right.split(curveP3Tadjusted).left
}
return curve.likeCurve(thisSplit)
}
/**
* @returns {BezierCurve}
*/
reversed() {
return new BezierCurve(this.p3, this.p2, this.p1, this.p0)
}
Given this code:
var pts = [ 120, 79.63967819396663, 173.33333333333331, 118.72659522329167, 226.66666666666669, 109.84371034788978, 272, 85.57546366477419 ],
curve = new Bezier(pts),
ocurve = curve.offset(20);
draw_curve(curve, "line");
ocurve.forEach(function(c) {
draw_curve(c, "offsetpath");
});
I get a properly offset line below curve
, but also a bit of the curve offset by the same amount but above curve
. Here is a demo.
Is this a bug, or am I misunderstanding how this is an expected behavior?
ex.)
function example(){
var bezier = new Bezier( [ {x:1031,y:325},{x:1031,y:500},{x:1271,y:530},{x:1271,y:375} ] );
return bezier.toSVG();
}
Output:
"M 1031 325 C 1031 5001271 5301271 375"
Expected Output:
"M1031 325C1031 500 1271 530 1271 375"
For linear traversal along the curve, or just a way to query the curve for its distance reparameterisation.
demonstrator curve:
p1: 213,203
p2: 35,200
p3: 220,260
p4: 220,40
Those props are initialized in the constructor a used in a few places throughout the code. I haven't been able to figure out what is their purpose.
Could you explain this to me?
Thank you in advance :-)
var curve = new Bezier(330,592,330,557,315,522,315,485);
curve.offset(20); // it causes TypeError
I found it was generated while calling t.split within .reduce function of curve.
I suggest exposing it either as Bezier.Poly or Bezier.PolyBezier (I vote for the conciseness of Bezier.Poly).
Should you agree, I'll gladly send a PR :-)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.