Detect if rect is completely inside circle d3js - d3.js

I'm trying to detect if my rectangle is completely enclosed inside the circle. If the rectangle is completely enclosed in the circle I'd like it to stay "steelblue" if it touches or crosses the line I'd like it to switch to red.
I've figured out a way based of the x, y, cx, cy, and r to determine if it is within the bounding box of the circle, but I need to check it against the actual circle.
I have a running example here: http://jsfiddle.net/TheMcMurder/T92jF/
my code is below:
var drag = d3.behavior.drag()
.on("drag", function(){
var self = d3.select(this)
var dx = d3.event.dx;
var dy = d3.event.dy;
var x = self.attr("x")
var y = self.attr("y")
self.attr("x", (+x + dx))
self.attr("y", (+y + dy))
detection(d3.select(".circle"), d3.select(".rect"))
})
var svg = d3.select("body").append("svg")
.attr("width", 400)
.attr("height", 400)
.attr("class", "parent_svg")
svg.append("rect")
.attr("width", 400)
.attr("height", 400)
.style("fill", "#e4e5e5")
var rect = svg.append("rect")
.attr("width", 100)
.attr("height", 48)
.attr("x", 50)
.attr("y", 50)
.style("fill", "steelblue")
.attr("class", "rect")
.style("cursor", "all-scroll")
.call(drag)
var circle = svg.append("circle")
.attr("class", "circle")
.attr("r", 300/2)
.attr("cx", 350/2)
.attr("cy", 350/2)
.style("fill", "none")
.attr("stroke", "orange")
.attr("stroke-width", 1)
detection(circle, rect)
function detection(circle, rect){
var cx = (+circle.attr("cx"))
var cy = (+circle.attr("cy"))
var r = (+circle.attr("r"))
var x = (+rect.attr("x"))
var y = (+rect.attr("y"))
var width = (+rect.attr("width"))
var height = (+rect.attr("height"))
var x_range = false
var y_range = false
if ( x > (cx-r) && (x+width) < (cx+r)){
x_range = true;
}
if ( y > (cy-r) && (y+height) < (cy+r)){
y_range = true;
}
if (x_range && y_range){
rect.style("fill", "steelblue")
}else{
rect.style("fill", "red")
}
}

One way of doing this is to iterate over the four corners of the rectangle and check if the distance to the centre of the circle is less than the radius. If this is true for all four points, the rectangle is within the circle. If this is true for 1-3 points, the rectangle touches or intersects the circle.
var sum = 0;
[[x,y], [x+width,y], [x,y+height], [x+width,y+height]].forEach(function(c) {
sum += Math.sqrt(Math.pow(cx - c[0], 2) + Math.pow(cy - c[1], 2)) < r ? 1 : 0;
});
This code constructs the coordinates of the four corner points and counts the number of corners that are within the circle (i.e. distance to centre is less than radius). All you need to do now is to check whether that number is 4 or something else.
Complete demo here.

Related

d3 donut/pie chart - drawing a line between arcs

