One option is to nest the text inside a group. The group would get positioned mostly like you're currently positioning the text, except instead of using an "x" attribute, you'd use the transform
attribute with a translate
in addition to your existing rotate
directive. Note that the order of the transformations matters here; you need to first rotate THEN translate.
Then you'd apply local transformation to the text within the group. I.e. it would get rotated 180 degrees if it needs to be flipped. This way the text doesn't need to be moved into position –– the group takes care of that –– but it will probably need to have small local adjustments when it's flipped (also, ir would probably have to be aligned right in this case).
The code below should more or less do it, though it's impossible for me to test and tweak it without a working jsFiddle.
svg.selectAll("g.label")
.data(partition.nodes)
.enter()
.append("g")
.attr("class", "label")
.attr("transform", function(d) {
// First, rotate the group (not the text) and then translate it
// by the same amount that used to be applied via "x" attr
return
"rotate(" + ((d.x + d.dx / 2 - Math.PI / 2) / Math.PI * 180) + ") " +
"translate(" + Math.sqrt(d.y) + ")";
})
.each(function(d, i) {
d3.select(this) // select the containing group (which was just created above)
.append('text') // append text inside the group
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) {
// Locally transform the text within the containing group:
// Label should be flipped if group's rotation is greater than Math.PI (aka 180 degs)
var shouldBeFlipped = d.x + d.dx / 2 - Math.PI / 2 > Math.PI;
return "rotate(" + (shouldBeFlippled ? 180 : 0) + ")"
})
.text(function(d) { return d.name;});
});