A Pen by Lingjia Liu on CodePen.
Last active
July 16, 2019 23:47
-
-
Save mutoo/f9f4e66008804c816f3b54fb16e4a6a2 to your computer and use it in GitHub Desktop.
A closed uniform BSpline curve
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
| <div id="app"> | |
| <div class="container" ref="container"></div> | |
| </div> |
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
| const INITIAL_POINTS = [ | |
| [68, 83], | |
| [32, 320], | |
| [272, 375], | |
| [396, 22], | |
| [563, 72], | |
| [439, 317]]; | |
| // The uniform bspline matrix | |
| // http://graphics.cs.ucdavis.edu/~joy/ecs178/Unit-7-Notes/MatrixBSpline.pdf | |
| const UNIFORM_BSPLINE_MATRIX = math.matrix([ | |
| [1 / 6, 4 / 6, 1 / 6, 0], | |
| [-3 / 6, 0, 3 / 6, 0], | |
| [3 / 6, -6 / 6, 3 / 6, 0], | |
| [-1 / 6, 3 / 6, -3 / 6, 1 / 6], | |
| ]); | |
| let STEP_PER_SEGMENT = 6; | |
| new Vue({ | |
| el: '#app', | |
| mounted() { | |
| this.setup(); | |
| this.createHandleDef(); | |
| let updateStage = () => { | |
| this.stage.clear(); | |
| if (this.points.length < 4) return; | |
| let points = this.points.map((p) => { | |
| return [p.x(), p.y()]; | |
| }); | |
| points.push(points[0]); // close the control polygon | |
| this.stage.polyline(points).fill('none').stroke({width: 1}); | |
| points.push(points[1]); // to close the b spline curve | |
| points.push(points[2]); // requires more points | |
| // reference: https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve-closed.html | |
| let i = 4; | |
| let controlPoints = points.slice(0, i); | |
| do { | |
| let line = []; | |
| let controlMatrix = math.multiply(UNIFORM_BSPLINE_MATRIX, | |
| controlPoints); | |
| for (let i = 0; i <= 1; i += 1 / STEP_PER_SEGMENT) { | |
| let ts = [1, i, i * i, i * i * i]; | |
| let p = math.multiply(ts, controlMatrix); | |
| line.push(p.toArray()); | |
| } | |
| this.stage.polyline(line). | |
| fill('none'). | |
| stroke({width: 2}). | |
| addClass(i % 2 ? 'segment--a' : 'segment--b'); | |
| controlPoints.shift(); | |
| controlPoints.push(points[i]); | |
| } while (i++ < points.length); | |
| }; | |
| this.points = INITIAL_POINTS.map((p) => { | |
| return this.createHandle(p[0], p[1], updateStage); | |
| }); | |
| updateStage(); | |
| }, | |
| methods: { | |
| setup() { | |
| this.canvas = SVG(this.$refs['container']).size(640, 480); | |
| this.canvas.rect(640, 480).fill('none').stroke({width: 1}); | |
| this.stage = this.canvas.group(); | |
| }, | |
| createHandleDef() { | |
| let defs = this.canvas.defs(); | |
| this.handleDef = defs.group().addClass('handle'); | |
| // make it easy to move on touch screen; | |
| this.handleDef.rect(40, 40).fill('transparent').translate(-20, -20); | |
| this.handleDef.rect(10, 10).fill('black').translate(-5, -5); | |
| }, | |
| createHandle(x, y, callback) { | |
| let handle = this.canvas.use(this.handleDef).x(x).y(y); | |
| let hammer = new Hammer(handle.node); | |
| let handleX, handleY; | |
| hammer.on('panstart', (ev) => { | |
| handleX = handle.x(); | |
| handleY = handle.y(); | |
| }); | |
| hammer.on('panmove', (ev) => { | |
| handle.x(handleX + ev.deltaX); | |
| handle.y(handleY + ev.deltaY); | |
| callback(); | |
| }); | |
| hammer.on('panend', (ev) => { | |
| }); | |
| return handle; | |
| }, | |
| }, | |
| }); |
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="//unpkg.com/svg.js"></script> | |
| <script src="//unpkg.com/hammerjs"></script> | |
| <script src="//unpkg.com/mathjs/dist/math.js"></script> | |
| <script src="//unpkg.com/vue"></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
| .handle { | |
| cursor: move; | |
| } | |
| .segment--a { | |
| stroke: red; | |
| } | |
| .segment--b { | |
| stroke: blue; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment