PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : grlinien zeichnen etc


i.columbo
20.09.2007, 22:06
hi!

ich wollte fragen, wie ich in pascal linien, rechtecke o.ä. zeichnen kann.

am besten währe ein code example :D

thx für eure antworten

euer
inspector


Xpyder
21.09.2007, 12:23
ich wollte fragen, wie ich in pascal linien, rechtecke o.ä. zeichnen kann.
am besten währe ein code example

Ja, also sowas habe ich schon sehr oft gemacht, hab auch ein schönes selbstgemachte Pascal-Unterprogramm hier (arbeite da mit so "Festkomma"-Trick und all diesem Kram...

Also, dann kommen wir erstmal zum Hauptproblem: In welchem Pascal, unter welchem Betriebssystem und in welchem Grafikmodus soll das Ganze stattfinden?

Nehmen wir mal an, es ginge um Borland Pascal (oder Turbo Pascal).

Erstmal: Das, was ich hier beschreibe, gilt für DOS.
Es ist ebenfalls in Windows (bis Windows XP) benutzbar. Unter Windows Vista funktioniert es nicht mehr!
MS supportet 16bit-DOS im 32bit-Vista nur noch im "Textmode" (im Fenster).
Im 64bit-Vista ist überhaupt kein Support für DOS mehr vorhanden - auch nicht für 32bit-DOS- oder 32bit-Windows-Programme!
Wollte das nur vorher dazusagen - nicht daß jemand enttäuscht ist.

Anm: Es gibt auch heute noch "aktuelles" Pascal. Nennt sich wohl Delphi, läuft unter Windows. Hier funktioniert das mit der Grafik aber dann wohl sowieso komplett anders.


Ich beschreibe jetzt einmal zwei Methoden.

1. Die von Borland selbst dazu vorgesehene Methode

Das hat für einige BIOS-Modi eine vorgefertigte Unit "Graph". Diese erfordert allerdings, daß sich einer der Pascal-eigenen Grafiktreiber (*.BGI) in einem zu wählenden Pfad befinden muß oder mit einem Linker angelinkt werden... Bla...

Ich habe das hier gerade mal aus der Hilfe zur Unit "Graph" (Befehl "Line") herauskopiert.

Der Befehl "InitGraph" initialisiert den Grafikmodus. Der dritte Parameter gibt den Pfad für die Grafiktreiber an. Bei mir z.B. 'C:\BP\BGI\'. Man kann natürlich den gewünschten Treiber auch ins selbe Verzeichnis wie das Programm legen, dann funktioniert der Leerstring. Gd und Gm geben die Nummer des Treibers und die Nummer des Grafikmodus für diesen Treiber an. Dazu gibts eine Liste in der Hilfe. Kann die auch mal hier posten...
(Hilfe zu "Graph" aufrufen, dann "Weiter mit GRAPH.TPU Konstanten und Datentypen", und dann "Mögliche Grafiktreiber" (für Gd) und "Grafikmodi der Treiber (für Gm) aufrufen.)


{Beispielcode für die Prozedur Line }

uses Crt, Graph;
var Gd, Gm: Integer;
begin
Gd := Detect;
InitGraph(Gd, Gm, '');
if GraphResult <> grOk then
Halt(1);
Randomize;
repeat
Line(Random(200), Random(200), Random(200), Random(200));
until KeyPressed;
Readln;
CloseGraph;
end.

Eigentlich ist es ganz einfach, das zu kapieren. Man kopiert das ins Pascal, dann geht man auf die einzelnen Befehle mit Strg+F1, was die dazugehörige Hilfe aufruft...
Achja, das Beispiel benutzt die Unit Crt (die den lustigen RTE200-Bug auf "zu schnellen" Rechnern hat), das wohl aber nur für KeyPressed. Kann man auch alles selbermachen...

Xpyder
21.09.2007, 12:27
2. Meine eigene Methode

Ich war nie ganz glücklich damit, die Grafiktreiber als Extrafile mitzuliefern. Außerdem wurde die Entwicklung von Borland Pascal irgendwann 1992/93 eingestellt, da sind dann nur Grafikmodi vorhanden, die zu der Zeit schon "gebräuchlich" waren - die paar VGA-Modi, die's da gibt. Weiß gar nicht, ob da schon 256-Farb-Modi dabeiwaren.

Ich bin sozusagen "der Richtige", weil ich hauptsächlich "Grafik und alles was damit zu tun hat" programmiere. Das alles in Pascal und Assembler. Leider für DOS.

Deswegen habe ich hier meine eigenen Routinen gebaut, die selbst die Modi anschalten,

Um zu verstehen, wie Grafikmodi in der Theorie funktionieren, kann man es auf meiner Seite nachlesen:
( http://igames.inside1.net/html/dosd2.htm - "Wie funktioniert Grafik?" )

Ich habe selber ein paar Grafik-Units gebaut, die vor allem für die "brauchbareren" Modi (mit mehr als 16 Farben und auch höheren Auflösungen) gedacht sind.
Habe das unterteilt - eine Unit für "Mode X" genannte VGA-Modi (die eben auch selbstdefinierte Auflösungen enthält, aber eben nur (VGA-üblich) max. 256kB Speicher ansprechen kann und nur max. 256 von 262144 Farben hat). Und eine Unit für "VESA" genannte Modi, die die ganzen VESA-Modi supportet. Beide Units haben auch Linien- und Rechteck-Funktionen, sowie Funktionen zur Textausgabe. Zur Bewahrung der Kompatibilität arbeiten sie so, daß sie generalisierte Linien-, Rechteck-, Kreis-, Text-, etc Routinen haben, die dann auf die Pixelroutine zugreifen (jedes andere "Element" ist ja aus Pixeln zusammengesetzt), die für die Modi jeweils "ausgetauscht" wird. Glaub die Rechteck-Routine für "Mode X" habe ich aber speziell
optimiert.

Vor kurzem habe ich den VESA-Modi-Support auch auf die "nicht palettenbasierten" Modi erweitert, also mit >256 Farben, also 15, 16, 24 und 32 Bit Modi, mit Nutzung des XMS als Puffer und mit generalisierten Routinen, die jeden Modus behandeln wie einen 24-Bit-Modus. Man kann also immer mit 32-Bit-Farben zeichnen, RGB und ein "Alpha" Kanal (Transparenz), das wird entsprechend auf den jeweils wirklich aktiven Modus heruntergerechnet.

Also, die Linienroutine besteht wie man sieht aus zwei Teilen, einmal für "flache" und einmal für "steile" Linien, die Grenze ist bei 45°, also genau da, wo der Anstieg =1 ist. Das liegt an der besonderen Arbeitsweise. Eine der Koordinaten wird immer normal inkrementiert (um 1 erhöht), die andere um einen "krummen" Wert erhöht/vermindert. Hier verwende ich Methoden, die ohne Fließkomma auskommen, was das Ganze doch a) beschleunigt und b) viel leichter in Assembler umsetzbar macht.

(Anm: Daß vor den externen Variablen und Prozeduren "HC_" steht, liegt daran, daß ichs fix aus meiner "HiColor" Unit rauskopiert habe. Jede Unit kriegt von mir solche Kürzel verpaßt, damit sich Variaben/Konstanten/Prozeduren/Funktionen nicht mit anderen "überschneiden".)


{Vorher muß noch dafür definiert sein: }

{Ob geprüft wird, wenn sich Pixel außerhalb des erlaubten Grafikscreens befinden.
Ohne Prüfung = minimal schneller, dann aber auch "gefährlicher"}
const HC_CHECKPIX:boolean=TRUE;

{Wieviele Zeilen und Spalten im zu zeichnenden Bildbereich vorhanden sind.}
{Also sozusagen die Auflösung des Bildes.}
var HC_Rows,HC_Lines:word;

{Eine Pixelroutine, an die dann die jeweils aktuelle Routine zugewiesen wird.}
{Hier muß man natürlich eine deklarieren.}
var HC_Pixel:Procedure(X,Y:integer;COLOR:Longint);

{---------------------------------------------------------------------------}
{Zeichnet eine Linie von X0/Y0 nach X1/Y1 in Farbe COLOR}
procedure HC_Line(X0,Y0,X1,Y1:integer;COLOR:longint);
type LOHI=Record L,H:integer;end;
var XA,YA,ADDER:Longint;
XAI:LOHI absolute XA;YAI:LOHI absolute YA;ADDERI:LOHI absolute ADDER;
XDIF,YDIF:longint;
W:word;
begin
XDIF:=X1;dec(XDIF,X0);YDIF:=Y1;dec(YDIF,Y0);ADDER:=0;
if abs(XDIF)<abs(YDIF) then
begin
if YDIF<0 then begin W:=X0;X0:=X1;X1:=W;W:=Y0;Y0:=Y1;Y1:=W;XDIF:=-XDIF;YDIF:=-YDIF;end;
if HC_CHECKPIX then begin if (Y1<0) or (Y0>=HC_Lines) then exit;end;
XAI.L:=0;XAI.H:=X0;if YDIF<>0 then begin ADDERI.H:=XDIF;ADDER:=ADDER div YDIF;end;
if HC_CHECKPIX then
begin
if Y0<0 then begin dec(XA,ADDER*Y0);Y0:=0;end;{dec, weil Y0 ja negativ...}
if Y1>=HC_Lines then Y1:=pred(HC_Lines);
if XAI.H<0 then begin if ADDER >0 then begin inc(Y0, -XA div ADDER );XA:=0; end else exit;
end;
if XAI.H>=HC_Rows then begin if ADDER <0 then begin dec(Y0,(XA-(longint(pred(HC_Rows))shl 16))div ADDER);
XAI.H:=pred(HC_Rows);end else exit;
end;
end;
for W:=Y0 to Y1 do
begin HC_Pixel(XAI.H,W,COLOR);inc(XA,ADDER);if (XAI.H<0) or (XAI.H>=HC_Rows) then exit;end;
end
else
begin
if XDIF<0 then begin W:=X0;X0:=X1;X1:=W;W:=Y0;Y0:=Y1;Y1:=W;XDIF:=-XDIF;YDIF:=-YDIF;end;
if HC_CHECKPIX then begin if (X1<0) or (X0>=HC_Rows) then exit;end;
YAI.L:=0;YAI.H:=Y0;if XDIF<>0 then begin ADDERI.H:=YDIF;ADDER:=ADDER div XDIF;end;
if HC_CHECKPIX then
begin
if X0<0 then begin dec(YA,ADDER*X0);X0:=0;end;{dec, weil Y0 ja negativ...}
if X1>=HC_Rows then X1:=pred(HC_Rows);
if YAI.H<0 then begin if ADDER >0 then begin inc(X0, -YA div ADDER );YA:=0; end else exit;
end;
if YAI.H>=HC_Lines then begin if ADDER <0 then begin dec(X0,(YA-(longint(pred(HC_Lines))shl 16))div ADDER);
YAI.H:=pred(HC_Lines);end else exit;
end;
end;
for W:=X0 to X1 do
begin HC_Pixel(W,YAI.H,COLOR);inc(YA,ADDER);if (YAI.H<0) or (YAI.H>=HC_Lines) then exit;end;
end;
end;
{---------------------------------------------------------------------------}
{Zeichnet ein Rechteck von X0/Y0 nach X1/Y1 in Farbe COLOR}
procedure HC_Rectangle(X0,Y0,X1,Y1:integer;COLOR:longint);
var X,Y:integer;
begin if X0>X1 then begin X:=X0;X0:=X1;X1:=X;end;if Y0>Y1 then begin Y:=Y0;Y0:=Y1;Y1:=Y;end;
if HC_CHECKPIX then
begin if (X1<0) or (X0>=HC_Rows) or (Y1<0) or (Y0>=HC_Lines) then exit;
if X0<0 then X0:=0;if X1>=HC_Rows then X1:=HC_Rows ;
if Y0<0 then Y0:=0;if Y1>=HC_Lines then Y1:=HC_Lines;
end;
for Y:=Y0 to Y1 do for X:=X0 to X1 do HC_Pixel(X,Y,COLOR);
end;
{---------------------------------------------------------------------------}

Xpyder
21.09.2007, 12:28
Man könnte zwar hier nun auch das Pixelsetzen selbst in die Linienziehroutine einbauen (zweimal), (und in die Rechteck-Routine) aber der Vorteil ist wohl minimal, die Nachteile jedoch immens.
Es sei denn, Pixelsetzen wäre so etwas wie das hier:
(Die beiden kleinen "Asm"-Teile kann man auch durch "Intr" in Pascal realisieren, das ist aber m.E. zu umständlich.)

var SCREEN:array[0..199,0..319]of byte absolute $A000:$0000;
begin
asm mov ax,13h; int 10h;end; {Mode 13h einschalten: 320x200, 256 Farben, Grafik}
SCREEN[140,180]:=14; {Gelben Pixel in Zeile 140, Spalte 180 setzen}
readln; {Warten auf Enter, damit man das Kunstwerk bewundern kann}
asm mov ax,03h; int 10h;end; {Mode 03h einschalten: 80x50, 16 Farben, Text}
end.

D.h. Pixelsetzen im BIOS-Mode 13h (320x200x256) kann man einfach, indem man ein zweidimensionales Array an der Position des Grafikspeichers deklariert. Zu beachten: Der erste Index ist die Y-, der zweite die X-Koordinate!
Achja: Ob die Farbe 1,2,4,8,15,16,24 oder 32 Bit ist, ist für die "umgebenden" Routinen völlig unwichtig. Die Farbe wird nur angegeben, um sie unverändert an die Pixelroutine weiterzureichen. (Will sagen: Es ist auch durchaus möglich, einen Byte-Wert für die Farbe zu benutzen.)

Achja: Bisher habe ich hier kein "Fenster" definiert, d.h. das aktuelle Fenster ist immer das maximale (0,0,Bildbreite-1,Bildhöhe-1).
Es gibt zwei Möglichkeiten, damit umzugehen:

a) Die schnellere Methode: Man benutzt die schon in der HC_CHECKPIX-Abfrage eingearbeiteten Teile und ersetzt 0/0 und HC_Rows/HC_Lines durch die entsprechenden Fenstergrenzen.

