Skip to content
Snippets Groups Projects
arrowonpath.js 3.67 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    // from https://github.com/d3/d3-shape/blob/master/src/curve/natural.js
    function controlPoints(x) {
        var i,
            n = x.length - 1,
            m,
            a = new Array(n),
            b = new Array(n),
            r = new Array(n);
        a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];
        for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];
        a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];
        for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];
        a[n - 1] = r[n - 1] / b[n - 1];
        for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];
        b[n - 1] = (x[n] + a[n - 1]) / 2;
        for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];
        return [a, b];
    }
    
    
    export default class ArrowOnPath {
        constructor(path, start, end, parts = 3, thickness = 5, angle = 2, padding = 1) {
            this.path = path;
    
            this.angle = angle;
            this.thickness = thickness;
            var length = end - start;
            var partlen = (length - (2 * padding)) / (2 * parts);
            console.log("ArrowOnPath", start, end, partlen);
            this.p = [this.pointsAt(start + padding, (-1 * padding))];
            for (var i = 1; i < (2 * parts); i++) {
                this.p.push(this.pointsAt(start + (i * partlen)));
            }
            this.p.push(this.pointsAt(start + length - padding, (1 * padding)));
        }
        pointAngle(length) {
            var p1 = this.path.getPointAtLength(length);
            var p2 = this.path.getPointAtLength(length + 0.01);
            if (p1.x == p2.x && p1.y == p2.y) {
                p1 = this.path.getPointAtLength(length - 0.01);
            }
            console.log(p1, p2);
            return Math.atan2((p2.y - p1.y), (p2.x - p1.x));
    
        }
        pointsAt(length, angleoffset = 0) {
            var m = this.path.getPointAtLength(length);
            var m_angle = this.pointAngle(length + angleoffset)
            console.log("a", m_angle);
            var a = {
                x: m.x + (this.thickness * Math.cos(m_angle + this.angle))
                , y: m.y + (this.thickness * Math.sin(m_angle + this.angle))
            };
    
            var b = {
                x: m.x + (this.thickness * Math.cos(m_angle - this.angle))
                , y: m.y + (this.thickness * Math.sin(m_angle - this.angle))
            };
    
            return { a: a, m: m, b: b }
        }
    
        p2s(point) {
            return point.x.toString() + "," + point.y.toString();
        }
    
    
        d3path() {
            var path = d3.path();
            var i = 0
    
            path.moveTo(this.p[i].m.x, this.p[i].m.y);
            path.lineTo(this.p[i].a.x, this.p[i].a.y);
    
            var nat = d3.curveNatural(path);
            nat.lineStart();
            for (var i = 0; i < this.p.length; i++) {
                nat.point(this.p[i].a.x, this.p[i].a.y);
    
            }
            var px = controlPoints(nat._x),
                py = controlPoints(nat._y);
            var n = this.p.length;
            var x = nat._x,
                y = nat._y;
            for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {
                path.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);
            }
    
    
            i = this.p.length - 1;
            path.lineTo(this.p[i].m.x, this.p[i].m.y);
            path.lineTo(this.p[i].b.x, this.p[i].b.y);
    
            nat.lineStart();
    
            for (var i = this.p.length - 1; i >= 0; --i) {
    
                nat.point(this.p[i].b.x, this.p[i].b.y);
    
            }
            var px = controlPoints(nat._x),
                py = controlPoints(nat._y);
            var n = this.p.length;
            var x = nat._x,
                y = nat._y;
            for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {
                path.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);
            }
    
            path.closePath();
            return path;
        }
        toString() {
            return this.d3path().toString();
        }
    }