2012-11-01 2 views
3

J'ai une ligne brisée (PointF[]), certains string et Graphics objet. Maintenant, je veux dessiner cette chaîne sur ma ligne.Texte sur la ligne prédéfinie

Voici un exemple:

enter image description here

Y a-t-il des algorithmes pour le faire de la manière la plus facile?

[edit] Ok, j'ai essayé le code de @ endofzero et je l'ai modifié un peu. Voici la solution complète (avec des calculs d'angle et de distance):

private static void DrawBrokenString(Graphics g, IList<PointF> line, string text, Font font) 
{ 
    g.SmoothingMode = SmoothingMode.AntiAlias; 
    g.TextRenderingHint = TextRenderingHint.AntiAlias; 
    Pen pen = new Pen(Brushes.Black); 

    for (int index = 0; index < line.Count - 1; index++) 
    { 
     float distance = GetDistance(line[index], line[index + 1]); 

     if (text.Length > 0) 
     { 
      for (int cIndex = text.Length; cIndex >= 0; cIndex--) 
      { 
       SizeF textSize = g.MeasureString(text.Substring(0, cIndex).Trim(), font); 
       if (textSize.Width <= distance) 
       { 
        float rotation = GetAngle(line[index], line[index + 1]); 

        g.TranslateTransform(line[index].X, line[index].Y); 
        g.RotateTransform(rotation); 

        if (index != line.Count - 2) 
        { 
         g.DrawString(text.Substring(0, cIndex).Trim(), font, new SolidBrush(Color.Black), 
           new PointF(0, -textSize.Height)); 
        } 
        else 
        { 
         g.DrawString(text.Trim(), font, new SolidBrush(Color.Black), 
           new PointF(0, -textSize.Height)); 
        } 

        g.RotateTransform(-rotation); 
        g.TranslateTransform(-line[index].X, -line[index].Y); 

        text = text.Remove(0, cIndex); 
        break; 
       } 
      }  
     } 
     else 
     { 
      break; 
     } 

     g.DrawLine(pen, line[index].X, line[index].Y, line[index + 1].X, line[index + 1].Y); 
    } 
} 

private static float GetDistance(PointF p1, PointF p2) 
{ 
    return (float) Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); 
} 

private static float GetAngle(PointF p1, PointF p2) 
{ 
    float c = (float) Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); 

    if (c == 0) 
     return 0; 

    if (p1.X > p2.X) 
     return (float) (Math.Asin((p1.Y - p2.Y)/c)*180/Math.PI - 180); 

    return (float) (Math.Asin((p2.Y - p1.Y)/c)*180/Math.PI); 
} 

