2016-10-06 17 views
5

Parçacıkta gösterildiği gibi d3 grafiğindeki düğümler arasında bir ok çizen özel bir yol oluşturucu oluşturdum. Son bir sorunum var,d3v4'te iki nokta arasında nasıl ok çizebilirim?

Ok bölümünü, kaynağın yönü yerine eğrinin yönünü gösterecek şekilde nasıl döndürürüm?

var w2 = 6, 
 
    ar2 = w2 * 2, 
 
    ah = w2 * 3, 
 
    baseHeight = 30; 
 

 
// Arrow function 
 
function CurvedArrow(context, index) { 
 
    this._context = context; 
 
    this._index = index; 
 
} 
 
CurvedArrow.prototype = { 
 
    areaStart: function() { 
 
    this._line = 0; 
 
    }, 
 
    areaEnd: function() { 
 
    this._line = NaN; 
 
    }, 
 
    lineStart: function() { 
 
    this._point = 0; 
 
    }, 
 
    lineEnd: function() { 
 
    if (this._line || (this._line !== 0 && this._point === 1)) { 
 
     this._context.closePath(); 
 
    } 
 
    this._line = 1 - this._line; 
 
    }, 
 
    point: function(x, y) { 
 
    x = +x, y = +y; // jshint ignore:line 
 
    switch (this._point) { 
 
     case 0: 
 
     this._point = 1; 
 
     this._p1x = x; 
 
     this._p1y = y; 
 
     break; 
 
     case 1: 
 
     this._point = 2; // jshint ignore:line 
 
     default: 
 
     var p1x = this._p1x, 
 
      p1y = this._p1y, 
 
      p2x = x, 
 
      p2y = y, 
 
      dx = p2x - p1x, 
 
      dy = p2y - p1y, 
 
      px = dy, 
 
      py = -dx, 
 
      pr = Math.sqrt(px * px + py * py), 
 
      nx = px/pr, 
 
      ny = py/pr, 
 
      dr = Math.sqrt(dx * dx + dy * dy), 
 
      wx = dx/dr, 
 
      wy = dy/dr, 
 
      ahx = wx * ah, 
 
      ahy = wy * ah, 
 
      awx = nx * ar2, 
 
      awy = ny * ar2, 
 
      phx = nx * w2, 
 
      phy = ny * w2, 
 

 
      //Curve figures 
 
      alpha = Math.floor((this._index - 1)/2), 
 
      direction = p1y < p2y ? -1 : 1, 
 
      height = (baseHeight + alpha * 3 * ar2) * direction, 
 

 

 
      //    r5 
 
      //r7   r6|\ 
 
      // ------------ \ 
 
      // ____________ /r4 
 
      //r1   r2|/ 
 
      //    r3 
 

 
      r1x = p1x - phx, 
 
      r1y = p1y - phy, 
 
      r2x = p2x - phx - ahx, 
 
      r2y = p2y - phy - ahy, 
 
      r3x = p2x - awx - ahx, 
 
      r3y = p2y - awy - ahy, 
 
      r4x = p2x, 
 
      r4y = p2y, 
 
      r5x = p2x + awx - ahx, 
 
      r5y = p2y + awy - ahy, 
 
      r6x = p2x + phx - ahx, 
 
      r6y = p2y + phy - ahy, 
 
      r7x = p1x + phx, 
 
      r7y = p1y + phy, 
 
      //Curve 1 
 
      c1mx = (r2x + r1x)/2, 
 
      c1my = (r2y + r1y)/2, 
 
      m1b = (c1mx - r1x)/(r1y - c1my), 
 
      den1 = Math.sqrt(1 + Math.pow(m1b, 2)), 
 
      mp1x = c1mx + height * (1/den1), 
 
      mp1y = c1my + height * (m1b/den1), 
 
      //Curve 2 
 
      c2mx = (r7x + r6x)/2, 
 
      c2my = (r7y + r6y)/2, 
 
      m2b = (c2mx - r6x)/(r6y - c2my), 
 
      den2 = Math.sqrt(1 + Math.pow(m2b, 2)), 
 
      mp2x = c2mx + height * (1/den2), 
 
      mp2y = c2my + height * (m2b/den2); 
 

 
     this._context.moveTo(r1x, r1y); 
 
     this._context.quadraticCurveTo(mp1x, mp1y, r2x, r2y); 
 
     this._context.lineTo(r3x, r3y); 
 
     this._context.lineTo(r4x, r4y); 
 
     this._context.lineTo(r5x, r5y); 
 
     this._context.lineTo(r6x, r6y); 
 
     this._context.quadraticCurveTo(mp2x, mp2y, r7x, r7y); 
 

 
     break; 
 
    } 
 
    } 
 
}; 
 
var w = 600, 
 
    h = 220; 
 
var t0 = Date.now(); 
 

 
var points = [{ 
 
    R: 100, 
 
    r: 3, 
 
    speed: 2, 
 
    phi0: 190 
 
}]; 
 
