Skip to content

Instantly share code, notes, and snippets.

@mforando
Created August 29, 2017 00:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mforando/57e32915606a46ae18eeb58a3e1ea0e2 to your computer and use it in GitHub Desktop.
Save mforando/57e32915606a46ae18eeb58a3e1ea0e2 to your computer and use it in GitHub Desktop.
Bezier Curve Example
license: mit
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
</body>
</html>
<style>
text {font-family:"Franklin Gothic Medium";
font-size:14px;}
path {fill:none;};
circle {r:7;
stroke:black;
stroke-width:1.5;}
.controlPoints {
fill: rgb(0,103,79);
r:7;
stroke:black;
stroke-width:1.5;
}
.controlConnect {
stroke: rgb(199,198,197);
stroke-width: 1;
fill: none;
opacity:.7;
}
.bezierPath {
stroke:rgb(196,18,48);
stroke-width:1.5;
stroke: red;
}
.tLabel {
font-family:"Franklin Gothic Medium";
font-size:30px;}
.lineHead
{fill: red;
r:4;}
</style>
<body>
<script>
var t1 =0;
var lineGenerator = d3.line();
var w = 800,
h = 500,
padding = 30,
radius = 4,
ControlPoints = [],
xCoords=[]
yCoords=[];
var bezierLine = d3.line().x(function(d) { return d[0]; }).y(function(d) { return d[1]; }).curve(d3.curveBundle.beta(0));
var straightLine = d3.line().x(function(d) { return d[0]; }).y(function(d) { return d[1]; }).curve(d3.curveLinear);
var svg = d3.select("body").append("svg").attr("width", w + 2 * padding).attr("height", h + 2 * padding).on('click', drawCircle).append("g").attr("class","bezier");
var xForm = d3.select("body").append("div").attr("class","xForm").style("font-family","Franklin Gothic Medium").style("font-size",14);
var yForm = d3.select("body").append("div").attr("class","yForm").style("font-family","Franklin Gothic Medium").style("font-size",14);
var path = svg.append("g").attr("class","path");
var tLabel = svg.append("g")
.append("text")
.attr("class","tLabel")
.attr("x",w-padding)
.attr("y",h-padding)
.text("t=0");
function drawCircle(){
coords = d3.mouse(this);
point = svg.append("g").attr("id","ct"+(ControlPoints.length+1));
ControlPoints.push(coords);
xCoords.push(coords[0]);
yCoords.push(coords[1]);
if (ControlPoints.length==1){
lineHead = svg.append("g").append("circle")
.attr("class","lineHead")
.attr("cx",coords[0])
.attr("cy",coords[1]);
;}
path.selectAll("path").remove();
path.append("path")
.attr("class","controlConnect")
.attr("d", straightLine(ControlPoints));
point.append("circle")
.attr("class","controlPoints")
.attr("cx",coords[0])
.attr("cy",coords[1]);
point.append("text")
.attr("x",coords[0]+10)
.attr("y",coords[1])
.text("P"+ControlPoints.length);
point.append("text")
.attr("x",coords[0]+10)
.attr("y",coords[1]+15)
.style("font-family","Trebuchet MS")
.style("font-size",10)
.text("("+coords+")");
line = path.append("path")
.attr("class","bezierPath")
.attr("d", lineGenerator(getBezierPath()))
.attr("stroke-dasharray",function(d){ return this.getTotalLength() + " " + this.getTotalLength() ;})
.attr("stroke-dashoffset", function(d){ return this.getTotalLength();})
.interrupt()
.transition()
.delay(100)
.duration(ControlPoints.length*900)
.ease(d3.easeLinear)
.attr("stroke-dashoffset",0);
lineHead.interrupt().transition()
.delay(100)
.duration(ControlPoints.length*900)
.ease(d3.easeLinear)
.attrTween("cx",translateAlong(line.node(),"x"))
.attrTween("cy",translateAlong(line.node(),"y"))
}
function Bezier(n,m){
xsum = 0;
ysum = 0;
coeff = 0;
xformula = "";
yformula = "";
z = n
n = n - 1
for(k=0; k<z; k++){
var h = "";
if (k==n){h=k;}
else {h=k+1;}
coeff = binomial(z-1,k);
xsum += +xCoords[k]*coeff* Math.pow((1-m),(n-k))*Math.pow(m,k)
ysum += +yCoords[k]*coeff* Math.pow((1-m),(n-k))*Math.pow(m,k)
if (k==0){
if (n==k) {xformula += " + " + xCoords[k]+" x "+coeff+" * (1-"+m.toFixed(2)+")^"+(n-k);}
else {xformula += " + " + xCoords[k]+" x "+coeff;};}
else {
if (n==k) {xformula += " + " + xCoords[k]+" x "+coeff+" * "+m.toFixed(2)+"^"+k;}
else {xformula += " + " + xCoords[k]+" x "+coeff+" * (1-"+m.toFixed(2)+")^"+(n-k)+" * "+m.toFixed(2)+"^"+k;};}
if (k==0){
if (n==k) {yformula += " + " + yCoords[k]+" x "+coeff+" * (1-"+m.toFixed(2)+")^"+(n-k);}
else {yformula += " + " + yCoords[k]+" x "+coeff;};}
else {
if (n==k) {yformula += " + " + yCoords[k]+" x "+coeff+" * "+m.toFixed(2)+"^"+k;}
else {yformula += " + " + yCoords[k]+" x "+coeff+" * (1-"+m.toFixed(2)+")^"+(n-k)+" * "+m.toFixed(2)+"^"+k;};}
}
d3.selectAll(".tLabel").text("t="+m);
xForm.text(xsum.toFixed(0)+" ="+xformula);
yForm.text(ysum.toFixed(0)+" ="+yformula);
;}
function binomial(n, k) {
if ((typeof n !== 'number') || (typeof k !== 'number'))
return false;
var coeff = 1;
for (var x = n-k+1; x <= n; x++) coeff *= x;
for (x = 1; x <= k; x++) coeff /= x;
return coeff;
}
function getBezierPath() {
var x0 = xCoords[0],
y0 = yCoords[0],
pathData = [];
for (f=0; f<=1.01; f+=.01){
Bezier(ControlPoints.length,f);
pathData.push([xsum,ysum]);
}
return pathData;
;}
function translateAlong(linePath, coor) {
var l = linePath.getTotalLength();
return function(d, i, a) {
return function(t) {
t1 = t
drawLines(t)
Bezier(ControlPoints.length,t);
var p = [];
p = linePath.getPointAtLength((t) * l);
if (coor == "x") {return p.x;}
else {return p.y;};
};
};
}
var arrays = [];
var interpolated = [];
//create a recursive function to loop through control point levels until all lines are drawn
//i = current level
var interpolate = function(interpolated, t) {
var tempArray=[];
tempArray = interpolated;
console.log(tempArray)
if(tempArray.length>=2 && ControlPoints.length>=3){
interpolated=[];
for (i=2;i<tempArray.length;i++) {
j = i - 1;
k = i - 2;
tempLine = svg.append("g").attr("class","connector").append("path").attr("d",lineGenerator([tempArray[k],tempArray[j]])).attr("stroke","none");
tempLine2 = svg.append("g").attr("class","connector").append("path").attr("d",lineGenerator([tempArray[j],tempArray[i]])).attr("stroke","none");
tempInter = tempLine.node().getPointAtLength(t*tempLine.node().getTotalLength());
tempInter2 = tempLine2.node().getPointAtLength(t*tempLine2.node().getTotalLength());
connectLine = svg.append("g").attr("class","connector").append("path").attr("stroke","blue").attr("opacity",.3).attr("d",lineGenerator([[tempInter.x,tempInter.y],[tempInter2.x,tempInter2.y]]))
interpolated.push(connectLine.node().getPointAtLength(t*connectLine.node().getTotalLength()));
;}
i = 0;
;}
if (levels > 1) {
levels = levels-1
newSet = interpolated
// console.log(newSet)
return interpolate(newSet,t);
}
};
function drawLines(t){
levels = ControlPoints.length-2;
svg.selectAll(".connector").remove();
interpolate(ControlPoints, t);
;}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment