d3.symbols being cut off by ClipPath area

1.2k views Asked by At

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

There are 1 answers

2
Gerardo Furtado On BEST ANSWER

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/