can't figure to find the endpoint of the arc to draw a line from (0,0) to the arc's endpoint..image attached
I could find the centroid of the arc and draw a line but here I want to pull a line to end of arc so that I can extend that line to the left /right side (and then append the circle at line's endpoint)...could't find any such solution over whole google. Any help will be appreciated. Just a hint will do.
When you pass a data array to the pie generator, it returns an array of objects with the following properties:
data - the input datum; the corresponding element in the input data array.
value - the numeric value of the arc.
index - the zero-based sorted index of the arc.
startAngle - the start angle of the arc.
endAngle - the end angle of the arc.
padAngle - the pad angle of the arc.
From these, you can use startAngle or endAngle to draw your lines, since they hold the arcs' starting points (and endpoints).
But there is a catch: unlike the regular trigonometric representation, D3 pie generator puts the 0 angle at 12 o'clock:
The angular units are arbitrary, but if you plan to use the pie generator in conjunction with an arc generator, you should specify angles in radians, with 0 at -y (12 o’clock) and positive angles proceeding clockwise.
Therefore, we have to subtract Math.PI/2 to get the correct angles.
In the following demo, the coordinates are calculates using sine and cosine:
.attr("y2", function(d) {
return Math.sin(d.startAngle - Math.PI / 2) * (outerRadius)
})
.attr("x2", function(d) {
return Math.cos(d.startAngle - Math.PI / 2) * (outerRadius)
})
Check the demo:
var data = [10, ,12, 50, 15, 20, 40, 6, 32, 17];
var width = 500,
height = 400,
radius = Math.min(width, height) / 2;
var color = d3.scaleOrdinal(d3.schemeCategory10)
var pie = d3.pie()
.sort(null);
var arc = d3.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 50);
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll(null)
.data(pie(data))
.enter().append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
var lines = svg.selectAll(null)
.data(pie(data))
.enter()
.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("y2", function(d) {
return Math.sin(d.startAngle - Math.PI / 2) * (radius - 50)
})
.attr("x2", function(d) {
return Math.cos(d.startAngle - Math.PI / 2) * (radius - 50)
})
.attr("stroke", "black")
.attr("stroke-width", 1)
<script src="https://d3js.org/d3.v4.min.js"></script>
Once you apply pie layout to your dataset by doing
var pieData = myPieLayout(myDataset)
inside pieData you will find, for each element of your dataset, two properties called startAngle and endAngle. Using that, you can find the position of the point you want, from the center of the pie by iterating through pieData elements and doing
var x = Math.cos(d.endAngle)*radius
var y = Math.sin(d.endAngle)*radius

how to evenly arrange circles in D3.js and keep equal distance between circumferences

I have a data var myData1 = [5, 10, 15] and I want to visualize it with circles.
So, I start with var mySvg1 = d3.select('body').append('svg')
The FIRST way to arrange circles is this:
Distance between every center is equal, I can achieve it with this code:
mySvg1.selectAll('circle').data(myData1).enter().append('circle')
.attr('cx', function(d,i){return 100+(i*44)})
.attr('cy', '55')
.attr('r', function(d){return d})
The SECOND way is this:
Distance between every center is increasing accordingly to the values in array:
var myHelp1 = 100
mySvg1.selectAll('circle').data(myData1).enter().append('circle')
.attr('cx', function(d,i){
myHelp1 += d + i*22
return myHelp1
})
.attr('cy', '55')
.attr('r', function(d){return d})
And here is MY QUESTION:
How to evenly arrange circles by (still dynamically) keeping equal distance between circumferences?
IIUIC, Here's one way. Let's say,
Gap between diameter endpoints is gap, (length of red lines)
base is current circle's left diameter point
base + d gives you current circle's center
newbase is next circle's left diameter point
var myData1 = [5, 10, 15]
var mySvg1 = d3.select('body').append('svg')
var base = 90
var newbase = 90
var gap = 20
mySvg1.selectAll('circle').data(myData1).enter().append('circle')
.attr('cx', function(d, i){
base = newbase
newbase = newbase + 2*d + gap
return base + d
})
.attr('cy', '55')
.attr('r', function(d){return d})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
https://jsfiddle.net/utfk44tL/

How to draw vertical text as labels in D3

I'm trying to draw vertical labels for the heatmap that I'm working. I'm using the example from http://bl.ocks.org/tjdecke/5558084. Here is the part of the code that I've changed:
var timeLabels = svg.selectAll(".timeLabel")
.data(ife_nr)
.enter().append("text")
.text(function(d) {
return d;
})
.attr("x", function(d, i) {
return (i * gridSize);
})
.attr("y", 0)
//.style("text-anchor", "middle")
//.attr("transform", "translate(" + gridSize / 2 + '-5' + ")")
.attr("transform", "translate(" + gridSize/2 + '-8' + "), rotate(-90)")
.attr("class", function(d, i) {
return ((i >= 0) ? "timeLabel mono axis axis-worktime" : "timeLabel mono axis");
});
But it appears the labels seems to be stacked on top one another on top of the first grid. How can I edit this code to get the labels correctly displayed?
Two problems: first, the translate should have a comma separating the values:
"translate(" + gridSize/2 + ",-8), rotate(-90)")
Assuming that -8 is the y value for the translate. If you don't have a comma, the value inside the parenthesis should be just the x translation (If y is not provided, it is assumed to be zero). But even if there is actually no comma and all that gridSize/2 + '-8' is just the x value you still have a problem, because number plus string is a string. You'll have to clarify this point.
Besides that, for rotating the texts over their centres, you'll have to set the cx and cy of the rotate. Have a look at this demo:
var svg = d3.select("body")
.append("svg")
.attr("width", 400)
.attr("height", 100);
var texts = svg.selectAll(".texts")
.data(["foo", "bar", "baz"])
.enter()
.append("text");
texts.attr("y", 50)
.attr("x", function(d,i){ return 50 + 80*i})
.text(function(d){ return d});
texts.attr("transform", function(d,i){
return "rotate(-90 " + (50 + 80*i) + " 50)";
});
<script src="https://d3js.org/d3.v4.js"></script>

