Lord Bo
Lord Bo

Reputation: 3298

Correctly filling augmented svg path created by D3

I want to use D3 to create augmented svg-pathes and filling them. Augmented pathes are pathes, that contain multiple parts using different interpolation methods. Have a look at the following example (execute the code to see the svg):

p1 = [
  [10,10],
  [10,190],
  [190,190]
];

p2 = [
  [190,190],
  [50,50],
  [190,20]
];

p3 = [
  [190,20],
  [190, 10]
];

p4 = [
  [190,10],
  [100,20],
  [10,10]
];

  var svg = d3.select('svg')
              .attr("width", 200)
              .attr("height", 200)
              .attr("viewBox", "0 0 200 200");

  var line = d3.svg.line().interpolate("linear");
  var spline = d3.svg.line().interpolate("basis");
  
    svg.append("path")
     .attr("d", function(){
       return line(p1)  + spline(p2) + line(p3) + spline(p4)
     })
     .attr("fill", "none")
     .attr("stroke-width", 1)
     .attr("stroke", "black");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg>
</svg>
If I now fill the created path (changing fill from "none" to "purple" for example), I get an undesired result:

<svg viewBox="0 0 200 200" height="190" width="190">
<path stroke="black" stroke-width="1" fill="purple" d="M10,10L10,190L190,190M190,190L166.66666666666666,166.66666666666666C143.33333333333331,143.33333333333331,96.66666666666666,96.66666666666666,96.66666666666666,68.33333333333333C96.66666666666666,39.99999999999999,143.33333333333331,29.999999999999996,166.66666666666666,24.999999999999996L190,20M190,20L190,10M190,10L174.99999999999997,11.666666666666664C160,13.333333333333332,130,16.666666666666664,99.99999999999999,16.666666666666664C69.99999999999999,16.666666666666664,39.99999999999999,13.333333333333332,24.999999999999996,11.666666666666666L10,10"></path></svg>

The shape is not recognized as one shape and thus not filled correctly. (By the way: it works when using only one spline and only one linear interpolated part.) One possibility to solve that problem, would be to remove the unnessesary movement commands inserted by D3. However, I can't believe, that this is the only solution. There must be a simpler and cleaner solution. Probably I'm misinterpreting how to use D3. Can anyone give me a clue?

Edit: What I expect the filled shape to look like:

<svg viewBox="0 0 200 200" height="190" width="190">
<path stroke="black" stroke-width="1" fill="purple"
d="M10,10L10,190L190,190L166.66666666666666,166.66666666666666C143.33333333333331,143.33333333333331,96.66666666666666,96.66666666666666,96.66666666666666,68.33333333333333C96.66666666666666,39.99999999999999,143.33333333333331,29.999999999999996,166.66666666666666,24.999999999999996L190,20L190,10L174.99999999999997,11.666666666666664C160,13.333333333333332,130,16.666666666666664,99.99999999999999,16.666666666666664C69.99999999999999,16.666666666666664,39.99999999999999,13.333333333333332,24.999999999999996,11.666666666666666L10,10"></path></svg>

Upvotes: 0

Views: 156

Answers (1)

Mark
Mark

Reputation: 108517

You can't just concatenate the paths together. Each one starts with a move to command (M10,10 or something like that). This will produce an "open" path that you can't be filled correctly. Easy fix, is to just remove the move to command on all but the first path:

<!DOCTYPE html>
<html>

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
</head>

<body>
  <svg>
  </svg>
  <script>
    p1 = [
      [10, 10],
      [10, 190],
      [190, 190]
    ];

    p2 = [
      [190, 190],
      [50, 50],
      [190, 20]
    ];

    p3 = [
      [190, 20],
      [190, 10]
    ];

    p4 = [
      [190, 10],
      [100, 20],
      [10, 10]
    ];

    var svg = d3.select('svg')
      .attr("width", 200)
      .attr("height", 200)
      .attr("viewBox", "0 0 200 200");

    var line = d3.svg.line().interpolate("linear");
    var spline = d3.svg.line().interpolate("basis");

    svg.append("path")
      .attr("d", function() {

        var s1 = line(p1),
            s2 = spline(p2),
            s3 = line(p3),
            s4 = spline(p4);
            
        s2 = s2.substring(s2.indexOf("L"), s2.length);
        s3 = s3.substring(s3.indexOf("L"), s3.length);
        s4 = s4.substring(s4.indexOf("L"), s4.length);  

        return s1 + s2 + s3 + s4;
      })
      .attr("fill", "purple")
      .attr("stroke-width", 1)
      .attr("stroke", "black")
  </script>
</body>

</html>

Upvotes: 1

Related Questions