2016-10-06 42 views
5

मैंने एक कस्टम पथ रेंडरर बनाया है जो स्निपेट में दिखाए गए मेरे डी 3 ग्राफ में नोड्स के बीच एक तीर खींचता है। मेरे पास एक आखिरी मुद्दा है जिसे मैं अटक रहा हूं,मैं d3v4 में दो बिंदुओं के बीच एक तीर कैसे आकर्षित करूं?

मैं तीर भाग को कैसे घुमा दूंगा ताकि यह स्रोत की दिशा के बजाय वक्र की दिशा से इंगित हो रहा हो?

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

नहीं [कुछ इस तरह (एक मार्कर का उपयोग)] हैं (http://bl.ocks.org/mbostock/1153292) बहुत आसान हो सकता है? – Mark

+0

मुझे लगता है कि 'ओरिएंट = ऑटो' जैसे किसी मार्कर का उपयोग करना ड्राइंग/सिर घुमाए जाने से निश्चित रूप से सरल होगा, लेकिन मुझे नहीं पता कि आपके जैसे 'कैनवास' पर प्रतिपादन करते समय। एक svg तत्व पर http://bl.ocks.org/tomgp/d59de83f771ca2b6f1d4 उदाहरण के लिए देखें यदि आपने इसे नहीं देखा (मेरा नहीं)। – mgc

+1

@ मार्क मार्कर सिर्फ खराब दिखते हैं, आईएमओ को अनुकूलित करना अधिक कठिन होता है। जब वे हाइलाइट करते हैं तो उन्हें इस तरह की तरह सही ढंग से उनके चारों ओर एक सीमा नहीं मिलेगी। जब मैं पथ पर ढाल लागू करता हूं तो वे भी काम नहीं करेंगे। – Andrew

उत्तर

2

मैं क्या @Mark टिप्पणी में सुझाव कर समाप्त हो गया है, मैं बिंदु वक्र दूर दो अंक के बीच सामान्य रास्ते के मध्य में के साथ की ऊंचाई है कि गणना, तो इकाई की गणना शुरुआती बिंदु से मध्य बिंदु तक और फिर मध्यबिंदु से अंत तक वेक्टर। मैं फिर उन सभी आवश्यक अंक प्राप्त करने के लिए उपयोग कर सकते हैं।

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

बहुत अच्छा। मैं उसी पर काम कर रहा था [यहां] (http://plnkr.co/edit/eG1dtD1MHBo3vyTdgcKN?p=preview), आपने मुझे इसे हराया :) – Mark

संबंधित मुद्दे