2014-02-11 2 views
3

я смог построить меченый пончик диаграмму так же, как в следующей скрипке:D3 изогнутые метки в центре дуги

http://jsfiddle.net/MX7JC/9/

Но теперь я пытаюсь поставить метку в середине каждой дуги и охватывать их вдоль дуги (нарисуйте метку для каждой дуги). Для этого я подумывал поставить svg:text по адресу svg:textPath, используя функцию d3.svg.line.radial.

Потом я наткнулся на следующую скрипке:

http://jsfiddle.net/Wexcode/CrDUy/

Однако я с трудом, чтобы связать var arcs (тот, имеющий фактические данные) из бывшей скрипки с var line из последней скрипки как последняя скрипка использует функцию d3.range как данные.

Я делал пробную ошибку в течение нескольких часов, но ничего не работает. Кто-нибудь знает, как работает d3.svg.line.radial вместе с d3.svg.arc?

ответ

8

d3.svg.line.radial функция создает серию кубических кривых Безье (не дуги) между несколькими точками в массиве на основе входных полярных координат (радиус и угол) для каждой точки.

(Пример, связанный с , появляется, чтобы нарисовать круг, но только потому, что он разбивает круг на многие плотно разнесенные точки - попробуйте использовать 5 очков вместо 50, и вы увидите, что форма кривая не является реальным кругом.)

Функция d3.svg.arc выполняет функцию, состоящую из двух концентрических дуг и их прямых линий, основанных на значениях для внутренних Radius, внешних Radius, startAngle и endAngle.

Оба метода определяют углы в радианах, начиная с «12 часов» (вертикально вверх). Однако есть пара трудностей в том, что функция радиальной линии работает с объектами данных дуги.

Первая проблема заключается в том, что генератор линий ожидает передачи массива из нескольких точек, а не из одного объекта. Чтобы обойти это, вам нужно установить привязку элемента пути как массив объекта группы дуги, который повторяется дважды, один раз для начала и один раз для конца дуги, а затем использовать функцию в i, чтобы определить, следует ли использовать startAngle или endAngle для значения угла каждой точки.

Вот вариант вашей скрипки, создающей эти пути. Я не потрудился получать текст бежать по дорожке, я просто рисунок пути в черном:
http://jsfiddle.net/MX7JC/688/

Теперь вы видите, вторая проблема: если только даны две точки, генератор линия будет просто создать прямая линия между ними.

См простой пример кривой: http://jsfiddle.net/4VnHn/5/

Для того, чтобы получить любой вид кривой с генераторами на линии по умолчанию, вам необходимо добавить дополнительные точки, чтобы действовать в качестве контрольных точек, и изменить the line interpolate method to an "open" option таким образом, чтобы конечный контрольные точки Арен Нарисовано.Я обнаружил, что создание контрольных точек начала и конца на 45 градусов выше начальной и конечной точек кривой (вокруг круга) создало кривую, которая была приемлемо подобна дуге в моем простом примере.

Смотрите лучше простой пример кривой: http://jsfiddle.net/4VnHn/6/

Для вашей визуализации, кривые генератор теперь должен быть передан объект данных повторяется четыре раза в массиве, а угол сбруя теперь будет нужен переключатель чтобы определить разные точки: http://jsfiddle.net/MX7JC/689/

Результаты приемлемы для небольших сегментов пончиков, но не для тех, которые более 45 градусов сами по себе - в этих случаях контрольные точки до сих пор находятся вокруг вокруг круга, что они полностью отбрасывают кривую. Генератор кривой ничего не знает о круге, он просто пытается плавно подключить точки, чтобы показать тренд от одного к другому.

Лучшее решение для фактически нарисовать дугу, используя arc notation for SVG paths. Генератор дуги использует нотацию дуги, но создает полную двумерную форму. Чтобы создать дуги с генератором строк, вам понадобится специальная функция интерполяции строк, которую вы затем можете передать методу interpolate генератора линии.

Генератор линии выполнит функцию пользовательского интерполятора линии, передав в массив точек, которые уже были преобразованы из полярных координат в координаты x, y. Оттуда вам нужно определить уравнение дуги. Так как функция дуги также необходимо знать радиус дуги, я использую вложенную функцию - внешняя функция принимает радиус в качестве параметра и возвращает функцию, которая будет принимать массив точек в качестве параметра:

