2016-03-28 22 views
3

Bir daire çevresindeki metin etiketlerinin konumunu nasıl hesaplayacağımı anlamaya çalışıyorum. Bu, ilk okuduğunda düşündüğünüzden biraz daha karmaşıktır .Etiketler çevre etrafında nasıl konumlandırılır?

Ben temel almak: Bana yarıçap dRadius ile açı Dangle daireyi üzerine gelin verecektir Yani

X = ptCenter.X + (dRadius * Math.Cos(dAngle * Math.PI/180.0)) 
Y = ptCenter.Y + (dRadius * Math.Sin(dAngle * Math.PI/180.0)) 

. Tabii ki, DrawText (herhangi bir çeşit, ama ben özellikle DrawingContext.DrawText kullanarak, sizin cevabınız için herhangi bir fark yaparsanız) metnin sol üst köşesi olarak verilen nokta ile çizer.

Sorun şu ki, bu, metni çizmek için doğru konum değil.

https://support.office.com/en-us/article/Present-your-data-in-a-radar-chart-16e20279-eed4-43c2-9bf5-29ff9b10601d

Oca yerine Mart yatay Nisan dikey olarak ortalanır ortasında tür gibi görünüyor Şubat alt soldan çekilir nokta etrafında yatay olarak ortalanır: Burada konunun resmidir

Etiketler, etiketler noktanın etrafına düzgün bir şekilde yerleştirilmiyor. Çizmenizin açısına bağlı.

Çizmem gereken etiket sayısı değişebilir, bu yüzden kodlamada sert kodlama faktörleri çıkmaktadır. Ben de çizmek gerekir açıları da değişebilir, bu yüzden orada da herhangi bir şekerleme faktörleri. Bütün bunların anında hesaplanması gerekiyor.

0, 90, 180, 270 özel durumlar gibi gözüküyor, diğerleri ise, noktanın etrafında dikey olarak yarı merkezli olarak görünüyor, ancak konuştuğunuz çemberin hangi tarafına göre sağa veya sola çiziliyor gibi görünüyor. hakkında?

Burada doğru yolda mıyım? Yoksa “bilinen” bir algoritma var mı?

Teşekkürler.

+0

