Skip to content

Instantly share code, notes, and snippets.

@mforando
Created August 31, 2017 23:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mforando/a4e6a81341cb30f4fb41fa7ba6ab265e to your computer and use it in GitHub Desktop.
Save mforando/a4e6a81341cb30f4fb41fa7ba6ab265e to your computer and use it in GitHub Desktop.
Bezier
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:2;
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([x=coords[0],y=coords[1]]);
console.log(ControlPoints)
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 5;})
line.attr("stroke-dashoffset", line.node().getTotalLength());
update()
}
function update() {
/*
lineHead.interrupt().transition()
.delay(0)
.duration(ControlPoints.length*1000)
.ease(d3.easeLinear)
.attrTween("cx",translateAlong(line.node(),"x"))
.attrTween("cy",translateAlong(line.node(),"y"))
*/
line.attr("stroke-dashoffset", line.node().getTotalLength());
line.transition()
.ease(d3.easeLinear)
.delay(0)
.duration(ControlPoints.length*1000)
.attrTween("cx",translateAlong(line.node(),"x"))
.attr("stroke-dashoffset",0)
.on('end',update);
;}
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.00; 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) {
//console.log(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
function interpolate(interpolated, t) {
var tempArray =[];
tempArray=interpolated;
interpolated = [];
//if only 2 and second iteration, connect points
if (tempArray.length ==2 && ControlPoints.length>2) {
// svg.append("g").attr("class","connector").append("path").attr("d",lineGenerator([tempArray[0],tempArray[1]])).attr("stroke","blue").attr("opacity",.3)
;}
if (tempArray.length != ControlPoints.length) {
for (i=0;i<tempArray.length-1;i++)
{
z = i + 1
coords = tempArray[i]
coords1 = tempArray[z]
//draw connecting lines for each set of coordinates in the arrays
svg.append("g").attr("class","connector").append("circle").attr("r",2).attr("cx",coords.x).attr("cy",coords.y).attr("opacity",.1).attr("stroke","black").attr("stroke-width",.5);
svg.append("g").attr("class","connector").append("circle").attr("r",2).attr("cx",coords1.x).attr("cy",coords1.y).attr("opacity",.1).attr("stroke","black").attr("stroke-width",.5);
svg.append("g").attr("class","connector").append("path").attr("d",lineGenerator([tempArray[i],tempArray[i+1]])).attr("stroke-width",.5).attr("stroke","blue").attr("opacity",.75)
;}
}
if(tempArray.length>=2 && ControlPoints.length>=3){
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")
if (tempArray.length>=2)
{tempLine2 = svg.append("g").attr("class","connector").append("path").attr("d",lineGenerator([tempArray[j],tempArray[i]])).attr("stroke","none")
tempPt = tempLine.node().getPointAtLength(t*tempLine.node().getTotalLength());
tempPt2 = tempLine2.node().getPointAtLength(t*tempLine2.node().getTotalLength());
svg.append("g").attr("class","connector").append("circle").attr("r",2).attr("cx",tempPt.x).attr("cy",tempPt.y);
svg.append("g").attr("class","connector").append("circle").attr("r",2).attr("cx",tempPt2.x).attr("cy",tempPt2.y);
connectLine = svg.append("g").attr("class","connector").append("path").attr("stroke","blue").attr("stroke-width",.5).attr("opacity",.75).attr("d",lineGenerator([[tempPt.x,tempPt.y],[tempPt2.x,tempPt2.y]]))
midpt = connectLine.node().getPointAtLength(t*connectLine.node().getTotalLength())
interpolated.push([midpt.x,midpt.y]);
;}
;}
if (levels >= 1) {
levels = levels-1
n = t
return interpolate(interpolated,n);}
;}
};
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