function arcInterpolator(r) { 
    //creates a line interpolator function 
    //which will draw an arc of radius `r` 
    //between successive polar coordinate points on the line 

    return function(points) { 
    //the function must return a path definition string 
    //that can be appended after a "M" command 

     var allCommands = []; 

     var startAngle; //save the angle of the previous point 
         //in order to allow comparisons to determine 
         //if this is large arc or not, clockwise or not 

     points.forEach(function(point, i) { 

      //the points passed in by the line generator 
      //will be two-element arrays of the form [x,y] 
      //we also need to know the angle:   
      var angle = Math.atan2(point[0], point[1]); 
      //console.log("from", startAngle, "to", angle); 

      var command; 

      if (i) command = ["A", //draw an arc from the previous point to this point 
         r, //x-radius 
         r, //y-radius (same as x-radius for a circular arc) 
         0, //angle of ellipse (not relevant for circular arc) 
         +(Math.abs(angle - startAngle) > Math.PI), 
          //large arc flag, 
          //1 if the angle change is greater than 180degrees 
          // (pi radians), 
          //0 otherwise 
         +(angle < startAngle), //sweep flag, draws the arc clockwise 
         point[0], //x-coordinate of new point 
         point[1] //y-coordinate of new point 
         ]; 

      else command = point; //i = 0, first point of curve 

      startAngle = angle; 

      allCommands.push(command.join(" ")); 
       //convert to a string and add to the command list 
     }); 

     return allCommands.join(" "); 
    }; 
} 

Пример: http://jsfiddle.net/4VnHn/8/

Чтобы заставить его работать с вашим графиком пончика, я начал с версии выше, которая создавала прямые линии, и изменила параметр интерполяции линейного генератора, чтобы использовать мою пользовательскую функцию. Единственное дополнительное изменение, которое я должен был сделать, это добавить дополнительную проверку, чтобы убедиться, что ни один из углов на графике не закончился более чем на 360 градусов (что, я уверен, было проблемой округления на последнем сегменте дуги, но вызывало моя функция, чтобы нарисовать окончательную дугу всего пути по кругу, назад):

var curveFunction = d3.svg.line.radial() 
     .interpolate(arcInterpolator(r-45)) 
     .tension(0) 
     .radius(r-45) 
     .angle(function(d, i) { 
      return Math.min(
       i? d.endAngle : d.startAngle, 
       Math.PI*2 
       ); 
     //if i is 1 (true), this is the end of the curve, 
     //if i is 0 (false), this is the start of the curve 
     }); 

Живой пример: http://jsfiddle.net/MX7JC/690/

Наконец, чтобы использовать эти кривые траекторий текста:

  • Установит кривая не должна иметь никакого штриха и не заполнять;
  • дают каждой кривой уникальное значение Идентификатор на основе ваших категорий данных
    (для примера, вы можете использовать метку пончика плюс подпись данных, чтобы придумать что-то вроде «textcurve-Agg-Intl»);
  • Добавить <textPath> element для каждой маркировки;
  • набор текста пути xlink:href атрибут быть # плюс к тому же уникальное значение идентификатора для этих данных
2

Я подумал о другом подходе.Это немного окольный способ сделать это, но требует много меньше пользовательского кода.

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

Упрощенный пример здесь: http://jsfiddle.net/4VnHn/10/

Пример с кольцевой диаграммой здесь: http://jsfiddle.net/MX7JC/691/

Код ключа:

var textArc = d3.svg.arc().outerRadius(r-45); //to generate the arcs for the text 

textCurves.attr("d", function(d) { 
    var pie = textArc(d); //get the path code for the entire pie piece 

    var justArc = /[Mm][\d\.\-e,\s]+[Aa][\d\.\-e,\s]+/; 
     //regex that matches a move statement followed by an arc statement 

    return justArc.exec(pie)[0]; 
     //execute regular expression and extract matched part of string 
}); 

r-45 только на полпути между внутренними и внешними радиусами пончиков. Элемент регулярного выражения [\d\.\-e,\s]+ соответствует цифрам, периодам, отрицательным знакам, индикаторам экспоненты ('e'), запятым или пробелам, но не любым другим буквам, обозначающим другую команду пути. Я думаю, что остальное довольно самоочевидно.

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