Mirror

Draw text alongside a curve (Views: 100)


Problem/Question/Abstract:

How to draw text alongside a curve

Answer:

You need a free rotatable text and the exact measurement of fonts. I use the ATM metrix, but you can also try textwidth or textheight. I use this code under Windows NT to draw symbols along a polygon line.

procedure TDrawer.fOutSymbolXYW(w: Double; x, y: Integer; S: string);
var
  nx, ny, xw, xh: Integer;
  O, T: TXForm;
  sc, cHg, fHg: Double;
begin
  nx := 0;
  ny := 0;
  cHg := fFontMetrix.fY2 - fFontMetrix.fY1; {I use Adobe one}
  fHg := TextHeight(S);
  if fHG = 0 then
    Exit;
  Sc := cHg / fHg;
  xw := OperateFont(Textwidth(S));
  xh := OperateFont(SymbolHeight(S));
  case TextJust of
    tjCenterTop:
      begin
        nx := -xw div 2;
        ny := 0;
      end;
    tjCenterBottom:
      begin
        nx := -xw div 2;
        ny := -xh;
      end;
    tjCenterCenter:
      begin
        nx := -xw div 2;
        ny := -xh div 2;
      end;
    tjLeftTop:
      begin
        nx := 0;
        ny := 0;
      end;
    tjLeftCenter:
      begin
        nx := 0;
        ny := -xh div 2;
      end;
    tjLeftBottom:
      begin
        nx := 0;
        ny := -xh;
      end;
    tjRightCenter:
      begin
        nx := -xw;
        ny := -xh div 2;
      end;
    tjRightTop:
      begin
        nx := -xw;
        ny := 0;
      end;
    tjRightBottom:
      begin
        nx := -xw;
        ny := -xh;
      end;
  end;
  SetGraphicsMode(TheDraw.Handle, GM_Advanced);
  T.eM11 := 1 * Cos(w / 360 * Pi * 2);
  T.eM22 := 1 * Cos(w / 360 * Pi * 2);
  T.eM12 := 1 * Sin(w / 360 * Pi * 2);
  T.eM21 := 1 * -Sin(w / 360 * Pi * 2);
  T.eDX := X;
  T.eDY := Y;
  GetWorldTransform(TheDraw.Handle, O);
  ModifyWorldTransform(TheDraw.Handle, T, MWT_LEFTMULTIPLY);
  { TheDraw.Pen.Style := psClear;
  TheDraw.Rectangle(nx - 1, ny - 1, nx + xw + 3, ny + xh + 2); }
  TheDraw.TextOut(nx + OperateFont(FFontMetrix.fX1 / SC), ny -
    OperateFont(TextHeight(S)
    - SymbolHeight(S) + FFontMetrix.fY1 / sc), S);
  { SetPen(0, 200, 0, 0. 25, psSolid);
  TheDraw.Ellipse(nx - 1, ny - 1, nx + 1, ny + 1); }
  T.eM11 := 1;
  T.eM22 := 1;
  T.eM12 := 0;
  T.eM21 := 0;
  T.eDX := 0;
  T.eDY := 0;
  SetWorldTransform(TheDraw.Handle, O);
end;

procedure TDrawer.SymbolLine(Poly: TXYPointList; Distance: Double; Offset: Double;
  StartAngle: Double; R, G, B: Byte; Lib: string; CharSet: Byte; Size: Double; Style:
    TFontStyles;
  Sign: Char);
var
  i, Segment: Integer;
  PosX, PosY, TargetLength, CurrentLength: Double;
  {P, pxy: TXYpoint;}
  s, c: Double;
  Angle: Double;

  {Locates the angle of symbol at one linepoint}
  procedure LANGLE(j: Integer; var s, c: Double);
  var
    x1, x2, y1, y2, l: Double;
  begin
    x1 := Poly.Points[j].x;
    x2 := Poly.Points[j - 1].x;
    y1 := Poly.Points[j].y;
    y2 := Poly.Points[j - 1].y;
    l := sqrt(sqr(x2 - x1) + sqr(y2 - y1));
    s := (x1 - x2) / l;
    c := (y2 - y1) / l;
  end;

  {Llocates the angle of symbol between to lines linepoints}
  procedure SLANGLE(j: Integer; var s, c: Double);
  var
    x1, x2, y1, y2, l: Double;
  begin
    x1 := Poly.Points[j - 1].x;
    x2 := Poly.Points[j + 1].x;
    y1 := Poly.Points[j - 1].y;
    y2 := Poly.Points[j + 1].y;
    l := sqrt(sqr(x2 - x1) + sqr(y2 - y1));
    s := (x1 - x2) / l;
    c := (y2 - y1) / l;
  end;

  function Place(L: Double; var x, y: Double; var Pl: Double; var Index: integer;
    var s, c: Double): Boolean;
  var
    x1, x2, y1, y2: Real;
    l1, l2: Real;
    j: Integer;
  begin
    Place := False;
    if L < 0 then
      Exit;
    j := index;
    while (l >= Pl) and (j < Poly.MaxPoint) do
    begin
      inc(j);
      x1 := Poly.Points[j - 1].x;
      x2 := Poly.Points[j].x;
      y1 := Poly.Points[j - 1].y;
      y2 := Poly.Points[j].y;
      pl := pl + sqrt(sqr(x2 - x1) + sqr(y2 - y1));
    end;
    if not (l < Pl) and (j >= Poly.MaxPoint) then
      Exit;
    if (l = pl) then
    begin
      X := Poly.Points[j].X;
      Y := Poly.Points[j].Y;
      if j = Poly.MaxPoint then
        LAngle(j, s, c)
      else if j = 1 then
        LAngle(2, s, c)
      else
        SLAngle(j, s, c);
      Exit;
    end;
    x1 := Poly.Points[j - 1].x;
    x2 := Poly.Points[j].x;
    y1 := Poly.Points[j - 1].y;
    y2 := Poly.Points[j].y;
    l1 := sqrt(sqr(x2 - x1) + sqr(y2 - y1));
    if j < 3 then
      l2 := l
    else
      l2 := l - (Pl - l1);
    x := l2 / l1 * (x2 - x1) + Poly.Points[j - 1].x;
    y := l2 / l1 * (y2 - y1) + Poly.Points[j - 1].y;
    if j <> index then
      LANGLE(j, s, c);
    index := j;
    Place := True;
  end;

begin
  SetSymbols(R, G, B, LIB, Charset, Size, Style);
  if Distance = 0 then
    Distance := 1;
  CurrentLength := 0;
  TargetLength := Poly.PolyLength;
  Segment := 1;
  i := -1;
  if (Poly.MaxPoint < 2) or (Poly.PolyLength < Distance + Offset) then
    Exit;
  repeat
    Inc(i);
    if Place(Offset + Distance * i, PosX, PosY, CurrentLength, Segment, S, C) then
    begin
      Angle := ArcTan2(S, C) / 2 / Pi * 360;
      OutSymbolXYW(Angle + StartAngle, PosX, PosY, Sign);
    end;
  until
    Offset + Distance * i >= TargetLength;
end;

<< Back to main page