drawing text along a curved line?

newguy

Well-known member
Joined
Jun 30, 2008
Messages
611
Location
Denver Co, USA
Programming Experience
1-3
Working on drawing text along a curved line that contains 4 points. The text part works, but as I change/move the points the text has different spacings B/T the letters. I would like to see if I could get the text to remain the same spacing - any ideas on how I would change this code? Or if this it is poss to fix this. Here is the sub I am using:
VB.NET:
Private Sub DrawTextOnPath(ByVal target As Graphics, _
                ByVal baseLine As GraphicsPath, ByVal text As String, ByVal fnt As Font, ByVal col As Color)
    'SUB: Draws a string that has been fitted to a base curve defined inside the graphicspath
    'the effect is similar to WordArt in MS Word.  This assumes that there is a curve in the path object.
    Try
      Dim x, y, dx, dy As Single
      Dim letters As Char() = text.ToCharArray()
      Dim fontScale As Single = fnt.Size * fnt.FontFamily.GetCellAscent(fnt.Style) / fnt.FontFamily.GetEmHeight(fnt.Style)
      Dim m As Matrix = New Matrix
      'break up the smooth curve into a sequence of straight lines (tight curves may become jagged)
      baseLine.Flatten(m, DEFAULT_FLATNESS)
      'the matrix is just a default one
      'upper bound for loop counter
      Dim upper As Integer = Math.Min(baseLine.PathPoints.Length - 2, text.Length - 1)
      Dim scale As Single = 0
      Dim textBrush As Brush = New SolidBrush(col)
      With baseLine
        For i As Integer = 0 To upper
          x = .PathPoints(i).X
          y = .PathPoints(i).Y
          'find the normal by swapping the coords and negating the final y falue
          dx = .PathPoints(i + 1).Y - y
          dy = x - .PathPoints(i + 1).X
          ''dx = .PathPoints(i + 1).Y - y
          ''dy = x - .PathPoints(i + 1).X
          'change the dx, dy vector into a unit vector ("normalize" the normal)
          scale = (fontScale / CType(Math.Sqrt((dx * dx) + (dy * dy)), Single))
          dx *= scale
          dy *= scale
          m.Reset()
          'position the character
          m.Translate(x + dx, y + dy)
          'rotate the character to align it to the curve
          m.Rotate(CType((Math.Atan2(dy, dx) * TO_DEGREES) + 90, Single))
          target.Transform = m                            'apply the transformation to the target surface
          'draw a single character onto the target surface
          target.DrawString(letters(i), fnt, textBrush, 0, 0)
        Next i
      End With
    Catch
    End Try
End Sub
 

Attachments

  • myLine.jpg
    myLine.jpg
    3.9 KB · Views: 87

VicJ

Well-known member
Joined
Aug 12, 2008
Messages
79
Programming Experience
5-10
Hi Newguy,

It looks like you are drawing each letter on a line base from one PathPoint to the next -- and the Pathpoints aren't uniformly spaced. You might improve it by playing with the Flattening value. For example, if you set it to a small value, say 0.1 (compared to the default 0.25) the points will be spaced close together and fairly uniformly. Then you could pick out, say, every 10th point to use as a ruler for laying out the text. I don't know if that would work well but it might be worth a try.

Still, have you looked at this article?

Warping Text To Bézier curves

It answers exactly your question, and in a lot of detail. But it might be a bit complicated and you have to brave the curly braces.

bye, BB
 

newguy

Well-known member
Joined
Jun 30, 2008
Messages
611
Location
Denver Co, USA
Programming Experience
1-3
Yeah, I have followed the code and did know that the points path moved a lot. I have worked with the flattening value which does help some. I will look into your link soon. I'm not scared of curly braces =) Thanks for your help VicJ!
 

newguy

Well-known member
Joined
Jun 30, 2008
Messages
611
Location
Denver Co, USA
Programming Experience
1-3
I am having trouble muddling thru this code - some unknown variables - what do they belong to? (the stuff in red)
VB.NET:
string text = "Some text to wrap";

[[ Calculate coefficients A thru H from the control points ]]

GraphicsPath textPath = new GraphicsPath();

// the baseline should start at 0,0, so the next line is not quite correct
path.AddString(text, someFont, someStyle, someFontSize, new Point(0,0));

RectangleF textBounds = textPath.GetBounds();
 
for (int i =0; i < textPath.PathPoints.Length; i++)
{
    PointF pt = textPath.PathPoints[i];
    float textX = pt.X;
    float textY = pt.Y;
    
    // normalize the x coordinate into the parameterized value
    // with a domain between 0 and 1.
    float t =  textX / textBounds.Width;  
       
    // calculate spline point for parameter t
   [COLOR="Red"] float Sx = At3 + Bt2 + Ct + D 
    float Sy = Et3 + Ft2 + Gt + H [/COLOR]
        
    // calculate the tangent vector for the point        
   [COLOR="Red"] float Tx = 3At2 + 2Bt + C 
    float Ty = 3Et2 + 2Ft + G [/COLOR]
    // rotate 90 or 270 degrees to make it a perpendicular
    float Px =   Ty
    float Py = - Tx
    
    // normalize the perpendicular into a unit vector
    float magnitude = sqrt(Px2 + Py2)
    Px = Px / magnitude
    Py = Py / magnitude
    
    // assume that input text point y coord is the "height" or 
    // distance from the spline.  Multiply the perpendicular vector 
    // with y. it becomes the new magnitude of the vector.
    Px *= textY;
    Py *= textY;
    
    // translate the spline point using the resultant vector
    float finalX = Px + Sx
    float finalY = Py + Sy
    
    // I wish it were this easy, actually need 
    // to create a new path.
    textPath.PathPoints[i] = new PointF(finalX, finalY);
}