var path = d3.line() 
 
    .curve(function(ctx) { 
 
    return new CurvedArrow(ctx, 1); 
 
    }); 
 

 
var svg = d3.select("svg"); 
 
var container = svg.append("g") 
 
    .attr("transform", "translate(" + w/2 + "," + h/2 + ")") 
 

 
container.selectAll("g.planet").data(points).enter().append("g") 
 
    .attr("class", "planet").each(function(d, i) { 
 
    d3.select(this).append("circle").attr("r", d.r).attr("cx", d.R) 
 
     .attr("cy", 0).attr("class", "planet"); 
 
    }); 
 
container.append("path"); 
 
var planet = d3.select('.planet circle'); 
 

 
d3.timer(function() { 
 
    var delta = (Date.now() - t0); 
 
    planet.attr("transform", function(d) { 
 
    return "rotate(" + d.phi0 + delta * d.speed/50 + ")"; 
 
    }); 
 

 
    var g = document.createElementNS("http://www.w3.org/2000/svg", "g"); 
 
    g.setAttributeNS(null, "transform", planet.attr('transform')); 
 
    var matrix = g.transform.baseVal.consolidate().matrix; 
 
    svg.selectAll("path").attr('d', function(d) { 
 
    return path([ 
 
     [0, 0], 
 
     [matrix.a * 100, matrix.b * 100] 
 
    ]) 
 
    }); 
 
});
path { 
 
    stroke: #11a; 
 
    fill: #eee; 
 
}
<script src="https://d3js.org/d3.v4.min.js"></script> 
 
<svg width="600" height="220"></svg>

+2

