Built with blockbuilder.org
Created
August 31, 2017 23:27
-
-
Save mforando/a4e6a81341cb30f4fb41fa7ba6ab265e to your computer and use it in GitHub Desktop.
Bezier
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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