Maintenant je dois la seule chose à finir mon problème. Je ne veux pas que les chaînes se chevauchent. Des idées? Ahhh, et quand nous ne pouvons pas tirer une chaîne sur le chemin (en raison du montant trop de pauses, il doit être établi au-dessus de la ligne (en haut au milieu)

est ici l'exemple de chevauchement indésirable.

enter image description here

+2

a été la chaîne d'entrée « Certains texte sur la ligne » ou [ « Une partie du texte », « sur la ligne »]? –

+0

Je récupère la chaîne entière, alors « Une partie du texte sur la ligne »: SI doivent scinder moi – Nickon

+1

Vous pouvez dessiner le texte pivotée à l'aide DrawString après une RotateTransform appropriée (plus ici: http://msdn.microsoft.com/en -us/bibliothèque/a0z3f662 (v = VS.100) .aspx) et on peut voir la largeur d'une chaîne en utilisant la méthode de MeasureString (http://msdn.microsoft.com/en-us/library/system.drawing. graphics.measurestring (v = vs.100) .aspx). Cela peut vous aider à diviser le texte – BlackBear

Répondre

1

Voici un exemple simple de la façon de dessiner le texte suivant une ligne brisée.

private static void DrawBrokenLine(PointF[] line, string text, Graphics g, Font font) 
    { 
     for (int index = 0; index < line.Length - 1; index++) 
     { 
      float distance = distanceBetween(line[index], line[index + 1]); 

      if (text.Length > 0) 
      { 
       for (int cIndex = text.Length; cIndex >= 0; cIndex--) 
       { 
        SizeF textSize = g.MeasureString(text.Substring(0, cIndex).Trim(), font); 
        if (textSize.Width <= distance) 
        { 
         float rotation = angleBetween(line[index], line[index + 1]); 

         g.TranslateTransform(line[index].X, line[index].Y); 
         g.RotateTransform(rotation); 

         g.DrawString(text.Substring(0, cIndex).Trim(), font, new SolidBrush(Color.Black), 
          new PointF(0, -textSize.Height)); 

         g.RotateTransform(-rotation); 
         g.TranslateTransform(-line[index].X, -line[index].Y); 

         text = text.Remove(0, cIndex); 
         break; 
        } 
       } 
      } 
      else 
       break; 
     } 
    } 

Si vous voulez qu'il pause seulement à des espaces entre les mots, vous devrez changer la façon dont il raccourcit la chaîne lorsqu'elle mesure

EDIT

Pour corriger le chevauchement de texte sur les coins, j'ai ajouté un calcul pour elle:

private static void DrawBrokenLine(PointF[] line, string text, Graphics g, Font font) 
    { 
     float lastOverlap = 0; 

     for (int index = 0; index < line.Length - 1; index++) 
     { 
      float distance = distanceBetween(line[index], line[index + 1]); 

      float angleOfLines = 180; 
      if (index < line.Length - 2) 
      { 
       angleOfLines = angleBetweenLines(line[index], line[index + 1], line[index + 2]); 
      } 

      if (text.Length > 0) 
      { 
       SizeF textSize = g.MeasureString(text, font); 
       float overlap = 0; 
       if (angleOfLines < 180) 
       { 
        if (angleOfLines <= 90) 
        { 
         overlap = (textSize.Height/1.5f)/(
          Convert.ToSingle(Math.Tan((angleOfLines/2) * Math.PI/180)) 
          ); 
        } 
        else 
        { 
         overlap = Convert.ToSingle(
          Math.Sin(angleOfLines * Math.PI/180) * (textSize.Height/1.5f)); 
        } 
       } 

       for (int cIndex = text.Length; cIndex >= 0; cIndex--) 
       { 
        textSize = g.MeasureString(text.Substring(0, cIndex).Trim(), font); 

        if (textSize.Width <= distance - overlap - lastOverlap) //notice the subtraction of overlap 
        { 
         float rotation = angleBetween(line[index], line[index + 1]); 

         g.TranslateTransform(line[index].X, line[index].Y); 
         g.RotateTransform(rotation); 

         g.DrawString(text.Substring(0, cIndex).Trim(), font, new SolidBrush(Color.Black), 
          new PointF(lastOverlap, -textSize.Height)); 

         g.RotateTransform(-rotation); 
         g.TranslateTransform(-line[index].X, -line[index].Y); 

         text = text.Remove(0, cIndex); 
         break; 
        } 
       } 

       lastOverlap = overlap; 
      } 
      else 
       break; 
     } 
    } 

    private static float angleBetweenLines(PointF A, PointF B, PointF C) 
    { 
     float angle1 = 360 - angleBetween(A, B); 
     float angle2 = 360 - angleBetween(B, C); 

     if (angle1 < 0) 
      angle1 += 360; 
     if (angle2 < 0) 
      angle2 += 360; 

     float delta = 180 + angle1 - angle2; 

     if (delta < 0) 
      delta += 360; 
     if (delta > 360) 
      delta -= 360; 

     return delta; 
    } 

enter image description here

Il y a une mise en garde avec mon exemple: si vous remarquez, je hauteur du texte divisé par un nombre spécial. J'ai eu des problèmes avec MeasureString ne pas renvoyer une hauteur précise pour le texte, alors j'ai essayé de corriger cela manuellement. Concernant le dessin d'une chaîne au-dessus de la ligne (en raison de trop de pauses), cela dépend de la façon dont vous voulez détecter cette situation (qu'est-ce qui constitue un trop grand nombre, où placer le texte? . Peut-être que si vous pouvez donner un exemple d'image de cette situation, vous avez en tête que cela aiderait à clarifier.

+0

Merci, ça fonctionne :) J'ai presque fait moi-même, mais il y avait un X-Y-offset qui est apparu à cause des transformations mauvaises de la matrice: P (mon mauvais). Mais vos solutions fonctionnent bien :) Pourriez-vous vérifier ma modification, s'il vous plaît? – Nickon

+1

@Nickon Mise à jour de ma réponse concernant le chevauchement de texte. – endofzero

+0

Dernière chose: D Que faire s'il y a une ligne brisée qui contient des centaines de sous-pixels de 2-3 pixels? Y a-t-il un moyen de le modifier? Ou devrais-je essayer un algorithme?: S – Nickon

Questions connexes