[böyle bir şey (işaretleyici kullanarak)] etmem (http://bl.ocks.org/mbostock/1153292) çok daha kolay olacak? – Mark

+0

'Orient = auto' gibi bir şeye sahip bir işaretleyici kullanmak sanırım başını çizim/döndürmekten kesinlikle daha basit olurdu ama ben sizin gibi bir tuvalde nasıl işlediğimi bilmiyorum. Bir svg öğesinde, örneğin görmediyseniz (benim değil) http://bl.ocks.org/tomgp/d59de83f771ca2b6f1d4 adresine bakın. – mgc

+1

@Mark işaretçileri sadece kötü görünüyor, IMO'yu özelleştirmek daha zordur. Vurgu yaparken, bu irade gibi doğru bir sınır elde etmeyeceklerdir. Ayrıca, yola bir degrade uygularken çalışmazlar. – Andrew

cevap

2

ben @ Mark Yorum önerilen yapıyor sona erdi, o zaman birimi hesaplamak, uzak iki nokta arasındaki normal yarıda boyunca eğrinin yüksekliği noktasını hesaplamak başlangıç ​​noktasından orta noktasına ve tekrar orta noktadan sonuna doğru vektörler. Tüm gerekli noktaları almak için bunları kullanabilirim.

var arrowRadius = 6, 
 
    arrowPointRadius = arrowRadius * 2, 
 
    arrowPointHeight = arrowRadius * 3, 
 
    baseHeight = 30; 
 

 
// Arrow function 
 
function CurvedArrow(context, index) { 
 
    this._context = context; 
 
    this._index = index; 
 
} 
 
CurvedArrow.prototype = { 
 
    areaStart: function() { 
 
    this._line = 0; 
 
    }, 
 
    areaEnd: function() { 
 
    this._line = NaN; 
 
    }, 
 
    lineStart: function() { 
 
    this._point = 0; 
 
    }, 
 
    lineEnd: function() { 
 
    if (this._line || (this._line !== 0 && this._point === 1)) { 
 
     this._context.closePath(); 
 
    } 
 
    this._line = 1 - this._line; 
 
    }, 
 
    point: function(x, y) { 
 
    x = +x, y = +y; // jshint ignore:line 
 
    switch (this._point) { 
 
     case 0: 
 
     this._point = 1; 
 
     this._p1x = x; 
 
     this._p1y = y; 
 
     break; 
 
     case 1: 
 
     this._point = 2; // jshint ignore:line 
 
     default: 
 
     var p1x = this._p1x, 
 
      p1y = this._p1y, 
 
      p2x = x, 
 
      p2y = y, 
 

 
      //Curve figures 
 

 
      //    mp1 
 
      //    | 
 
      //    | height 
 
      //    | 
 
      // p1 ----------------------- p2 
 
      // 
 
      alpha = Math.floor((this._index - 1)/2), 
 
      direction = p1y < p2y ? -1 : 1, 
 
      height = (baseHeight + alpha * 3 * arrowPointRadius) * direction, 
 
      c1mx = (p2x + p1x)/2, 
 
      c1my = (p2y + p1y)/2, 
 
      m1b = (c1mx - p1x)/(p1y - c1my), 
 
      den1 = Math.sqrt(1 + Math.pow(m1b, 2)), 
 
      // Perpendicular point from the midpoint. 
 
      mp1x = c1mx + height * (1/den1), 
 
      mp1y = c1my + height * (m1b/den1), 
 

 
      // Arrow figures 
 
      dx = p2x - mp1x, 
 
      dy = p2y - mp1y, 
 
      dr = Math.sqrt(dx * dx + dy * dy), 
 
      // Normal unit vectors 
 
      nx = dy/dr, 
 
      wy = nx, 
 
      wx = dx/dr, 
 
      ny = -wx, 
 
      ahx = wx * arrowPointHeight, 
 
      ahy = wy * arrowPointHeight, 
 
      awx = nx * arrowPointRadius, 
 
      awy = ny * arrowPointRadius, 
 
      phx = nx * arrowRadius, 
 
      phy = ny * arrowRadius, 
 

 
      // Start arrow offset. 
 
      sdx = mp1x - p1x, 
 
      sdy = mp1y - p1y, 
 
      spr = Math.sqrt(sdy * sdy + sdx * sdx), 
 
      snx = sdy/spr, 
 
      sny = -sdx/spr, 
 
      sphx = snx * arrowRadius, 
 
      sphy = sny * arrowRadius, 
 

 
      //    r5 
 
      //r7   r6|\ 
 
      // ------------ \ 
 
      // ____________ /r4 
 
      //r1   r2|/ 
 
      //    r3 
 

 
      r1x = p1x - sphx, 
 
      r1y = p1y - sphy, 
 
      r2x = p2x - phx - ahx, 
 
      r2y = p2y - phy - ahy, 
 
      r3x = p2x - awx - ahx, 
 
      r3y = p2y - awy - ahy, 
 
      r4x = p2x, 
 
      r4y = p2y, 
 
      r5x = p2x + awx - ahx, 
 
      r5y = p2y + awy - ahy, 
 
      r6x = p2x + phx - ahx, 
 
      r6y = p2y + phy - ahy, 
 
      r7x = p1x + sphx, 
 
      r7y = p1y + sphy, 
 
      mpc1x = mp1x - phx, 
 
      mpc1y = mp1y - phy, 
 
      mpc2x = mp1x + phx, 
 
      mpc2y = mp1y + phy; 
 

 
     this._context.moveTo(r1x, r1y); 
 
     this._context.quadraticCurveTo(mpc1x, mpc1y, r2x, r2y); 
 
     this._context.lineTo(r3x, r3y); 
 
     this._context.lineTo(r4x, r4y); 
 
     this._context.lineTo(r5x, r5y); 
 
     this._context.lineTo(r6x, r6y); 
 
     this._context.quadraticCurveTo(mpc2x, mpc2y, r7x, r7y); 
 
     this._context.closePath(); 
 

 
     break; 
 
    } 
 
    } 
 
}; 
 

 
var w = 600, 
 
    h = 220; 
 
var t0 = Date.now(); 
 

 
var points = [{ 
 
    R: 100, 
 
    r: 3, 
 
    speed: 2, 
 
    phi0: 190 
 
}]; 
 
var path = d3.line() 
 
    .curve(function(ctx) { 
 
    return new CurvedArrow(ctx, 1); 
 
    }); 
 

 
var svg = d3.select("svg"); 
 
var container = svg.append("g") 
 
    .attr("transform", "translate(" + w/2 + "," + h/2 + ")") 
 

 
container.selectAll("g.planet").data(points).enter().append("g") 
 
    .attr("class", "planet").each(function(d, i) { 
 
    d3.select(this).append("circle").attr("r", d.r).attr("cx", d.R) 
 
     .attr("cy", 0).attr("class", "planet"); 
 
    }); 
 
container.append("path"); 
 
var planet = d3.select('.planet circle'); 
 

 
d3.timer(function() { 
 
    var delta = (Date.now() - t0); 
 
    planet.attr("transform", function(d) { 
 
    return "rotate(" + d.phi0 + delta * d.speed/50 + ")"; 
 
    }); 
 

 
    var g = document.createElementNS("http://www.w3.org/2000/svg", "g"); 
 
    g.setAttributeNS(null, "transform", planet.attr('transform')); 
 
    var matrix = g.transform.baseVal.consolidate().matrix; 
 
    svg.selectAll("path").attr('d', function(d) { 
 
    return path([ 
 
     [0, 0], 
 
     [matrix.a * 100, matrix.b * 100] 
 
    ]) 
 
    }); 
 
});
path { 
 
    stroke: #11a; 
 
    fill: #eee; 
 
}
<script src="https://d3js.org/d3.v4.min.js"></script> 
 
<svg width="600" height="220"></svg>

+0

Çok havalı. Ben aynı [burada] çalışıyordum (http://plnkr.co/edit/eG1dtD1MHBo3vyTdgcKN?p=preview), beni bunun için yendi :) – Mark

İlgili konular