1

Still in the process of improving my competence about D3, I got stuck with a problem where I'm trying to plot a zoomable curve in a SVG element with margins (so that I need a clipPath rect to avoid that plot invades margins when zoomed) but the clipPath margins cut the display of d3.symbols off the plot.

This is the relevant code for the plot

var margin = {top: 20, right: 60, bottom: 30, left: 30},
    w = 960 - margin.left - margin.right,
    h = 500 - margin.top - margin.bottom;

var svg = d3.select("body").append("svg")
                .attr("width", w + margin.left + margin.right)
            .attr("height", h + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.append("defs").append("clipPath")
        .attr("id", "clip")
    .append("rect")
        .attr("width", w)
        .attr("height", h);

// The curve I want to plot: y=x^2
var my_curve = d3.range(10).map(function(d) { var my_y = d * d; return { "x" : d, "y" : my_y }; });

var x_range_min = d3.min(my_curve, function(d) { return d.x; }); 
var x_range_max = d3.max(my_curve, function(d) { return d.x; });
var y_range_min = d3.min(my_curve, function(d) { return d.y; }); 
var y_range_max = d3.max(my_curve, function(d) { return d.y; });

var xScale = d3.scaleLinear().domain([x_range_min, x_range_max]).range([0, w]);
var yScale = d3.scaleLinear().domain([y_range_max, y_range_min]).range([0, h]);

var xAxis = d3.axisBottom().scale(xScale);
var yAxis = d3.axisLeft().scale(yScale);


// symbols
svg.selectAll(".my_pts").data(my_curve).enter().append("path")
    .attr("class", "my_pts")
    .attr("d", d3.symbol().type(d3.symbolTriangle).size(200))
    .attr("transform", function(d) { return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"; })
    // with this zoomed line does not enter margin area
    .attr("clip-path", "url(#clip)");

...as you can see only part of the triangle symbol is depicted, I guess because the path is drawn at 0,0 and cut by the clipPath before the translation can be performed.

I have also posted this fiddle https://jsfiddle.net/fabio_p/988c1sjv/ where you can find a more complete version of the code, with the brush & zoom function, so that you can see why the clipPath is needed (if you have never encountered the issue with margins before)

My question is the following: is there a workaround to this problem? I was hoping to find a way to directly draw the symbol in the right place without the need of a later translation (possibly with a "custom symbol type"), but it seems it goes beyond my current skills, since I was unable to produce any actual solution.

Any suggestion would be welcome. Thanks in advance.

1 Answer 1

3

Create a group with the clip path:

var clipping = svg.append("g")
    .attr("clip-path", "url(#clip)");

And append both the line and the symbols to the group:

clipping.append("path")
    .data([my_curve])
    //etc...

Here is your updated fiddle: https://jsfiddle.net/cvtvrL2q/

2
  • Brilliant! This solution is simple and elegant! From your suggestion however it seems that most of my "problems" originated from the <svg> design more than from d3: is there any good source of info about the order in which this kind of svg operations (clipping areas / transforming / etc.) take place? The W3C recommendation "...the given child element is clipped by the referenced clipping path before OR'ing [...] with the silhouettes of the other child elements" was not so self-explanatory to me and who knows if I'm still missing further details :-(
    – Fabio P.
    Commented Jan 6, 2017 at 8:26
  • Well, I'm not the best person to guide you regarding this because I normally don't use clip paths in my graphs. Maybe you can post this issue as a new question using the "svg" tag only, that way the SVG gurus can help you. But you have to make a question about an actual code, not just "asking for a tutorial or best practices", otherwise they will close your question as off topic in seconds... Commented Jan 6, 2017 at 12:55

Not the answer you're looking for? Browse other questions tagged or ask your own question.