d3 version 4 zoom behaviour on g element

The attached fiddle shows that on zoom the blue rectangles resize with the scale as expected but the yellow rectangles don't! The main difference is that the yellow rectangles were added to a 'g' element with text included. Any ideas why?
https://jsfiddle.net/sjp700/u6rj20jc/1/
var group = svg.selectAll(".rectangle")
.data(data);
gEnter = group.enter()
.append("g")
.attr("class", "rectangle")
.attr("fill", "yellow")
.attr("transform", function (d) { return "translate(" + x(d.start) + "," + y(d.finish) + ")"; });
gEnter.append("rect")
.attr("class", "rectband")
.merge(gEnter)
.attr("width", 50)
.attr("height", 18)
//.attr("rx", 10)
//.attr("ry", 10)
.style("opacity", .5) // set the element opacity
.style("stroke", "black");
Your yellow rectangles and text is not contained in an element that the zoom is applied to. Simple fix is to append them to gMain (which is the element on which the zoom is applied):
var group = gMain
.selectAll(".rectangle")
.data(data);
Updated fiddle here.

make a circle progress bar, but animate arc.endAngle (angle >180 degree) stops working in d3.js

I am running D3.js to draw a progress bar in circle shape, which you will see the demo on jsfiddle , the progress bar has a transition animation.
The main code is
var width = 960,
height = 500,
twoPi = 2 * Math.PI,
progress = 0,
total = 1308573, // must be hard-coded if server doesn't report Content-Length
formatPercent = d3.format(".0%");
var arc = d3.svg.arc()
.startAngle(0)
.innerRadius(0)
.outerRadius(240);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var meter = svg.append("g")
.attr("class", "progress-meter");
meter.append("path")
.attr("class", "background")
.attr("d", arc.endAngle(twoPi));
var foreground = meter.append("path")
.attr("class", "foreground");
foreground.attr("d", arc.endAngle(twoPi * 0))
foreground.transition().duration(1500).attr("d", arc.endAngle( twoPi * 2/3 ));
var text = meter.append("text")
.attr("text-anchor", "middle")
.attr("dy", ".35em");
to make the progress bar move, we only need to change to the arc.endAngle(), which is on the line.
foreground.transition().duration(1500).attr("d", arc.endAngle( twoPi * 2/3 ));
if the angle is less than 180, ( endangle < twoPi*1/2), then the animation works fine, but when the angle is larger than 180, so means endangle >= twoPi*1/2. then the animation would not show, and if you look at the console, you will find many errors on d3.js
Error: Problem parsing d="M1.1633760361312584e-14,-190A190,190 0 1.481481481481482e-7,1 -0.000022772330200401806,-189.9999883969182L0,0Z" meeting.html:1
2
Error: Problem parsing d="M1.1633760361312584e-14,-190A190,190 0 2.56e-7,1 -0.00003935058659476369,-189.99997994987467L0,0Z"
so what is the exact problem for this, how to solve it
It doesn't work because you can't use the standard transition for radial paths. By default, it simply interpolates the numbers without knowing what they represent, so in your case, you end up with some really small numbers (e.g. 1.1633760361312584e-14) which Javascript represents in exponential notation which is not valid for SVG paths.
The solution is to use a custom tween function that knows how to interpolate arcs:
function arcTween() {
var i = d3.interpolate(0, twoPi * 2/3);
return function(t) {
return arc.endAngle(i(t))();
};
}
Complete example here. You may also be interested in this example, which shows how to do it with data bound to the paths.

Resources