Built with blockbuilder.org
Created
August 29, 2017 00:57
-
-
Save mforando/57e32915606a46ae18eeb58a3e1ea0e2 to your computer and use it in GitHub Desktop.
Bezier Curve Example
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: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