Rovinné køivky
Uživatelská souøadná soustava
Jak již víme, DELPHI pracuje
s tzv. svìtovými souøadnicemi, které jsou na pøipojeném obrázku vyznaèeny
modøe. Práce v takové soustavì je pro uživatele znaènì nepøíjemná.
Vytvoøíme si proto tzv. uživatelskou souøadnou soustavu, která je vyznaèena
èervenì. „Délku“ osy
(interval
) jakož i „délku“ osy
(interval
) budeme moci mìnit. V obrázku je
resp.
délka uživatelské jednotky na ose
resp.
ve
svìtových souøadnicích. Zøejmì je

;
(1)
Souøadnice uživatelského poèátku O ve svìtových souøadnicích jsou pak
;
(2)
Je-li pak
libovolný bod na kreslicí ploše,
jeho souøadnice
v uživatelské soustavì a
jeho souøadnice
ve svìtové soustavì, pak zøejmì platí
;
(3)
Realizujme nyní uživatelskou
souøadnou soustavu a vykresleme trojúhelník s danými vrcholy. Na formuláø
jsme opìt umístili objekt Image, proceduru, která trojúhelník vykresluje, jsme
nazvali Triangle
a budeme ji aktivovat OnClick.
unit Graph2D;
interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs,ExtCtrls;
type
TPoint = array [1..2]
of Double;
TForm1 = class(TForm)
Image1: TImage;
procedure Triangle(Sender:TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure Scale(x1,x2,y1,y2:Double);
function XCoor(x:Double):integer;
function YCoor(y:Double):integer;
end;
Všimnìte si však,
že se hlavièka této procedury automaticky zapsala také jako nová vlastnost typu
TForm. Tímto
zpùsobem mùžeme i my sami obohacovat typ TForm
o nové promìnné, procedury a funkce, nejlépe do odstavce Public (zde
budou tyto vlastnosti v pøípadì potøeby dostupné i z jiných jednotek).
Nyní se tedy dostáváme k èervenému textu, který musíme dopsat sami a kterým realizujeme uživatelskou souøadnou
soustavu.
var Form1
:TForm1;
Unit_X,Unit_Y,x1,x2,y1,y2:Double;
O :TPoint;
implementation
{$R *.DFM}
procedure TForm1.Scale(x1,x2,y1,y2:Double);
begin
Unit_X:= Image1.Width/(x2-x1);
Unit_Y:=Image1.Height/(y2-y1);
O[1]:= -x1*Unit_X;O[2]:=y2*Unit_Y;
end;
function TForm1.XCoor(x:Double):integer;
begin XCoor:=trunc(O[1]+Unit_X*x);end;
function TForm1.YCoor(y:Double):integer;
begin
YCoor:=trunc(O[2]-Unit_Y*y);end;
procedure
TForm1.Triangle(Sender: TObject);
begin
end;
end.
Protože
budeme chtít pracovat s body v rovinì, deklarujeme v èásti Interface typ TPoint jako uspoøádanou dvojici reálných èísel (pøedefinovali
jsme tedy typ TPoint, který je v
Pøíklad 1:
Výše uvedená èást kódu by samozøejmì ještì trojúhelník nevykreslila. Doplòme
jej tak, abychom na výstupu trojúhelník dostali. V øešeném pøíkladu jsou
podobnì jako výše uvedené tøi procedury a funkce doplnìny další:
procedure
PutPixel(X:TPoint;Color:DWord);
procedure
MoveTo(X:TPoint);
procedure
LineTo(X:TPoint;Color:DWord);
procedure
Line(X,Y:TPoint;Color:DWord);
procedure
XAxis(x1,x2,y:Double;Color:DWord);
procedure
YAxis(y1,y2,x:Double;Color:DWord);
Procedury PutPixel, MoveTo a LineTo fungují analogicky, jako metody
obsažené v TCanvas ve standardním
procedure
TForm1.LineTo(X:TPoint;Color:DWord);
begin
With
Image1.Canvas do
Begin
Pen.Color:=Color;LineTo(XCoor(X[1]),YCoor(X[2]));
end;
end;
V tìchto procedurách tedy není využit pøístup
do bitmapy pøes obrazové øádky tak, jak bylo uvedeno v matematických
podrobnostech pøedchozí kapitoly. V dalším textu budeme
prozatím používat osmibitové barvy, resp. pøeddeklarované konstanty - napø.
slRed, slGreen atd., které byly popsány v
matematických podrobnostech pøedchozí kapitoly, a vlastní proceduru Line pro
úseèku, která je popsána v matematických podrobnostech této kapitoly .
Jsou-li k dispozici výše uvedené
procedury, pak trojúhelník vykreslíme velmi jednoduše:
procedure
TForm1.Triangle(Sender: TObject);
var A,B,C:TPoint;
begin
A[1]:=-2;A[2]:=6;
B[1]:= 4;B[2]:=-1; C[1]:= 5;C[2]:=5;
{definice vrcholù}
x1:=-4;x2:=7;y1:=-3;y2:=8;
Scale(x1,x2,y1,y2);
{definice uživatelské kreslicí plochy}
Line(A,B,slRed);Line(B,C,slRed);Line(A,C,slRed); {vlastní
konstrukce}
end;
Zde
najdete kompletní zdrojový
kód a zde spustitelný kód

Pøíklad 2: Pøedchozí pøíklad vylepšíme o cejchování
souøadných os, který obstarávají procedury
XGauge (x1,x2,y:Double;Color:Byte);
resp. YGauge
(y1,y2,x:Double; Color: Byte). Podle velikosti mìøítka procedury
zvolí vhodnou velikost jednotky na popis, k popisu pøipojí vzdálenost
sousedních „fousù“. Fungují následujícím zpùsobem (jako pøíklad uveïme popis
osy x):
procedure TForm1.XScale(x1,x2,y:Double;Color:DWord);
var AxisUnit :Double;
Text :String;
Lx,Exponent :Integer;
begin
Image1.Canvas.Font.Color:=Color;
AxisUnit:=1E30;Lx:=0;Exponent:=30;
While (x2-x1)/AxisUnit<1 do
begin
AxisUnit:=AxisUnit/10;Exponent:=Exponent-1;
end; {nalezení vhodné velikosti jednotky}
if x1<0
{nalezení
nejmenší popisky}
then Repeat Lx:=Lx-1 Until
AxisUnit*(Lx-1)<x1
else Repeat Lx:=Lx+1 Until AxisUnit*(Lx+1)>x1;
With Image1.Canvas do
repeat
MoveTo(XCoor(AxisUnit*Lx),YCoor(y)-3); {cejchování osy}
LineTo(XCoor(AxisUnit*Lx),YCoor(y)+3);
Str(Lx,Text);TextOut(XCoor(AxisUnit*Lx),YCoor(y)+5,Text);Lx:=Lx+1; {popisky}
until
AxisUnit*Lx>x2;
Str(Exponent,Text); {funkce Str pøevádí èíselnou hodnotu na øetìzec}
Image1.Canvas.TextOut(XCoor(x2)+15,YCoor(y)+5,'*10');
{jednotka
a exponent}
Image1.Canvas.TextOut(XCoor(x2)+35,YCoor(y),Text);
end;
Zde najdete kompletní zdrojový
kód a zde spustitelný kód
Pøedchozí pøíklady neumožòují mìnit parametry za bìhu programu. Tento nedostatek napravíme v následujícím pøíkladu.
Pøíklad 3: K ovládání našeho formuláøe Form1 založíme celý nový formuláø. V menu File klikneme na New Form. Objeví se nám nový formuláø Form2 spolu s novou jednotkou Unit2. Tomuto formuláøi zmìníme titulek na Control Panel. a umístíme na nìj tøi tlaèítka, která vybereme ze stránky Standard vícestránkové lišty. Tìmto tlaèítkùm zmìníme titulky na „Start“, „Clear Image“ a „Exit“. Dále nìkolik objektù Static Text, tento typ najdeme v lištì Additional, jejichž titulky budou „Triangle“, „Scale“, další budou sloužit pro popis vrcholù - „A=“, „B=“, „C=“ a uživatelských rozmìrù kreslicí plochy „x“, „y“. Dále zde umístíme Check Box (lišta Standard) s titulkem „Show Axses“, s jehož pomocí bude uživatel vybírat možnost zobrazení souøadných os. O tom, zda se souøadné osy mají èi nemají zobrazit, rozhoduje vlastnost Checked typu Boolean. Koneènì pøidáme potøebný poèet objektù Edit (opìt v lištì Standard), do kterých bude uživatel zadávat hodnoty. Zmìníme jejich jména (vlastnost Name v Object Inspectoru) na EditA1, EditA2, …, resp EditX1, EditX2…, a Text, tak, aby pøi spuštìní programu tato okna obsahovala výchozí parametry. Náš formuláø vypadá tak, jak vidíme na pøiloženém obrázku. Procedury týkající se rovinné uživatelské souøadné soustavy jsou v samostatné jednotce - pùvodní Unit1 pøejmenujeme na Graph2D pomocí File/SaveAs. Do této jednotky jsme také umístili objekt typu TImage, který jsme nazvali Draw2D a všechny konstrukce probíhají v tomto objektu. Napø. procedura pro mazání kreslicí plochy kreslí pøes celou tuto plochu bílý obdélník:
procedure TDraw2D.ClearImage;
begin
with Image1.Canvas do
begin
Pen.Color:=clWhite;Brush.Color:=clWhite;end;
with Image1 do Canvas.Rectangle(0,0,Width-1,Height-1);
end;
V Control Panelu nejdøíve zaøídíme ètení parametrù z Edit-okének, a to procedurou Setting:
Procedure TControl_Panel.Setting;
var Code:Integer;
begin
With
Draw2D do
begin
Val(EditA1.Text,A[1],Code);
Val(EditA2.Text,A[2],Code);
Val(EditY2.Text,y2,Code);
Scale(x1,x2,y1,y2);
end;
end;
Tlaèítka v Control Panelu spojíme pøes událost OnClick poøadì s procedurami Triangle, ClearImage a Exit:
procedure TControl_Panel.Triangle(Sender: TObject);
begin
With Draw2D do
begin
ClearImage;Setting;
if CheckBox1.Checked then CoorSystem(slRed);
Line(A,B,slBlue);Line(B,C,slBlue);Line(A,C,slBlue);
end;
end;
procedure TControl_Panel.ClearImage(Sender: TObject);
begin Draw2D.ClearImage;end;
procedure TControl_Panel.Exit(Sender: TObject);
begin Halt; end;
V pøíkladu je navíc øešeno zadávání vrcholù trojúhelníka myší
Zde najdete kompletní zdrojový
kód a zde spustitelný kód
Køivky
typu ![]()
Z hlediska konstrukce se jedná o nejjednodušší køivky. Lze je konstruovat jako lomené èáry složené z úseèek dle pøipojeného obrázku:

Pøíklad 1: Sestrojme proceduru
Explicit_Curve, která sestrojí graf funkce
(viz pøipojený výpis). Nejdøíve opìt
sestavíme ovládací panel ze známých komponent (v panelu NumberOfSegments bude
uživatel zadávat poèet segmentù, z kterých má být køivka sestrojena. Stejnì
jako v pøedchozí kapitole sestrojíme uživatelskou kreslicí plochu,
souøadné osy a definujeme barvu sestrojované køivky. Vidíme, že funkce
je definována uvnitø procedury. Vlastní konstrukce spoèívá v nastavení
kroku hx a v postupné konstrukci úseèek
:

Procedure
TControl_Panel.Explicit_Curve(Sender:TObject);
var x,hx:Double;
A,B :TPoint;
function f(x:Double):Double;
begin if x=0 then f:=0
else f:=x+10*x*x*sin(1/x); end;
begin
With Draw2D do {Uživatelská kreslicí plocha}
begin
if CheckBox1.Checked then
begin
{souøadné osy a
jejich popis}
XScale(x1,x2,0,clRed); {sestrojí stupnici rovnobìžnou s osou x, resp. y.}
YScale(y1,y2,0,clRed); { Pro urèení barvy je použita standardní konstanta }
end; { kvùli snazšímu
popisu os pomocí procedury TextOut}
hx:=(x2-x1)/NumberOfSegments;x:=x1;A[1]:=x;A[2]:=f(x);
Repeat {konstrukce køivky jako lomené èáry}
B[1]:=x+hx;B[2]:=f(x+hx); Line(A,B,slBlue);A:=B;x:=x+hx
Until x>x2
end;
end;

Zde najdete kompletní zdrojový
kód a zde spustitelný kód
V pøípadì
køivek bývá výhodné posloupnost bodù, kterými køivka prochází, nejdøíve celou
spoèítat a teprve potom proložit køivku.
Pøíklad 2: Pøedefinujme metodu PolyLine a funkci z pø. 1. sestrojme pomocí této nové metody. Naše metoda bude pracovat s posloupností bodù, musíme tedy nejdøíve deklarovat nový typ, nejlépe hned za TPoint:
TPoint = array [1..2] of Double;
TArrayOfPoints = array [0..800] of TPoint;
TPoint je tedy jeden bod - uspoøádaná dvojice èísel, TArrayOfPoints je posloupnost nejvýše osmi set bodù. Do promìnné tohoto typu se budou ukládat body, kterými má procházet naše køivka. S nástroji, které již máme k dispozici, je její konstrukce jednoduchá:

procedure Draw2D.PolyLine(Q:TArrayOfPoints; n:Word;Color:Byte);
var
i:Word;
begin
MoveTo(Q[1]);
for
i:=1 to n do LineTo(Q[i],Color);
end;
Všimnìme si nejdøíve hlavièky: Q
je posloupnost bodù, n definuje poèet bodù, které mají být spojeny, Color
pak barvu, kterou má být køivka sestrojena. Procedura nejdøíve umístí pero do
prvního bodu pomocí naší metody MoveTo, do následujících pak kreslí úseèky naší
metodou LineTo. Samozøejmì je tøeba upravit i naši proceduru
Explicit_Curve (uvádím pouze èást kódu, která se od pøedchozího pøíkladu liší).
procedure TControl_Panel.Explicit_Curve(Sender:TObject);
var x,hx:Double; Q :TArrayOfPoints; i
:Word;
function f.....
begin
.......
hx:=(x2-x1)/NumberOfSegments;i:=0;x:=x1;
Repeat
Q[i,1]:=x;Q[i,2]:=f(x); {konstrukce køivky jako lomené èáry}
x:=x+hx;i:=succ(i);
Until x>x2+hx;
PolyLine(Q,i,Color);
end;
Zde najdete kompletní zdrojový
kód a zde spustitelný kód
Je zde jedna ponìkud nepøíjemná vìc: V promìnné typu TArrayOfPoints nám pøibyl jeden index, který udává poøadí bodu v posloupnosti. Ten je tøeba uvádìt vždy jako první. Zápis Q [i,2] tedy znamená, že se jedná o druhou souøadnici i - tého bodu v posloupnosti Q. Naši metodu PolyLine použijeme i pøi konstrukci køivek zadaných parametricky a polárnì. Zde bude ponìkud odlišný pouze zpùsob naplnìní pole Q.
Z hlediska uživatele zùstává jiná nepøíjemnost vìc: z ovládacího panelu není možné mìnit zadání funkce.
V pøíkladu 3, jehož zdrojový kód je zde a spustitelný kód je zde,

je tento nedostatek odstranìn. Øetezec zadávaný do okna EditFunction je vyèíslován v jednotce Graph2D_256 funkcí Calc(Text:String; ErrorReport:String). V zadávaném øetìzci je možno zadávat všechny bìžné funkce vèetnì cyklometrických, k oznaèení promìnných lze použít x, y, t, k. ErrorReport vrací chybové hlášení. Máme-li k dispozici tuto proceduru, odpadá zadávání funkce f ve zdrojovém kódu. Promìnnou f deklarujeme jako øetìzec a pøíkaz A[i,2]:=f(x) nahradíme pøíkazem A[i,2]:=Calc(f, ErrorReport). Øetìzec ErrorReport je pøi korektním výpoètu správný. Pøi chybném vyhodnocení je vrácen popis chyby, èehož je možno využít k chybovému hlášení a pøerušení procedury, napø:
A[i,2]:=Calc(f, ErrorReport)
if ErrorReport<>’’
then begin
ShowMessage(ErrorReport);
exit;
end;
Samotné vyèíslení øetezce (funkce Calc) však není jednoduchou záležitostí, bezprostøednì nesouvisí pøímo s grafickými problémy a nebudeme se jím proto podrobnìji zabývat.
Døíve, než postoupíme dále, je
tøeba uèinit ještì jednu poznámku. Barvy zadáváme v
procedure TControl_Panel.FormActivate(Sender:TObject);
begin
Draw2D.InitImage(Image1.Width,Image1.Height);
end;
procedure TControl_Panel.Explicit_Curve(Sender:TObject);
var x,hx:Double; Q :TArrayOfPoints; i
:Word;
function f.....
begin
.......
hx:=(x2-x1)/NumberOfSegments;i:=0;x:=x1;
Repeat
Q[i,1]:=x;Q[i,2]:=f(x); {konstrukce køivky jako lomené èáry}
x:=x+hx;i:=succ(i);
Until x>x2+hx;
PolyLine(Q,i,Color);
end;
Odkazy na kódy pracující v True Color režimu budeme uvádìt takto:
True Color: zdrojový kód spustitelný kód
Køivky
zadané parametricky a polárnì

Konstrukce tìchto køivek je velmi podobná, ukážeme si ji na konkrétním pøíkladu:
Pøíklad 1: Sestrojte køivku urèenou
obecnì parametrickými rovnicemi
;
(jedná se o
tzv. Lissajousovy køivky, které opisuje kyvadlo rozkmitané ve dvou navzájem
kolmých rovinách a amplitudami
,
a úhlovými
frekvencemi
;
). Náš úkol øeší procedura Param_Curve. Nejprve jsou
deklarovány konstanty t1, t2, které definují rozsah parametru. Místo promìnných
x, hx je deklarován parametr t a jeho krok ht. Na deklaraci parametrických rovnic
ve zdrojovém kódu musíme místo funkce použít proceduru, nebo� jako výstup
potøebujeme dvì promìnné - x, y.
procedure TControl_Panel.Param_Curve (Sender:
TObject);
var i:Word;
procedure Lissajous(t:double;var x,y:double);
begin
x:=3*sin(3*t); y:=3*cos(5*t);
end;
begin
With Draw2D do
begin
ClearImage(slWhite); {vyplnìní plochy bílou barvou}
Scale(x1,x2,y1,y2);
ht:=(t2-t1)/NumberOfSegments;
t:=t1;i:=0;
Repeat
Lissajous(t,Q[i,1],Q[i,2]);
t:=t+ht;i:=succ(i);
Until t>t2+ht;
PolyLine(Q,i,slBlue);
end;
end;
Výstupní parametry od vstupních je tøeba v hlavièce procedury oddìlit klíèovým slovem var. Následují parametrické rovnice (s konkrétními volbami za a, b, j, y). Ve vlastní proceduøe jsem se omezil opìt výhradnì na konstrukci køivky. Nejdøíve je potøeba nastavit krok parametru ht a jeho poèáteèní hodnotu. V cyklu pak naplòujeme pole Q parametrickými rovnicemi, dokud parametr t krokem ht neprobìhne celý interval át1;t2ñ (indexem i poèítáme body v posloupnosti Q). Nakonec køivku vykreslíme opìt pomocí PolyLine.