2015-08-12 3 views
1

Я пытаюсь сгенерировать элементы SVG, используя родительские и вложенные данные, но у меня проблемы с получением информации. Это очень похоже на Combining Parent and Nested Data with d3.js, что было очень полезно, но не совсем решает мою проблему.Вложенные данные с D3.js

В этом примере я рисую пути SVG, состоящие из нескольких точек. Я хочу нарисовать касательную к каждому из сегментов линии пути. В основном это работает, но я не могу создавать группы для касательных путей, и вместо этого они заканчиваются в корне SVG. Я хотел бы структуру выглядеть следующим образом:

<svg width="100" height="100"> 

    <g id="0" class="linegroup"> 
    <g class="linepath"> 
     <path d="M0,0L10,10L20,20"></path> 
    </g> 
    <g class="tangentgroup"> 
     <path class="tan" d="M5,5L-5,15"></path> 
     <path class="tan" d="M15,15L5,25"></path> 
    </g> 
    </g> 

    <g id="1" class="linegroup"> 
    <g class="linepath"> 
     <path d="M30,30L40,40L50,50"></path> 
    </g> 
    <g class="tangentgroup"> 
     <path class="tan" d="M35,35L25,45"></path> 
     <path class="tan" d="M45,45L35,55"></path> 
    </g> 
    </g> 

</svg> 

Но вместо этого, все касательные путей в настоящее время добавляется к SVG и я не могу создать «tangentgroup» SVG группы:

<svg width="100" height="100"> 

    <g id="0" class="linegroup"> 
    <g class="linepath"> 
     <path d="M0,0L10,10L20,20"></path> 
    </g> 
    </g> 

    <g id="1" class="linegroup"> 
    <g class="linepath"> 
     <path d="M30,30L40,40L50,50"></path> 
    </g> 
    </g> 

    <path class="tan" d="M5,5L-5,15"></path> 
    <path class="tan" d="M15,15L5,25"></path> 
    <path class="tan" d="M35,35L25,45"></path> 
    <path class="tan" d="M45,45L35,55"></path> 

</svg> 

Вот мой код, как сейчас. Я также открыт для предложений о том, как улучшить его другими способами.

// test data 
var lines = [{ 
     id: 0, 
     coordinates: [[0,0],[10,10],[20,20]]}, 
    { 
     id: 1, 
     coordinates: [[30,30],[40,40],[50,50]] 
    }]; 

var diameter = 100; 

var d3line = d3.svg.line(); 

var svg = d3.select("body").append("svg") 
    .attr("width", diameter) 
    .attr("height", diameter); 

// create groups for each line and its tangents 
var linegroups = svg.selectAll('.linegroup') 
    .data(lines) 
    .enter().append('g') 
    .attr('class', 'linegroup') 
    .attr('id', function(d) { return d.id; }); 

// add the line path 
linegroups.append('g') 
    .attr('class', 'linepath') 
.append('path') 
    .attr("d", function(d) { return d3line(d.coordinates); }); 

/////// Problem section //////// 

// create a group for the line's segment's tangents, 
// and create a tangent path for each segment 
linegroups.each(function(line, i) { 
    d3.selectAll(this) 
     // The "tangentgroup" groups never show up 
    .append('g') 
    .attr('class', 'tangentgroup') 
    .data(lineSegments(line.coordinates)) 
     // 'tan' paths get appended to parent svg, not to 'linegroup'. 
    .enter().append('path') 
    .attr('class', 'tan') 
    .attr('d', function (d) { 
     return d3line(tangentFromMidpoint(d)); 
    }); 
}); 

//////////////////////////////// 

// returns lineSegments, comprised of pairs of points 
function lineSegments (coordinates) { 
    return d3.range(coordinates.length - 1).map(function(i) { 
    return [coordinates[i], coordinates[i + 1]]; 
    }); 
} 

// returns a tangent line starting from the mid-point of the original line 
function tangentFromMidpoint (line) { 
    var p1 = line[0]; 
    var p2 = line[1]; 
    var midPoint = [(p1[0] + p2[0])/2, (p1[1] + p2[1])/2]; 
    var tv = tangentVectors(p1,p2)[0]; 
    return [midPoint, [tv[0] + midPoint[0], tv[1] + midPoint[1]]]; 
} 

// Returns both tangent vectors (not unit-vector) for a line 
function tangentVectors (p1,p2) { 
    // if we define dx=x2-x1 and dy=y2-y1, 
    // then the normals are (-dy, dx) and (dy, -dx) 
    var dx = p2[0] - p1[0]; 
    var dy = p2[1] - p1[1]; 
    return [[-dy, dx, dy, -dx]]; 
} 

ответ

1

Проблема заключается в том, что вы звоните .data() без .selectAll() первый, но затем действовать по выбору .enter(). Код должен быть

linegroups.each(function(line, i) { 
    d3.select(this) 
    .append('g') 
    .attr('class', 'tangentgroup') 
    .selectAll("path") 
    .data(lineSegments(line.coordinates)) 
    .enter().append('path') 
    .attr('class', 'tan') 
    .attr('d', function (d) { 
     return d3line(tangentFromMidpoint(d)); 
    }); 
}); 

Полная демонстрация here. Вам даже не нужен .each() хотя:

linegroups.append('g') 
    .attr('class', 'tangentgroup') 
    .selectAll("path") 
    .data(function(line) { return lineSegments(line.coordinates); }) 
    .enter().append('path') 
    .attr('class', 'tan') 
    .attr('d', function (d) { 
    return d3line(tangentFromMidpoint(d)); 
    }); 

Полная демо here.

+0

Интересно, я не понимал, что 'd3.selectAll (this)' также вызывает проблемы. Мне, очевидно, придется перечитать подробности API-интерфейса выбора. Благодарим за улучшение, избавившись от '.each()'! – seren

+1

Если вы используете 'd3.selectAll()', это должен быть список узлов, например. '[Это]'. –

+0

Ах, конечно. Я совсем забыл. Благодаря! – seren

Смежные вопросы