Görünüşe göre, metnin orta noktasını hesaplamak zorunda kalacaksınız, ki bu elbette metne göre değişir .. [Here] (http://stackoverflow.com/questions/25308612/vertical-alignment-with- drawingcontext-drawtext) metnin kendisine göre olan şeyleri hesaplamanın bir örneğidir. [FormattedText] (https://msdn.microsoft.com/en-us/library/system.windows.media.formattedtext (v = vs.110) .aspx) sınıfı size ihtiyacınız olan verileri verecek gibi görünüyor. (Genişlik, WidthIncludingTrailingWhitespace, vb ..) – Quantic

cevap

3

İhtiyacınız olan şey, hesapladığınız noktaya yerleştirilecek metnin merkezidir. Bu yüzden, X yönünde yarı genişlikte ve Y'de yarım yükseklikte kaydırma yapmalısınız. Bu, etiketleri dairenin "içine" yerleştirecektir. Şunun gibi:

public class CircleText : FrameworkElement { 
    public string[] Labels 
    { 
     get { return (string[])GetValue(LabelsProperty); } 
     set { SetValue(LabelsProperty, value); } 
    } 

    public static readonly DependencyProperty LabelsProperty = 
     DependencyProperty.Register("Labels", typeof(string[]), typeof(CircleText), new PropertyMetadata(null, OnLabelsChanged)); 

    private static void OnLabelsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { 
     ((CircleText) d).InvalidateVisual(); 
    } 

    protected override void OnRender(DrawingContext drawingContext) { 
     if (Labels == null || Labels.Length == 0) 
      return; 
     var centerX = this.ActualWidth/2; 
     var centerY = this.ActualHeight/2; 
     var rad = Math.Min(this.ActualWidth/2, this.ActualHeight/2); 
     for (int i = 0; i < Labels.Length; i++) { 
      var angle = 360/(Labels.Length) * i; 
      var x = centerX + rad * Math.Cos(angle * Math.PI/180.0); 
      var y = centerY + rad * Math.Sin(angle * Math.PI/180.0); 
      FormattedText text = new FormattedText(
       Labels[i], 
       CultureInfo.GetCultureInfo("en-us"), 
       FlowDirection.LeftToRight, 
       new Typeface("Verdana"), 
       12, 
       Brushes.Black); 
      x -= text.Width/2; 
      y -= text.Height/2; 
      drawingContext.DrawText(text, new Point(x, y)); 
     } 

    } 
} 

Eğer noktalara çizgiler çizmek istiyorum ve etiketler bu çizgilerin dışında olmak istiyorsanız - Eğer zaten hesaplanmış cos ve sin değerlerine dayanarak etiketleri kaydırmaya gerek. Bu, "dış" etiketlerini şu şekilde konumlandıracaktır:

protected override void OnRender(DrawingContext drawingContext) { 
     if (Labels == null || Labels.Length == 0) 
      return; 
     var centerX = this.ActualWidth/2; 
     var centerY = this.ActualHeight/2; 
     var rad = Math.Min(this.ActualWidth/2, this.ActualHeight/2); 
     for (int i = 0; i < Labels.Length; i++) { 
      var angle = 360/(Labels.Length) * i; 
      var xshift = Math.Cos(angle * Math.PI/180.0); 
      var yshift = Math.Sin(angle * Math.PI/180.0); 
      var x = centerX + rad * xshift; 
      var y = centerY + rad * yshift; 
      drawingContext.DrawLine(new Pen(Brushes.Black, 1), new Point(centerX, centerY), new Point(x,y)); 
      FormattedText text = new FormattedText(
       Labels[i], 
       CultureInfo.GetCultureInfo("en-us"), 
       FlowDirection.LeftToRight, 
       new Typeface("Verdana"), 
       12, 
       Brushes.Black); 
      x -= (1 - xshift) * text.Width/2; 
      y -= (1 - yshift) * text.Height/2;     
      drawingContext.DrawText(text, new Point(x, y)); 
     }    
    } 

Tabii ki yukarıdaki etiketler için yukarıdaki işler.

+0

teşekkürler! Örnek kodunuzla birkaç dakikalığına oynadım (bir toplantı lol var), ancak etiketleri doğru noktalara yerleştirmek gibi görünüyor! Yukarı ve cevap olarak işaretlendi. – SledgeHammer

3

Haklısınız, sorun göründüğü kadar basit değil. kalem ve kağıt ile oturarak sonra sorun daha basit adımlara de-oluşabilir düşünüyorum:

enter image description here

  1. çevrenizden boyutlarını ve istenen marjı belirle (görüntü üzerinde: mavi daire kesik).
  2. Etiket metninin boyutunu belirleyin (resimdeki: Etiketi çevreleyen siyah çerçeve).
  3. Mavi kesikli daireye dokunması gereken (siyah) etiketli metin çerçevesini belirleyin.Her bir boyut için (X, Y) ayrı ayrı:
    • Metin etiketinin orta noktası, eksenin etiketinin yarısı boyutundaysa (resimdeki: turuncu çift satır içinde - dairenin üzerindeki etikete bakın) nokta
    • Aksi takdirde etiket metin çerçevesinin kenarına ile olup, nokta etiket metni çerçevenin köşe de olup. siyah çerçeve noktası mavi kesik çizgi temas edecek şekilde adım tespit
  4. 'taşı' dairenin ortasından etiketi. (resim üzerinde: dairenin ortasından etiketin ortasına kırmızı çizgi)
  5. Daireye temas eden noktaya göre etiketinizin sol üst noktasını belirleyin ve etiketi sonuçta çizin.

Üzgünüz, burada formül yok, bunları oluşturmak için daha fazla zamana ihtiyacım var. Ancak, buradaki konumlandırma etiketlerinin, iki satırlık kodla sin() ve 'un basit değerlendirmesinden biraz daha fazlasını aldığını göstermek istedim.

+1

Güzel yanıt. Yine de problemin daha basit olduğunu düşünüyorum. Dikkat etmeniz gereken 8 olası noktanız vardır: köşeler ve etiketin sınırlayıcı dikdörtgeni çizgilerinin merkezleri. Dairenin çevresindeki açıya göre kutunun konumunu hesaplayın, böylece ilgili nokta daire üzerindedir. Örneğin 0 açısı için sol noktayı önemsersiniz, PI/4 açısı için sol üst noktayı önemsersiniz ve PI/2 için en üst noktayı dikkate alırsınız. –

+0

@SideriteZackwehdex - 8'den fazla noktanız var: etiketin eksene yakın olması durumunda 4 kenar ve her kenardaki noktaların sonsuzluğu (sadece 4 değil). Bir etiketi tam olarak eksenin merkezinde düşünün ve hareket ettirmeye başlayın. İşaretleme noktası, eksenle kesişen kesilene kadar köşe olmaz (görselleştirme iyi görünüyorsa). – miroxlav

+0

Dairenin etrafında dönen etiketi gerçekte canlandırmak istemezseniz, özellikle de açıkladığınız gibi, bir kenar boşluğu ile yaklaşık olarak doğru olurdum. Aslında, sadece çeyrek geçiş yaparken "atlama" etiketi ile tamam iseniz, köşelere ihtiyacınız olabilir. Tabii ki, bir yerlerde matematiksel bir formül olmalı, ama gerçekten gerekli mi? –