D3 fügen Sie Bogenbeschriftungen in ein Kreisdiagramm ein, wenn genügend Platz vorhanden ist

8

Ich werde ein Textelement in jeden Bogen meines Kreisdiagramms (Mitte) einfügen - wie in diesem Beispiel gezeigt: Ссылка

Aber ich werde das Textelement nur einfügen, wenn der Raum für den ganzen Text ausreicht, also muss ich die Größe meines Textelements mit dem "verfügbaren" Leerzeichen in jedem Bogen vergleichen.

Ich denke, dass ich dies mit getBBox () tun kann, um die Textdimensionen zu erhalten ... aber wie kann ich die Dimension des verfügbaren Raums in jedem Bogen erhalten (und vergleichen).

thx ...!

    
Kollisionskurs 05.11.2013, 15:28
quelle

2 Antworten

36

Diese Frage wurde wurde mehrmals zuvor angefragt.

Die Lösungen, die ich vorgeschlagen habe, sind rotate das Label, aber es hat mich nie ganz zufrieden gestellt. Ein Teil davon war das schreckliche Font-Rendering, das von einigen Browsern durchgeführt wurde, und der Verlust an Lesbarkeit, der das bringt, und das seltsame flip , wenn ein Label die 180° -Zeile kreuzt. In einigen Fällen waren die Ergebnisse akzeptabel und unvermeidbar, z. wenn die Etiketten zu lang waren.

Eine der anderen Lösungen, die von Lars vorgeschlagen wurde, besteht darin, die Etiketten außerhalb des Tortendiagramms zu platzieren. Das schiebt jedoch nur die Labels nach außen, was ihnen einen größeren Radius verleiht, aber das Problem overlap nicht vollständig löst.

Die andere Lösung verwendet tatsächlich die Technik, die Sie vorschlagen: entfernen die Etiketten, die nicht passen.

Überlaufende Etiketten ausblenden

Vergleichen Sie Original mit >= 65 label, das zu Lösung wo das überlaufende Label verschwunden ist.

Reduzierung des Problems

Die wichtigste Erkenntnis besteht darin, dass bei diesem Problem festgestellt wird, ob ein konvexes Polygon ( ein Rechteck , die Bounding Box) in einem anderen konvexen Polygon ( -ish ) ( ein Keil ).

Das Problem kann reduziert werden, um herauszufinden, ob alle Punkte des Rechtecks ​​innerhalb des Keils liegen oder nicht. Wenn dies der Fall ist, liegt das Rechteck innerhalb des Bogens.

Liegt ein Punkt in einem Keil

?

Jetzt ist dieser Teil einfach. Alles, was Sie tun müssen, ist zu überprüfen:

  1. Der Abstand des Punktes vom Mittelpunkt ist kleiner als radius
  2. Der Winkel zwischen dem Punkt in der Mitte liegt zwischen startAngle und endAngle des Bogens.
%Vor%

Suchen Sie die Begrenzungsbox der Beschriftungen

Jetzt, wo wir das aus dem Weg haben, wird im zweiten Teil herausgefunden, was die vier Ecken des Rechtecks ​​sind. Das ist auch einfach:

%Vor%

Das Mark der Lösung befindet sich in der Funktion each . Wir platzieren den Text zuerst an der richtigen Stelle, so dass das DOM ihn rendert. Dann verwenden wir die getBBox() -Methode, um die Bounding-Box von text in der Benutzerbereich Ein neuer Benutzerbereich wird von jedem Element erstellt, für das ein transform -Attribut gesetzt ist darauf . Dieses Element ist in unserem Fall die Box text . Der zurückgegebene Begrenzungsrahmen ist also relativ zur Mitte des Textes, da text-anchor auf middle gesetzt wurde.

Die Position von text relativ zu arc kann berechnet werden, da wir die Transformation 'translate(' + arc.centroid(d) + ')' darauf angewendet haben. Sobald wir das Zentrum haben, berechnen wir nur die topLeft , topRight , bottomLeft und bottomRight Punkte daraus und sehen, ob sie alle in wedge liegen.

Schließlich bestimmen wir, ob alle Punkte innerhalb des Keils liegen und wenn sie nicht passen, setzen Sie die Eigenschaft display CSS auf none .

Arbeitsdemo

Original

Lösung

Hinweis

  1. Ich benutze die innerRadius welche, wenn nicht Null, die wedge nicht-konvex macht, was die Berechnungen viel komplexer macht! Ich denke jedoch, dass die Gefahr hier nicht signifikant ist, da der einzige Fall, in dem es scheitern könnte, dies ist, und ehrlich gesagt denke ich nicht, dass es oft passieren wird (ich hatte Schwierigkeiten, dieses Gegenbeispiel zu finden):

  2. x und y werden umgedreht und y hat ein negatives Vorzeichen während der Berechnung von Math.atan2 . Dies liegt an der Differenz zwischen der Darstellung von Math.atan2 und d3.svg.arc im Koordinatensystem und der Richtung positiver y mit svg .

    Koordinatensystem für Math.atan2

    θ = Math.atan2(y, x) = Math.atan2(-svg.y, x)

    Koordinatensystem für d3.svg.arc

    θ = Math.atan2(x, y) = Math.atan2(x, -svg.y)

musically_ut 05.11.2013, 23:59
quelle
2

Das können Sie nicht wirklich mit der Begrenzungsbox tun, da die Begrenzungsbox viel größer als ein Keil für die Keile des Kreisdiagramms ist. Das heißt, obwohl der Keil am äußeren Rand breit genug wäre, um den Text aufzunehmen, bedeutet das nicht, dass er an der tatsächlichen Position des Textes breit genug ist.

Leider gibt es keine einfache Möglichkeit, das zu tun, was Sie versuchen (Pixel-Level-Overlap-Test). Siehe z.B. diese Frage für weitere Informationen. Ich würde vorschlagen, die Textbeschriftungen einfach außerhalb des Tortendiagramms zu platzieren, damit Sie nicht auf dieses Problem stoßen.

    
Lars Kotthoff 05.11.2013 15:41
quelle

Tags und Links