Homepage von Thomas Schwobe
Home Software Quelltext Impressum Datenschutzerklärung

Quelltext:

unit FontUnit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    TrackBar1: TTrackBar;
    TrackBar2: TTrackBar;
    TrackBar3: TTrackBar;
    FontDialog1: TFontDialog;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure TrackBar2Change(Sender: TObject);
    procedure TrackBar3Change(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TVector = array of Extended;

  TMatrix = record
    cols: Integer;
    rows: Integer;
    data: array of TVector;
  end;

  TPoint3D = record
    X, Y, Z: Extended;
  end;

var
  polytext: string = '3D Text';
  polypoints: array of TPoint;
  polypoints2D: array of TPoint;
  polypoints3D: array of TPoint3D;
  polytypes: array of Byte;

  P0, Pox, Poy, Poz, Px, Py, Pz: TPoint3D;

function clearmatrix: TMatrix;
begin
  result.cols:=0;
  result.rows:=0;
  result.data:=nil;
end;

function creatematrix(cols, rows: Integer): TMatrix;
var i, j: Integer;

begin
  result.cols:=cols;
  result.rows:=rows;
  SetLength(result.data, cols);

  for i:=0 to cols-1 do
  begin
    SetLength(result.data[i], rows);

    for j:=0 to rows-1 do
      result.data[i, j]:=0;
  end;
end;

procedure destroymatrix(var mx: TMatrix);
var i: Integer;

begin
  for i:=0 to mx.cols-1 do
    Finalize(mx.data[i]);

  Finalize(mx.data);

  mx:=clearmatrix;
end;

procedure fillmatrix(var mx: TMatrix; value: Extended);
var i, j: Integer;

begin
  for j:=0 to mx.rows-1 do
    for i:=0 to mx.cols-1 do
      mx.data[i, j]:=value;
end;

function multmatrix(mxA, mxB: TMatrix): TMatrix;
var ai, aj, bi, bj: Integer;

begin
  result:=clearmatrix;

  if (mxA.cols<>mxB.rows) then
    Exit;

  result:=creatematrix(mxB.cols, mxA.rows);

  fillmatrix(result, 0);

  for aj:=0 to mxA.rows-1 do
    for bi:=0 to mxB.cols-1 do
      for ai:=0 to mxA.cols-1 do //weil: mxA.cols=mxB.rows
      begin
        bj:=ai;
        result.data[bi, aj]:=result.data[bi, aj]+mxA.data[ai, aj]*mxB.data[bi, bj];
      end
end;

//

function XSin(angle: Extended): Extended;
begin
  result:=Sin((angle*Pi)/180);
end;

function XCos(angle: Extended): Extended;
begin
  result:=Cos((angle*Pi)/180);
end;

//

function Point3D(X, Y, Z: Extended): TPoint3D;
begin
  result.X:=X;
  result.Y:=Y;
  result.Z:=Z;
end;

//

function Point3DToPoint2D(Point3D: TPoint3D): TPoint;
begin
  result.x:=Round(Point3D.X);
  result.y:=Round(Point3D.Y);
end;

//

function XRotateMatrix(angle: Extended): TMatrix;
begin
  result:=creatematrix(4, 4);

  with result do
  begin
    data[0, 0]:=1;
    data[0, 1]:=0;
    data[0, 2]:=0;
    data[0, 3]:=0;

    data[1, 0]:=0;
    data[1, 1]:=XCos(angle);
    data[1, 2]:=-XSin(angle);
    data[1, 3]:=0;

    data[2, 0]:=0;
    data[2, 1]:=XSin(angle);
    data[2, 2]:=XCos(angle);
    data[2, 3]:=0;

    data[3, 0]:=0;
    data[3, 1]:=0;
    data[3, 2]:=0;
    data[3, 3]:=1;
  end;
end;

function YRotateMatrix(angle: Extended): TMatrix;
begin
  result:=creatematrix(4, 4);

  with result do
  begin
    data[0, 0]:=XCos(angle);
    data[0, 1]:=0;
    data[0, 2]:=XSin(angle);
    data[0, 3]:=0;

    data[1, 0]:=0;
    data[1, 1]:=1;
    data[1, 2]:=0;
    data[1, 3]:=0;

    data[2, 0]:=-XSin(angle);
    data[2, 1]:=0;
    data[2, 2]:=XCos(angle);
    data[2, 3]:=0;

    data[3, 0]:=0;
    data[3, 1]:=0;
    data[3, 2]:=0;
    data[3, 3]:=1;
  end;
end;

function ZRotateMatrix(angle: Extended): TMatrix;
begin
  result:=creatematrix(4, 4);

  with result do
  begin
    data[0, 0]:=XCos(angle);
    data[0, 1]:=-XSin(angle);
    data[0, 2]:=0;
    data[0, 3]:=0;

    data[1, 0]:=XSin(angle);
    data[1, 1]:=XCos(angle);
    data[1, 2]:=0;
    data[1, 3]:=0;

    data[2, 0]:=0;
    data[2, 1]:=0;
    data[2, 2]:=1;
    data[2, 3]:=0;

    data[3, 0]:=0;
    data[3, 1]:=0;
    data[3, 2]:=0;
    data[3, 3]:=1;
  end;
end;

procedure Rotate(alpha, beta, gamma: Extended; flag: Boolean);
var i: Integer;
    Rx, Ry, Rz, Rt, Rm, Xm, Xim, Ym, Yim, Zm, Zim: TMatrix;

    procedure FillM(var M: TMatrix; P: TPoint3D);
    begin
      M.data[0, 0]:=P.X;
      M.data[0, 1]:=P.Y;
      M.data[0, 2]:=P.Z;
      M.data[0, 3]:=0;
    end;

    function TurnP(P: TPoint3D): TPoint3D;
    var M, Mi: TMatrix;

    begin
      M:=creatematrix(1, 4);
      FillM(M, P);
      Mi:=multmatrix(Rm, M);

      result:=Point3D(Mi.data[0, 0], Mi.data[0, 1], Mi.data[0, 2]);

      destroymatrix(M);
      destroymatrix(Mi);
    end;

begin
  Rx:=XRotateMatrix(alpha);
  Ry:=YRotateMatrix(beta);
  Rz:=ZRotateMatrix(gamma);

  Rt:=multmatrix(Rx, Ry);
  Rm:=multmatrix(Rt, Rz);

  if flag then
  begin
    Px:=Pox;
    Py:=Poy;
    Pz:=Poz;

    Px:=TurnP(Px);
    Py:=TurnP(Py);
    Pz:=TurnP(Pz);
  end
  else
  begin
    SetLength(polypoints3D, Length(polypoints));

    for i:=0 to High(polypoints) do
      polypoints3D[i]:=TurnP(Point3D(polypoints[i].x, polypoints[i].y, 0));
  end;
  
  destroymatrix(Rx);
  destroymatrix(Ry);
  destroymatrix(Rz);
  destroymatrix(Rt);
  destroymatrix(Rm);
end;

//

procedure Line(Canvas: TCanvas; pt1, pt2: TPoint);
begin
  Canvas.MoveTo(pt1.X, pt1.Y);
  Canvas.LineTo(pt2.X, pt2.Y);
end;

//

procedure FontToPolygon(Font: TFont; s: string);
var i: Integer;
    b: Byte;

    pt: TPoint;

    bmp: TBitmap;

begin
  bmp:=TBitmap.Create;

  BeginPath(bmp.Canvas.Handle);
  bmp.Canvas.Font:=Font;
  bmp.Canvas.Brush.Style:=bsClear;
  bmp.Canvas.TextOut(0, 0, s);
  EndPath(bmp.Canvas.Handle);

  FlattenPath(bmp.Canvas.Handle); //Glätten

  pt:=Point(0, 0); b:=0;

  i:=GetPath(bmp.Canvas.Handle, @pt, @b, 0);

  SetLength(polypoints, i);
  SetLength(polytypes, i);

  GetPath(bmp.Canvas.Handle, @polypoints[0], @polytypes[0], i);

  AbortPath(bmp.Canvas.Handle);

  bmp.Free;
end;

function PolygonToRgn: HRGN;
var i, j: Integer;

    rgn: HRGN;

begin
  i:=0; j:=0; result:=0;

  while i<=High(polypoints2D) do
  begin
    if (polytypes[i] and PT_CLOSEFIGURE)<>0 then
    begin
      if result=0 then
        result:=CreatePolygonRgn(polypoints2D[j], i-j+1, WINDING)
      else
      begin
        rgn:=CreatePolygonRgn(polypoints2D[j], i-j+1, WINDING);
        CombineRgn(result, result, rgn, RGN_XOR);
        DeleteObject(rgn);
      end;

      j:=i+1;
    end;

    inc(i);
  end;
end;

//

procedure TForm1.FormCreate(Sender: TObject);
begin
  P0:=Point3D(0, 0, 0);
  Pox:=Point3D(200, 0, 0);
  Poy:=Point3D(0, 200 ,0);
  Poz:=Point3D(0, 0, 200);
  Px:=Point3D(200, 0, 0);
  Py:=Point3D(0, 200, 0);
  Pz:=Point3D(0, 0, 200);

  FontToPolygon(Font, polytext);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Finalize(polypoints);
  Finalize(polypoints2D);
  Finalize(polypoints3D);
  Finalize(polytypes);
end;

procedure TForm1.FormPaint(Sender: TObject);
var i: Integer;
    pt0, ptx, pty, ptz: TPoint;
    rgn: HRGN;

begin
  Canvas.Brush.Color:=clBtnFace;
  Canvas.FillRect(ClientRect);
  
  //Achsen
  
  SetMapMode(Canvas.Handle, MM_ISOTROPIC);
  SetWindowExtEx(Canvas.Handle, ClientWidth, ClientHeight, nil); //darzustellende Punkte x,y
  SetViewportExtEx(Canvas.Handle, ClientWidth, -ClientHeight, nil); //kippen an
  SetViewportOrgEx(Canvas.Handle, ClientWidth div 2, ClientHeight div 2, nil); //Nullpunkt

  //Punkte rotieren

  Rotate(-TrackBar1.Position, TrackBar2.Position, TrackBar3.Position, True);

  //Punkte umrechnen

  pt0:=Point3DToPoint2D(P0);
  ptx:=Point3DToPoint2D(Px);
  pty:=Point3DToPoint2D(Py);
  ptz:=Point3DToPoint2D(Pz);

  //Zeichnen

  Canvas.Pen.Color:=clBlack;

  Line(Canvas, pt0, ptx); Canvas.TextOut(ptx.x, ptx.y, 'X');
  Line(Canvas, pt0, pty); Canvas.TextOut(pty.x, pty.y, 'Y');
  Line(Canvas, pt0, ptz); Canvas.TextOut(ptz.x, ptz.y, 'Z');

  //Text ***********************************************************************

  SetMapMode(Canvas.Handle, MM_ISOTROPIC);
  SetWindowExtEx(Canvas.Handle, ClientWidth, ClientHeight, nil); //darzustellende Punkte x,y
  SetViewportExtEx(Canvas.Handle, ClientWidth, ClientHeight, nil); //kippen aus
  SetViewportOrgEx(Canvas.Handle, 0, 0, nil); //Nullpunkt

  //Punkte rotieren

  Rotate(TrackBar1.Position, TrackBar2.Position, -TrackBar3.Position, False);

  //Punkte umrechnen

  SetLength(polypoints2D, Length(polypoints3D));

  for i:=0 to High(polypoints2D) do
  begin
    polypoints2D[i]:=Point3DToPoint2D(polypoints3D[i]);
    polypoints2D[i].X:=polypoints2D[i].X+ClientWidth div 2;
    polypoints2D[i].Y:=polypoints2D[i].Y+ClientHeight div 2;
  end;

  //Zeichnen

  rgn:=PolygonToRgn;

  Canvas.Brush.Color:=clGreen;
  FillRgn(Canvas.Handle, rgn, Canvas.Brush.Handle);

  DeleteObject(rgn);
end;

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  Label1.Caption:='X: '+inttostr(TrackBar1.Position);
  Invalidate;
end;

procedure TForm1.TrackBar2Change(Sender: TObject);
begin
  Label2.Caption:='Y: '+inttostr(TrackBar2.Position);
  Invalidate;
end;

procedure TForm1.TrackBar3Change(Sender: TObject);
begin
  Label3.Caption:='Z: '+inttostr(TrackBar3.Position);
  Invalidate;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FontDialog1.Font:=Font;
  
  if FontDialog1.Execute then
  begin
    Font:=FontDialog1.Font;
    FontToPolygon(Font, polytext);
    Invalidate;
  end;
end;

end.


object Form1: TForm1
  Left = 225
  Top = 104
  Caption = 'Form1'
  ClientHeight = 519
  ClientWidth = 716
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -21
  Font.Name = 'Teen Light'
  Font.Style = []
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  OnPaint = FormPaint
  TextHeight = 24
  object Label1: TLabel
    Left = 528
    Top = 8
    Width = 34
    Height = 24
    AutoSize = False
    Caption = 'X'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'Tahoma'
    Font.Style = []
    ParentFont = False
  end
  object Label2: TLabel
    Left = 528
    Top = 32
    Width = 34
    Height = 24
    AutoSize = False
    Caption = 'Y'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'Tahoma'
    Font.Style = []
    ParentFont = False
  end
  object Label3: TLabel
    Left = 528
    Top = 56
    Width = 34
    Height = 24
    AutoSize = False
    Caption = 'Z'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'Tahoma'
    Font.Style = []
    ParentFont = False
  end
  object Button1: TButton
    Left = 576
    Top = 88
    Width = 75
    Height = 25
    Caption = 'Font'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'Tahoma'
    Font.Style = []
    ParentFont = False
    TabOrder = 0
    OnClick = Button1Click
  end
  object TrackBar1: TTrackBar
    Left = 568
    Top = 8
    Width = 150
    Height = 24
    Max = 360
    TabOrder = 1
    TickMarks = tmBoth
    TickStyle = tsNone
    OnChange = TrackBar1Change
  end
  object TrackBar2: TTrackBar
    Left = 568
    Top = 32
    Width = 150
    Height = 24
    Max = 360
    TabOrder = 2
    TickMarks = tmBoth
    TickStyle = tsNone
    OnChange = TrackBar2Change
  end
  object TrackBar3: TTrackBar
    Left = 568
    Top = 56
    Width = 150
    Height = 24
    Max = 360
    TabOrder = 3
    TickMarks = tmBoth
    TickStyle = tsNone
    OnChange = TrackBar3Change
  end
  object FontDialog1: TFontDialog
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'MS Sans Serif'
    Font.Style = []
    Left = 576
    Top = 120
  end
end

© 2025 by Thomas Schwobe