b) Die einfachere Methode: Man baut in die Pixelroutine eine Abfrage ein, ob sich der Pixel in den Fenstergrenzen befindet.

Ich persönlich arbeite immer so: Die "interne" Pixelroutine fragt keine Grenzen ab (d.h. dafür gibts noch eine vorgeschaltete "externe" Pixelroutine, die dann die interne aufruft.)
Zweck des Ganzen ist, daß man z.B. bei Linien-/Rechteck- o.ä. Routinen nicht bei jedem Pixel abfragen muß, ob er sich in den erlaubten Grenzen befindet, sondern vorher durch vernünftige Programmierung dafür sorgen kann, daß er das immer tut (und man daher die schnellere "interne" Pixelroutine benutzen kann).

Kann bei Bedarf auch noch Prozeduren liefern, die gefüllte Polygone und Ellipsen (Kreis ist nur ne Sonderform der Ellipse) zeichnen.

Ebenfalls hab ich noch Prozeduren hier, um "fixed" Fonts (8x8, 8x16) oder proportionale Fonts zu zeichnen, sowie "Grafiken", (also "Sprites").
Da es hierzu keine allgemeingültigen Formate gibt (oder deren Specs nicht bekannt sind - oder sie zu speicherverschwendend waren) habe ich hierfür eigene Fileformate entwickelt.
(Konvertierprogramme sind vorhanden - z.B. um einen proportionalen Zeichensatz von einer "Grafik" einzulesen.)

Also, falls weiteres Interesse speziell an Grafikprogrammierung in Pascal besteht, kann ich auch gern weiterhelfen.