From c7024336d1ea81338ed802f6377d75ab0811fcc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergiusz=20Baza=C5=84ski?= Date: Mon, 25 Feb 2013 18:26:09 +0100 Subject: [PATCH] Use bezier curves to draw cables. --- main.js | 121 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/main.js b/main.js index 2ad0334..097f45a 100644 --- a/main.js +++ b/main.js @@ -15,6 +15,7 @@ var Verlet = function(Width, Height) this.AddConstraint = function(Constraint) { this.Constraints.push(Constraint); + return Constraint; } this.Update = function(TimeDelta) @@ -139,28 +140,75 @@ var Constraint = function(Mass1, Mass2) } } +var Cable = function(Verlet, X1, Y1, X2, Y2, Steps) +{ + Steps = Steps || 10; + var StepX = (X2 - X1) / Steps; + var StepY = (Y2 - Y1) / Steps; + + this.Masses = []; + var LastMass = v.AddMass(new Mass(X1, Y1, 0.5)); + this.Masses.push(LastMass); + LastMass.Active = false; + + for (var i = 1; i <= Steps; i++) + { + var NewMass = v.AddMass(new Mass(X1 + StepX * i, Y1 + StepY * i, 1)); + this.Masses.push(NewMass); + v.AddConstraint(new Constraint(LastMass, NewMass)); + LastMass = NewMass; + } + + this.BinomialCache = {} + this.FastBinomial = function(n, k) + { + var key = n + ".." + k; + if (key in this.BinomialCache) + return this.BinomialCache[key]; + + function Factorial(num) + { + var rval=1; + for (var i = 2; i <= num; i++) + rval = rval * i; + return rval; + } + return (Factorial(n) / (Factorial(k) * Factorial(n - k))); + } + this.Bezier = function(t) + { + var Points = this.Masses.length - 1; + var ValueX = 0; + var ValueY = 0; + for (var i = 0; i <= Points; i++) + { + var Coef = this.FastBinomial(Points, i) * Math.pow(1 -t, Points - i) * Math.pow(t, i); + ValueX += Coef * this.Masses[i].X; + ValueY += Coef * this.Masses[i].Y; + } + + return {X: ValueX, Y: ValueY}; + } + + this.Render = function(context) + { + context.strokeStyle = '#FFFFFF'; + context.beginPath(); + var First = this.Bezier(0); + context.moveTo(First.X, First.Y); + for (var T = 1; T < 100; T++) + { + var t = T / 100; + var Next = this.Bezier(t); + context.lineTo(Next.X, Next.Y); + } + context.stroke(); + } +} + var v = new Verlet(800, 600); -var m1 = v.AddMass(new Mass( 5, 5, 1)); -var m2 = v.AddMass(new Mass(20, 5, 1)); -var m3 = v.AddMass(new Mass(35, 5, 1)); -var m4 = v.AddMass(new Mass(50, 5, 1)); -var m5 = v.AddMass(new Mass(65, 5, 1)); -var m6 = v.AddMass(new Mass(80, 5, 1)); -m6.Active = false; +var cable = new Cable(v, 5, 5, 500, 5); -var t1 = v.AddMass(new Mass(20, 15, 2)); -var t2 = v.AddMass(new Mass(0, 45, 2)); -var t3 = v.AddMass(new Mass(40, 45, 2)); -v.AddConstraint(new Constraint(m1, m2)); -v.AddConstraint(new Constraint(m2, m3)); -v.AddConstraint(new Constraint(m3, m4)); -v.AddConstraint(new Constraint(m4, m5)); -v.AddConstraint(new Constraint(m5, m6)); - -v.AddConstraint(new Constraint(t1, t2)); -v.AddConstraint(new Constraint(t1, t3)); -v.AddConstraint(new Constraint(t3, t2)); -v.AddConstraint(new Constraint(m1, t1)); var c = document.getElementById("main"); var ctx = c.getContext('2d'); @@ -177,30 +225,21 @@ setInterval(function() v.Update(1/50); + cable.Render(ctx); ctx.fillStyle = '#FFFFFF'; - for (k in v.Masses) - { - var m = v.Masses[k]; - if (typeof(m) == "function") - continue; - ctx.beginPath(); - ctx.arc(m.X, m.Y, 5, 0, Math.PI*2, true); - ctx.closePath(); - ctx.fill(); - } - ctx.strokeStyle = '#FFFFFF'; - for (k in v.Constraints) - { - var c = v.Constraints[k]; - if (typeof(c) == "function") - continue; + var First = v.Masses[0]; + var Last = v.Masses[v.Masses.length - 1]; + + ctx.beginPath(); + ctx.arc(First.X, First.Y, 5, 0, Math.PI*2, true); + ctx.closePath(); + ctx.fill(); - ctx.beginPath(); - ctx.moveTo(c.Mass1.X, c.Mass1.Y); - ctx.lineTo(c.Mass2.X, c.Mass2.Y); - ctx.stroke(); - } + ctx.beginPath(); + ctx.arc(Last.X, Last.Y, 5, 0, Math.PI*2, true); + ctx.closePath(); + ctx.fill(); }, 1000/50);