// draw the transformed text path		
g.DrawPath(Pens.Black, textPath);

// draw the Bézier for reference
g.DrawBezier(Pens.Black, P0,  P1,  P2,  P3);
 

VicJ

Well-known member
Joined
Aug 12, 2008
Messages
79
Programming Experience
5-10
er ... are you asking me, newguy? Hey, I'm the one with the curly brace phobia! Besides, I didn't write that article. I didn't even read it properly. I skimmed through it a while back and bookmarked it as something to study after my next brain transplant.

I wonder what language that is. Java perhaps? Looking at it as a kind of private pseudocode (which it may be) I guess that this:

float Ty = 3Et2 + 2Ft + G

could stand for something like this in VB notation:

Dim Ty As Double = (3 * E * t ^ 2) + (2 * F * t) + G

Does that make sense?

Vic

EDIT: I am sure MattP points the right way. PS. why do so many people on this forum have a login name in that format? In my case it was pure chance because I didn't notice it until after I signed up.
 

newguy

Well-known member
Joined
Jun 30, 2008
Messages
611
Location
Denver Co, USA
Programming Experience
1-3
@VicJ, well I am not scared so much as the braces makes my dyslexia go haywire. As for the math I was thinking something along those lines, but I don't normally do crazy math like this anymore (probably forgot some of it). @MattP I did find that and boy did that make my brain hurt =) I will try to work thru this some more. Everything I need is right there, I just have to hunker down and trudge thru it. Thank you both for your time.
 

newguy

Well-known member
Joined
Jun 30, 2008
Messages
611
Location
Denver Co, USA
Programming Experience
1-3
Ok, I have not been able to decipher this equation, mainly where those variables would come from/go. Any help would be great, actually incredible!
 

VicJ

Well-known member
Joined
Aug 12, 2008
Messages
79
Programming Experience
5-10
I noticed that somewhere else on that website Jay says he is using a language called "processing":

Processing is an "open source programming language and environment for people who want to program images, animation, and interactions". Based on Java, it is cross platform. The libraries are high-level, easy to use, and fun to play around with.

I guess that applies to this code too. I wonder whether those libraries can be ported to DotNet? It might be worth looking into. At least you could look at them to get an idea what is going on in the code.

p.s. I'm very interested in this subject because it's something I've long wanted to do myself -- when I get the time.

all the best, Vic

edit: you can download Processing from here: Download \ Processing 1.0. I just did!
 
Last edited:

newguy

Well-known member
Joined
Jun 30, 2008
Messages
611
Location
Denver Co, USA
Programming Experience
1-3
Sorry I did not mean to come off that way. It's looks like quite a handful and you said you wanted to learn it - wow - hats off to you. And no I did not find anything yet in there that will help me. I guess I am not meant to learn this - just not finding the tools to get me going.
 

VicJ

Well-known member
Joined
Aug 12, 2008
Messages
79
Programming Experience
5-10
No offence taken: I'd have added a wink smiley except I don't like this forum's gloopy looking bunch! I should have looked more carefully myself before getting enthusiastic about "processing". As to Jay's paper I think it gives some useful ideas theory of text bending, but his code can't be more than a rough guide. There are other useful sources though. For example have you seen this stuff on Bezier curves in the CodeProject: here and here? I think the information is all out there but it will take a lot of work to piece it all together.

Vic
 

newguy

Well-known member
Joined
Jun 30, 2008
Messages
611
Location
Denver Co, USA
Programming Experience
1-3
Thanks again VicJ, I will keep looking into this. One thought, am I supposed to make an array of the PathPoints and have a function that changes them based on that equation and resubmits it for drawing?
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,604
Location
Norway
Programming Experience
10+

hufftracey

New member
Joined
Apr 11, 2011
Messages
1
Programming Experience
1-3
Has any figured out a way to arch text? I am working on a project that requires text from a string to be arched along the top of an oval. Any ideas?
 

VicJ

Well-known member
Joined
Aug 12, 2008
Messages
79
Programming Experience
5-10
Have you tried the link JohnH posted? I haven't looked at the code but the results look very good. I think you would only have to make the path _gp in the author's Usage Example in a different way. Instead of defining the path from an array of points, you should use _gp.AddArc with arguments to define the ellipse segment you want to use.

Alternatively you could try a method I posted (as my alter ego boops boops) for deforming text into a curved shape. In that example the text has a pincushion shape with an ellipse at the top and bottom. It works by adding text to a graphics path with GraphicsPath.AddString and then modifying the path's PathPoints with a trig function. It's not fast, but I like the results. You could get an arched shape by replacing the line that sets the value of newY, for example to this:
VB.NET:
	Dim newY = pf.Y + 200 * (1 - Math.Cos(pf.X / w))
You would have to experiment with the values to get the particular shape you want, and it will also depend on the font you use.

Vic
 
Top Bottom