Created
October 20, 2016 16:42
-
-
Save shshaw/7eec087ea22442f1444c543ded941bf9 to your computer and use it in GitHub Desktop.
Hose curve generation
This file contains hidden or 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
<svg id="svg"> | |
<path id="path" d="" /></path> | |
</svg> |
This file contains hidden or 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
console.clear(); | |
var canvas = document.createElement('canvas'), | |
ctx = canvas.getContext('2d'); | |
canvas.width = window.innerWidth; | |
canvas.height = window.innerHeight; | |
document.body.appendChild(canvas); | |
/*////////////////////////////////////////*/ | |
// http://stackoverflow.com/questions/16227300/how-to-draw-bezier-curves-with-native-javascript-code-without-ctx-beziercurveto | |
function bezier(p0, p1, p2, p3){ | |
var cX = 3 * (p1.x - p0.x), | |
bX = 3 * (p2.x - p1.x) - cX, | |
aX = p3.x - p0.x - cX - bX; | |
var cY = 3 * (p1.y - p0.y), | |
bY = 3 * (p2.y - p1.y) - cY, | |
aY = p3.y - p0.y - cY - bY; | |
return function(t){ | |
var x = (aX * Math.pow(t, 3)) + (bX * Math.pow(t, 2)) + (cX * t) + p0.x; | |
var y = (aY * Math.pow(t, 3)) + (bY * Math.pow(t, 2)) + (cY * t) + p0.y; | |
return {x: x, y: y}; | |
} | |
}; | |
function getCurve(points, accuracy){ | |
accuracy = accuracy || 0.033; | |
var b = bezier(points[0], points[1], points[2], points[3]), | |
curve = []; | |
for (var i = 0; i < 1; i += accuracy ){ | |
curve.push(b(i)); | |
} | |
curve.push(b(1)); | |
return curve; | |
} | |
/*////////////////////////////////////////*/ | |
function drawPoint(point,fill){ | |
ctx[ fill ? 'fill' : 'stroke' ](); | |
} | |
/*////////////////////////////////////////*/ | |
function Hose(start, end) { | |
if (!(this instanceof Hose)) { return new Hose(start, end); } | |
if ( arguments.length === 1 ){ | |
for (var key in start) { | |
if ( start.hasOwnProperty(key) ) { this[key] = start[key]; } | |
} | |
} else { | |
this.start = start; | |
this.end = end; | |
} | |
this.length = this.getLength(); | |
this.update(); | |
return this; | |
} | |
Hose.prototype = { | |
constructor: Hose, | |
// 0 = sharp, 1 = rounded | |
straightness: 0.7, | |
offset: 0.5, | |
length: null, | |
// Increase the length of the rope | |
extend: 0, | |
invertBend: true, | |
accuracy: 0.02, | |
pointCount: 100, | |
update: function(){ | |
var length = this.getLength(); | |
var diff = this.length - length; | |
diff = ( diff > 0 ? diff : 0 ); | |
diff = ( this.invertBend ? -diff : diff ) * (this.extend + 1); | |
var percent = length / this.length; | |
var angle = Math.atan2(this.dy, this.dx), | |
sin = Math.sin(-angle), | |
cos = Math.cos(-angle), | |
distanceX = Math.min( sin * diff , this.length * 0.6), | |
distanceY = Math.min( cos * diff , this.length * 0.6); | |
var midX = this.dx * this.straightness * percent, | |
// - ( this.dy * this.bend ),// * this.offset, | |
midY = this.dy * this.straightness * percent; | |
// - ( this.dx * this.bend );// * this.offset; | |
this.cp1 = this.cp1 || {}; | |
this.cp1.x = this.start.x + (distanceX + midX);///percent ; | |
this.cp1.y = this.start.y + (distanceY + midY);///percent; | |
this.cp2 = this.cp2 || {}; | |
this.cp2.x = this.end.x + (distanceX - midX);// / percent; | |
this.cp2.y = this.end.y + (distanceY - midY);// / percent; | |
this.controls = [this.start, this.cp1, this.cp2, this.end]; | |
this.draw(); | |
}, | |
getLength: function(){ | |
return ( this.start && this.end ? Math.sqrt(( this.dx * this.dx ) + ( this.dy * this.dy )) : null ); | |
}, | |
draw: function(){ | |
// var c = this.points; | |
// ctx.beginPath(); | |
// ctx.moveTo( c[0].x, c[0].y); | |
// for (var i = 0 ; i < c.length; i ++){ ctx.lineTo(c[i].x, c[i].y); } | |
// ctx.stroke(); | |
this.renderCanvas(ctx); | |
ctx.lineWidth = 1; | |
ctx.fillStyle = ctx.strokeStyle = 'red'; | |
for (var i = 0; i < this.controls.length; i++ ){ | |
ctx.beginPath(); | |
ctx.arc(this.controls[i].x, this.controls[i].y, 4, 0, 2 * Math.PI); | |
ctx[ i === 1 || i === 2 ? 'fill' : 'stroke' ](); | |
} | |
}, | |
renderCanvas: function(ctx){ | |
ctx.beginPath(); | |
ctx.fillStyle = ctx.strokeStyle = 'black'; | |
ctx.lineWidth = 25; | |
ctx.moveTo( this.start.x, this.start.y); | |
ctx.bezierCurveTo( this.cp1.x, this.cp1.y, this.cp2.x, this.cp2.y, this.end.x, this.end.y); | |
ctx.stroke(); | |
}, | |
renderSVG: function(){ | |
return ['M', this.start.x, this.start.y, 'C', this.cp1.x, this.cp1.y, this.cp2.x, this.cp2.y, this.end.x, this.end.y].join(' '); | |
} | |
}; | |
var radToDeg = 180 / Math.PI; // rads to degs, range (-180, 180) | |
Object.defineProperties(Hose.prototype, { | |
'dx': { get: function(){ return this.end.x - this.start.x; } }, | |
'dy': { get: function(){ return this.end.y - this.start.y; } }, | |
'points': { get: function(){ return getCurve(this.controls, 1 / ( this.pointCount - 1) ); } }, | |
// 'start': { | |
// set: function(val){ | |
// this.s = val; | |
// this.length = this.getLength(); | |
// } | |
// }, | |
// 'end': { | |
// set: function(val){ | |
// this. = val; | |
// this.length = this.getLength(); | |
// } | |
// } | |
// 'length': { | |
// get: function(){ | |
// return Math.sqrt(( this.dx * this.dx ) + ( this.dy * this.dy )); | |
// } | |
// }, | |
// 'midpoint': { | |
// get: function(){ | |
// return { | |
// x: ( this.start.x + this.end.x ) / 2, | |
// y: ( this.start.y + this.end.y ) / 2 | |
// } | |
// } | |
// } | |
}); | |
var centerY = Math.round( window.innerHeight / 2 ); | |
// var r = new Hose({ x: 100, y: centerY }, { x: 500, y: centerY }); | |
// //r.update(); | |
var offset = 80; | |
var length = 300; | |
var leg1 = new Hose({ | |
invertBend: true, | |
start: { x: 100, y: centerY - length/2 }, | |
end: { x: 100, y: centerY + (length/2) } | |
}); | |
var leg2 = new Hose({ | |
invertBend: true, | |
start: { x: 100 + offset, y: centerY - (length/2.1) }, | |
end: { x: 100 + offset, y: centerY + (length/2.1) } | |
}); | |
console.log(leg1.points.length); | |
/*////////////////////////////////////////*/ | |
// TweenMax.fromTo(r, 2,{ | |
// extrude: 0.5, | |
// bend: -0.25, | |
// },{ | |
// extrude: -0.5, | |
// bend: 0.5, | |
// repeat: -1, | |
// yoyo: true, | |
// onUpdate: r.update, | |
// onUpdateScope: r, | |
// repeatDelay: 3, | |
// ease: Elastic.easeInOut,//Linear.easeNone,//SteppedEase.config(5) | |
// }); | |
/*////////////////////////////////////////*/ | |
function update(e){ | |
if ( mousedown ) { | |
leg1.start.x = e.clientX; | |
leg1.start.y = e.clientY; | |
leg2.start.x = e.clientX + offset; | |
leg2.start.y = e.clientY; | |
} | |
} | |
var mousedown = false; | |
canvas.addEventListener('mousedown',function(e){ | |
mousedown = true; | |
update(e); | |
}); | |
canvas.addEventListener('mousemove',update); | |
canvas.addEventListener('mouseup',function(e){ | |
update(e); | |
mousedown = false; | |
}); | |
/*////////////////////////////////////////*/ | |
var gui = new dat.GUI(); | |
// var myType = gui.add(myOptions, 'type', [ 'random', 'unify', 'reverse', 'vary', 'compact' ]), | |
//length = glui.add(r, 'length', -500, 500).step(10), | |
//extrude = gui.add(r, 'extrude', -2, 2).step(0.1), | |
straightness = gui.add(leg1, 'straightness', -1, 1).step(0.1); | |
//offset = gui.add(r, 'offset', -1, 1).step(0.1); | |
var svg = document.getElementById('svg'); | |
var path = document.getElementById('path'); | |
svg.setAttribute('width', window.innerWidth); | |
svg.setAttribute('height', window.innerHeight); | |
function draw(){ | |
requestAnimationFrame(draw); | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
leg2.straightness = leg1.straightness; | |
leg2.update(); | |
leg1.update(); | |
path.setAttribute('d',leg1.renderSVG()); | |
} | |
requestAnimationFrame(draw); |
This file contains hidden or 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
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.1/dat.gui.min.js"></script> |
This file contains hidden or 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
body { | |
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' width=%2210%22 height=%2210%22%3E%0A %3Crect width=%2210%22 height=%2210%22 stroke=%22%23EEE%22 fill=%22none%22 %2F%3E%0A%3C%2Fsvg%3E%0A"); | |
} | |
canvas, svg { | |
position: absolute; | |
top: 0; | |
left: 0; | |
} | |
svg { pointer-events: none; stroke: #F00; stroke-width: 2; z-index: 2; fill: none; } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment