home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / 1990 / 11 / dtp / bezier.pas < prev    next >
Pascal/Delphi Source File  |  1990-09-13  |  9KB  |  288 lines

  1. (* ------------------------------------------------------ *)
  2. (*                       BEZIER.PAS                       *)
  3. (*           Unit zum Zeichnen von Bézier-Kurven          *)
  4. (*            anhand eines iterativen Verfahrens          *)
  5. (*                  Turbo Pascal ab 5.0                   *)
  6. (*          (c) 1990 Andreas Heinemann & TOOLBOX          *)
  7. (* ------------------------------------------------------ *)
  8.  
  9. UNIT Bezier;
  10.  
  11. INTERFACE
  12.  
  13. USES Crt, Graph;
  14.  
  15. {DEFINE DEMO}                        { Schaltet Demo-Modus }
  16.  
  17. {$IFDEF DEMO}
  18. VAR     { Demo-Modus: Die Geraden werden mit eingezeichnet }
  19.   FarbeAlt: WORD;
  20.   DemoLineColor: WORD;             { Farbe der Stützlinien }
  21. CONST
  22.   Verzoegerung = 50;          { Verzögerung für Demo-Modus }
  23. {$ENDIF}
  24.  
  25. CONST
  26.   BezMaxPoints     = 30;          { maximale Anzahl Punkte }
  27.   BezMaxIt: WORD   = 3;                  { Iterationstiefe }
  28.   BezMaxVirtPix    = 3200; { max. Anzahl virtueller Punkte }
  29.  
  30. TYPE
  31.   BezRealTyp       = REAL;
  32.                      { Koordinatenpaar in REAL-Darstellung }
  33.   BezPoint         = RECORD
  34.                        x, y : BezRealTyp;
  35.                      END;
  36.        { Typ für Startpunkt, Endpunkt und alle Stützpunkte }
  37.   BezPoints        = ARRAY [1..BezMaxPoints] OF BezPoint;
  38.                       { Array der Koordinaten aller Punkte }
  39.   BezPointsPtr     = ^BezPoints;
  40.                              { Pointer auf die Koordinaten }
  41.   BezTempPointsDat = ARRAY [1..BezMaxPoints]
  42.                      OF BezPointsPtr;
  43.               { Array of Pointer auf die temporären Punkte }
  44.   BezTempPointsPtr = ^BezTempPointsDat;
  45.              { Pointer auf das Array der temporären Punkte }
  46.   BezPixData       = ARRAY [1..BezMaxVirtPix] OF BezPoint;
  47.           { Array der virtuellen Punkte für die Berechnung }
  48.   BezPixDataPtr    = ^BezPixData;
  49.              { Pointer auf das Array der virtuellen Punkte }
  50.   BezPointProcTyp  = PROCEDURE(x, y : BezRealTyp);
  51.   { Funktion zur Darstellung eines Punktes auf dem Monitor }
  52.  
  53.                     { Alle benötigten Daten und Prozeduren }
  54.   BezDatRec = RECORD
  55.     Punkte         : INTEGER;
  56.     TempPoints     : BezTempPointsPtr;
  57.     MaxIt          : WORD;
  58.     MaxDX, MaxDY   : WORD;
  59.     MaxDXY, MaxDYX : WORD;
  60.     DLHoch2        : WORD;
  61.  
  62.     Half           : PROCEDURE(VAR p1,p2,h : BezPoint);
  63.     SetPoint       : BezPointProcTyp;
  64.                   { wird sofort nach Berechnung aufgerufen }
  65.     SetFirstPoint  : BezPointProcTyp;
  66.                  { erster Punkt,wird als erstes ausgeführt }
  67.     SetLastPoint   : BezPointProcTyp;
  68.                              { wird als letztes ausgeführt }
  69.     DrawPoint      : BezPointProcTyp;
  70.                      { wird aufgerufen, wenn keine weitere }
  71.                              { Iteration durchgeführt wird }
  72.     Iterate        : FUNCTION (It : WORD;
  73.                                x, y : BezRealTyp) : BOOLEAN;
  74.     LastPoint      : BezPoint;             { Letzter Punkt }
  75.     UserData       : Pointer;   { für eigene Erweiterungen }
  76.   END;
  77.  
  78.   BezDatPtr       = ^BezDatRec;
  79.                    { Pointer auf die gesamte Datenstruktur }
  80.  
  81. VAR
  82.   BezDat: BezDatPtr;               { gesamte Datenstruktur }
  83.  
  84. CONST
  85.   BezPointMem = SizeOf(BezPoint);
  86.     { für die Reservierung von Speicher auf dem Heap nötig }
  87.  
  88. PROCEDURE BezInitBezier(UPunkte : WORD);
  89. { initialisert Variablen und reserviert den nötigen
  90.   Speicherplatz auf dem Heap }
  91.  
  92. PROCEDURE BezDisposeBezier;
  93. { räumt nach getaner Arbeit den Heap wieder auf }
  94.  
  95. PROCEDURE BezDoBeziere;
  96. { Aufruf der Bezierberechnung }
  97.  
  98. IMPLEMENTATION
  99.  
  100. PROCEDURE BezHalf(VAR p1, p2, h : BezPoint);
  101. { teilt die Strecke zwischen den Koordinatenpaaren p1 und
  102.   p2 und gibt den Mittelpunkt in h zurück }
  103. BEGIN
  104. {$IFDEF DEMO}
  105.   FarbeAlt:=GetColor;
  106.   SetColor(DemoLineColor);
  107.   Line(Round(p1.x), Round(p1.y), Round(p2.x), Round(p2.y));
  108.   SetColor(FarbeAlt);
  109.   Delay(Verzoegerung);
  110. {$ENDIF}
  111.   h.x:=(p1.x + p2.x) * 0.5;
  112.   h.y:=(p1.y + p2.y) * 0.5;
  113. END;
  114.  
  115. PROCEDURE BezSetPoint(x, y : BezRealTyp);   { Punkt setzen }
  116. BEGIN
  117.   PutPixel(Round(x), Round(y), GetColor);
  118. END;
  119.  
  120. FUNCTION BezIterate(It : WORD;
  121.                     x, y : BezRealTyp) : BOOLEAN;
  122. { liefert TRUE, wenn Iterationstiefe noch nicht
  123.   erreicht ist, ansonsten FALSE }
  124. BEGIN
  125.   WITH BezDat^ DO
  126.     IF It <= MaxIt THEN
  127.       Bezier.BezIterate := TRUE
  128.     ELSE
  129.       Bezier.BezIterate := FALSE;
  130. END;
  131.  
  132. FUNCTION BezIterateDist(It : WORD;
  133.                         x, y : BezRealTyp) : BOOLEAN;
  134. { optimierte Abbruchbedingung: Berechnung abbrechen,
  135.   wenn weitere Iterationen keine sichtbare Verbesserung
  136.   der Bezièrkurve bewirken }
  137. VAR
  138.   f        : BOOLEAN;
  139.   ddx, ddy : WORD;
  140. BEGIN
  141.   f := TRUE;
  142.   WITH BezDat^ DO BEGIN
  143.     IF It >= MaxIt THEN
  144.       f := FALSE
  145.     ELSE
  146.     BEGIN            { Abstand zum letzten Punkt ermitteln }
  147.       ddx := Abs(Round(LastPoint.x - x));
  148.       ddy := Abs(Round(LastPoint.y - y));
  149.                              { nah genug am letzten Punkt? }
  150.       IF Sqr(ddx) + Sqr(ddy) < DLHoch2 THEN
  151.         f := FALSE
  152.       ELSE                { lange gerade Strecken abfangen }
  153.         IF (((ddx < MaxDX) AND (ddy < MaxDXY)) OR
  154.             ((ddy < MaxDY) AND (ddx < MaxDYX))) THEN
  155.           f := FALSE;
  156.     END;
  157.   END;
  158.   BezIterateDist := f;
  159. END;
  160.  
  161. PROCEDURE BezDrawPoint(x, y : BezRealTyp);  { Linie ziehen }
  162. BEGIN
  163.   LineTo(Round(x),Round(y));
  164.   WITH BezDat^ DO BEGIN
  165.     LastPoint.x := x;
  166.     LastPoint.y := y;
  167.   END;
  168. END;
  169.  
  170. PROCEDURE BezSetFirstPoint(x, y : BezRealTyp);
  171.                                        { Startpunkt setzen }
  172. BEGIN
  173.   MoveTo(Round(x), Round(y));
  174.   WITH BezDat^ DO BEGIN
  175.     LastPoint.x := x;
  176.     LastPoint.y := y;
  177.   END;
  178. END;
  179.  
  180. PROCEDURE BezSetLastPoint(x, y : BezRealTyp);
  181.                                { Linie zum Endpunkt ziehen }
  182. BEGIN
  183.   LineTo(Round(x), Round(y));
  184. END;
  185.  
  186. PROCEDURE BezInitBezier(UPunkte : WORD);
  187. VAR
  188.   i : INTEGER;
  189. BEGIN
  190.   New(BezDat);
  191.   WITH BezDat^ DO BEGIN
  192.     Punkte := UPunkte;               { Anzahl aller Punkte }
  193.     New(TempPoints);
  194.           { Speicherplatz für temporäre Punkte reservieren }
  195.     FOR i := 1 TO Punkte DO
  196.       GetMem(TempPoints^[i], (Punkte-i+1) * BezPointMem);
  197.     MaxIt   := BezMaxIt;
  198.     MaxDX   := 10;                   { Konstante Werte für }
  199.     MaxDY   := 5;                    { die Ermittlung der  }
  200.     MaxDXY  := 5;                    { Abbruchbedingung    }
  201.     MaxDYX  := 2;
  202.     DLHoch2 := 100;       { Abstand^2 für Abbruchbedingung }
  203.  
  204.     UserData := NIL;        { reserviert für Benutzerdaten }
  205.  
  206.     Half          := BezHalf;          { hier kann der     }
  207.     SetPoint      := BezSetPoint;      { Anwender eigene   }
  208.     DrawPoint     := BezDrawPoint;     { Funktionen statt  }
  209.     SetFirstPoint := BezSetFirstPoint; { der vorgegebenen  }
  210.     SetLastPoint  := BezSetLastPoint;  { implementieren    }
  211.     Iterate       := BezIterateDist;
  212.   END;
  213. END;
  214.  
  215. PROCEDURE BezDisposeBezier;
  216. { Heap-Speicher nach der Berechnung wieder freigeben }
  217. VAR
  218.   i : INTEGER;
  219. BEGIN
  220.   WITH BezDat^ DO BEGIN
  221.     FOR i := 1 TO Punkte DO
  222.       FreeMem(TempPoints^[i], (Punkte-i+1) * BezPointMem);
  223.       Dispose(TempPoints);
  224.   END;
  225.   Dispose(BezDat);
  226. END;
  227.  
  228. PROCEDURE BezComputeBezier(It : WORD);
  229. { rekursive Berechnung der Bezièrkurven; in it
  230.   wird die Anzahl der Iterationen übergeben }
  231. VAR i, j        : INTEGER;
  232.     SparePoints : BezPoints;
  233.     x, y        : BezRealTyp;
  234. BEGIN
  235.   WITH BezDat^ DO BEGIN
  236.     FOR i := 2 TO Punkte DO              { Strecken teilen }
  237.       FOR j := 1 TO Punkte - i + 1 DO
  238.         Half(TempPoints^[i-1]^[j], TempPoints^[i-1]^[j+1],
  239.              TempPoints^[i]^[j]);
  240.     x := TempPoints^[Punkte]^[1].x;
  241.     y := TempPoints^[Punkte]^[1].y;
  242.     IF @SetPoint <> NIL THEN
  243.       SetPoint(x, y);
  244.                          { 2. Iterationsstützpunkte merken }
  245.     FOR i := 1 TO Punkte DO
  246.     BEGIN
  247.       TempPoints^[1]^[i] := TempPoints^[i]^[1];
  248.       SparePoints[Punkte-i+1] :=
  249.         TempPoints^[i]^[Punkte-i+1];
  250.     END;
  251.     Inc(It);                    { Iterationszähler erhöhen }
  252.     IF Iterate(It, x, y) THEN BEGIN
  253.                                 { weitere Berechnung nötig }
  254.       BezComputeBezier(It);            { rekursiver Aufruf }
  255.       FOR i := 1 TO Punkte DO
  256.         TempPoints^[1]^[i] := SparePoints[i];
  257.       BezComputeBezier(It);
  258.     END
  259.     ELSE BEGIN
  260.       FOR i := 2 TO Punkte DO
  261.         DrawPoint(TempPoints^[i]^[j].x,
  262.                   TempPoints^[i]^[j].y);
  263.       { bereits berechnete StützPunkte liegen näher an der
  264.          Bézier-Kurve als die Gerade vom letzten Punkt zum
  265.                                      aktuellen Punkt (x,y) }
  266.       FOR i := 2 TO Punkte - 1 DO
  267.         DrawPoint(SparePoints[i].x,SparePoints[i].y);
  268.     END;
  269.   END;
  270. END;
  271.  
  272. PROCEDURE BezDoBeziere;
  273. VAR
  274.   LastPointSet : BezPoint;
  275. BEGIN
  276.   WITH BezDat^ DO BEGIN
  277.     LastPointSet := BezDat^.TempPoints^[1]^[Punkte];
  278.     IF @SetFirstPoint <> NIL THEN
  279.       SetFirstPoint(TempPoints^[1]^[1].x,
  280.                     TempPoints^[1]^[1].y);
  281.       BezComputeBezier(0);
  282.       IF @SetLastPoint <> NIL THEN
  283.         SetLastPoint(LastPointSet.x, LastPointSet.y);
  284.   END;
  285. END;
  286.  
  287. END.
  288.