Hallo Besucher, der Thread wurde 3,5k mal aufgerufen und enthält 16 Antworten

letzter Beitrag von Hexworx am

Linien und Punkte für Graphen und Formen

  • Hallo,


    mit meinem "Hokube" Intro habe ich einen Bresenham-Algorithmus erstellt, den ich in der Zwischenzeit verbessert habe. So kann der sogenannte (Bresenham-)Fehler nun jenseits von +/- 128 liegen, obwohl die Fehlerberechnung 8-bitig bleibt. Das hat auch zur Folge, dass Linien kein Delta x > 255 haben können. Es ist aber problemlos möglich, Linien zu ziehen, die <255 anfangen und >255 aufhören und umgekehrt. Der Bresenham setzt auf dem Plot-Algorithmus im C64 Intern auf.
    Im Assembler-Code steckt noch viel Optimierungspotential. Aber übertragen auf Basic ist es wie Lichtgeschwindigkeit.
    Ich wollte immer schon in Basic Punkte und Linien schnell ziehen können und mich nicht totwarten müssen weil ich die Bitmap mit Basic beschreibe.


    Also habe ich mein Linienprogramm in einen Basic-Starter eingebunden und ein zweites Beispielprogramm gebaut das zeigt, wie man die neuen Funktionen nutzen kann:


    1.) load"linecmd.prg",8
    2.) run
    3.) warten bis Erfolgsmeldung
    4.) load"ltst4.prg",8
    5.) run


    Wenn ihr ltst4.prg listet, seht ihr, dass das Unterprogramm 2000 sich um Linien kümmert und 3000 um Punkte. Die Parameter-Übergaben seht ihr an den Beispielen ab Zeile 20. Die Routine 1000 bringt uns nach gedrückter beliebiger Taste wieder ins Basic zurück von wo aus wir unseren Basic Code ändern und erneut mit run starten können.


    Linien kann man ziehen von x=1 bis y=320 und y=1 bis y=200. "0" geht nicht.
    Punkte kann man setzen von x=1 bis x=318 und y=1 bis y=199. Diese Einschränkuing bringt der Plot-Algorithmus aus dem C64 intern mit. Ich habe das auch erst bemerkt, als ich zum Testen den Rahmen umgefärbt habe um die Genauigkeit an den Rändern zu prüfen.
    Ich habe also bei Linien in x- und y-Richtung 1 Pixel Versatz. Dafür ist es schnell und präzise genug für Graphen und Formen. Mit Fließkommawerten wird es genau (der gleiche Bresenham in Basic ist auf den Punkt exakt) aber langsam.


    Mit poke 49153,0 sorgt man dafür, dass jeder Aufruf des Linienprogramms dazu führt, dass die Bitmap gelöscht wird. poke 49153,1 sorgt dafür, dass der Bitmap-Inhalt stehen bleibt, auch nach Programmende und mit erneutem Ändern und run im Bild weiter gemalt werden kann.
    Man muss ein Folge-Programm wie ltst4.prg immer zuerst mit einem Linienaufruf (2000) starten, denn das initialisiert automatisch die Grafik. Ein Aufruf der Plot-Routine setzt zwar den Punkt in der Bitplane, sorgt aber nicht für einen Wechsel in den Grafikmodus. Ich gehe davon aus, dass man als erstes ein Koordinatensystem malt und dann erst plottet.


    Adressen:


    Das Linienprogramm liegt ab $4000. Die Bitmap ab $2000, der Bildschirm bei $0400. $c001 habe ich reserviert als Flag zum Löschen/nicht Löschen der Bitmap beim SYS Aufruf des Linienprogramms. Die Plot-Routine innerhalb des Linienprogramms liegt bei $4260.


    Natürlich ist es am C64 in V2 Basic nicht so toll zu zeichnen wie am CPC, aber es gibt eben dennoch einen Weg, die Rechenpower des V2 Basic mit schnellen Punkt- und Linienroutinen zu verbinden. Auf USR habe ich vorerst verzichtet. Vielleicht mache ich demnächst mal einen eigenen BASIC Befehl.


    Ich hoffe der ein oder andere kann das vorliegende Linien-Programmpaket in V2 Basic für eigene Experimente nutzen.


  • Präzisierung:


    Ursprüngliche Aussage:
    Linien kann man ziehen von x=1 bis y=320 und y=1 bis y=200. "0" geht nicht.


    Korrektur:
    Man kann nicht von 0 bis 320 ziehen.
    Man kann Linien von 0 bis 255 ziehen und von 255 bis 320. oder von 1 bis 320.


    Die "0" ist nicht per se verboten.


    Man kann auch von y=0 bis y=199 und von Y=50 bis 200. Oder von y=1 bis y=199. Aber nicht von 0 bis 200. Sonst sieht es nicht sauber aus.


    Die Anzahl gesetzter Punkte darf in y Richtung nicht 200 übersteigen und in x-Richtung nicht 320.

  • Und noch einer:


    Wenn man Zeile 3034 in ltst4.prg ändert in


    3034 if xa>255 then b=1:xa=xa-256:goto 3040


    dann sind Plot-Positionierungen zu 100% exakt. Der Geltungsbereich ist y= 0 bis 199 und x=0 bis 319.


    Danke MJ für den Tip.


    Der Linienalgorithmus muss umgeschrieben werden um die Ungenauigkeit von 1 Pixel bei den Längen beim Linien ziehen herauszubekommen.


    Für Diagramme reicht es nun, gerade weil das Plotten einzelner Punkte jetzt pixelgenau ist.

  • So. Jetzt laufen auch die Linien exakt und hören genau da auf wo man es sagt.
    Der Geltungsbereich ist wie beim Punkt 0-319 und 0-199. Man braucht die ltst4.prg Version wie unten abgedruckt.


    Es bleibt, dass die 0 als Startkoordinate beim Linien ziehen Probleme macht. Das liegt am Asm-Algorithmus, den ich noch ändern muss.
    Jede Linie die man ab Koordinate 1 (egal ob x oder y) zieht wohin man will, die sitzt nun exakt.


    Die Korrekturen im Basic Listing gehören eigentlich in die Maschinensprache-Routine, aber so hat man jetzt schon etwas das wirkt.


  • @ daybyter


    Ja, hier:


    http://csdb.dk/release/?id=156090


    Die Folge davon ist dieses kleine Projekt hier.


    Denn ich möchte am C64 solche Sachen machen wie ich es hier getan habe:


    http://www.cpcwiki.eu/forum/ap…tool-for-cpc464-and-6128/


    Man merkt halt je gründlicher man in etwas reingeht, wie viele Schwächen sich auftun. Aber ich seh es positiv und bleibe dran, besser zu werden.

  • Die Korrektur (256 statt 255 zum subtrahieren) solltest Du für die Zeilen(paare) 2034/2035, 2038/2039 und 3034/3035 durchgehend anwenden.


    Mit folgender 'spekulativer Befehlsausführung' (Struktur ELSE ... IF ... THEN ... an Stelle eines simulierten ELSE bei IF ... THEN ... ELSE) fällt dann auch das GOTO weg:

    Code
    1. 2034 B=0:IF X1>255 THEN B=1:X1=X1-256
    2. 2035 <entfällt>
    3. 2038 E=0:IF X2>255 THEN E=1:X2=X2-256
    4. 2039 <entfällt>
    5. 3034 B=0:IF XA>255 THEN B=1:XA=XA-256
    6. 3035 <entfällt>

    Den Trick kann man eigentlich fast immer anwenden. Üblicherweise packt man hier den häufiger zu erwartenden Fall in den ELSE-Zweig und formuliert die IF-Bedingung sinnvollerweise so, daß der THEN-Zweig derjenige ist, der seltener ausgeführt wird.


    Da B bzw. E eigentlich ja das Hi-Byte von X<was_auch_immer> darstellen sollen, läßt sich das auch ganz ohne IF programmieren:

    Code
    1. 2034 B=INT(X1/256):X1=X1-256*B

    ... analog für die Zeilen 2038 und 3034. In Maschinensprache taucht dieses Konstrukt dann ohnehin nicht mehr auf, da die 16-Bit-Zahlen im Regelfall ja schon frei Haus als Low- und High-Byte angeliefert werden. :)


    Gruß,


    Michael

  • Hi Mike,


    in einer Vor-Version von Post 4 hatte ich die 256 auch in der 2000er-Routine drin.
    Nur dann fiel mir auf, dass der Linienalgorithmus den letzten Punkt der Linie nicht mehr plottet.


    Es klappt nur mit -255 und dem Gedöns von 2000 bis 2006. Dann beginnen und enden die Linien genau da, wo sie auch erscheinen würden, wenn man Punkte an den Anfang und ans Ende plotten würde mit der Plot-Routine. Die Linien dürfen nur nicht bei 0 beginnen sonst bekomme ich 8 Zeilen Versatz. Den würde man mit -256 statt -255 vermutlich umgehen, aber dann enden meine Linien wieder einen Pixel zu früh. Das liegt am Asm-Algorithmus, da wurde die Schleife nicht richtig gesetzt, bzw ich hätte den ersten Punkt plotten sollen und dann die Schleife abzählen.


    Danke für Deine Kürzungs-Tips.


    Den Linien-Algorithmus kennst Du schon. Die einzige Änderung die ich gemacht habe, war, das, was Du mir damals vorgegeben hattest, mit dem Testen von Bit 7, ob der Fehler negativ geworden ist, per Carry Flag abzufangen statt mit bit oder bmi.
    Ich habe das nochmal neu geschrieben als ich gemerkt habe, dass bei Fehler-Resultaten über 128 ein Unterlauf angenommen wurde, den es gar nicht gab. Schabbelige Linien waren die Folge. Der Rest des Algorithmus ist imme rnoch offene Baustelle. MJ hat mir ein Super Beispiel geliefrt wie man das schnell und effektiv macht, aber ich möchte auch aus eigener Kraft besser werden, daher die Arbeit an meinem eigenen Code.

  • Es gab noch einen Fehler in Zeile 2002 für das "Rückwärts ziehen" von Linien. Der ist hier korrigiert. Wenn man jetzt bei x=319 beginnt und bei x=200 aufhören will, hört er auch bei 200 auf statt bei 202. Wenn man bei 200 startet und bei 319 aufhören will, das hat er vorher schon geschafft.



    @ Mike
    Ich konnte Deine eigentlich sinnvollen Korrekturen nicht einbauen. Das Konstrukt ohne IF führt dazu, dass einzelne Werte 256 sein können. Ich poke die Einzelwerte aber in einzelne Bytes im Speicher und ein Byte kann nur 255 aufnehmen. Ich löse das 256 Thema künftig komplett anders über einen besseren Algorithmus. Das Weglassen von b=0 und e=0 im IF-Konstrukt ist zwar logisch richtig, aber ich muss b und e sowieso von Hand nullsetzen, wenn ich die Routine 2000 mehrmals hintereinander innerhalb desselben Basuic Programms aufrufen will. Sonst bleibt eine gesetzte 1 einfach stehen obwohl sie beim zweiten Durchlauf zu einer 0 werden müsste.


    Anbei also die finale Verarbeitungsroutine, leider nur mit der Korrektur in Zeile 2002.


  • Jetzt kann man sagen, dass man einen uneingeschränkten Line-Befehl hat, der pixel-exakt ist. Auch das Ziehen langer Linien mit Delta x > 255 geht jetzt mit nur einem Befehl. Es werden keine letzten/ersten Pixel mehr verschluckt und die Linien sitzen bei Start- und Endpunkt genauso, wie sie auch per Plot Befehl sitzen müssen. Man kann auch bei jeder Länge die 0 als x1, bzw. y1 angeben und sie wird mitgedruckt. Geltungsbereich ist 0-319/0-199.


    Die schlechte Nachricht ist, dass ich mit dem Basic-Konstrukt ltst4.prg alle Schwächen im Asm-Algorithmus abfange und User-Eingaben passend verarbeite. Vieles davon gehört nicht ins Basic, sondern in einen sauberen 16Bit Asm Bresenham Algorithmus, den ich in den nächsten Wochen mit Mikes und MJs Hilfe komplett neu schreiben werde.


    Ich konnte nur das jetzige Projekt nicht einfach so stehen lassen. Mit dem jetzigen Tool KANN man exakt arbeiten, auch wenn es mit Pflastern und Rohrgeschlinge gemacht wurde.


    Von Zeile 2 bis 995 hat man freies Feld für eigenes Malen. Das Programmm MUSS mit einem Linienbefehl beginnen und direkt danach muss ein poke 49153,1 kommen, damit man im selben Basic Listing weiter Malbefehle ausführen kann. Wenn man das Programm per Tastendruck gestoppt hat, kann man mit poke 49153,0 das Löschen des Bildschirms vor dem nächsten Programmstart veranlassen (oder man lässt es und malt im Bestehenden weiter).


    Die Beispiele ab Zeile 7 zeigen, dass die Linien passen, auch mit abgesetzten Kontroll-Punkten unter/oberhalb einiger Linienenden.


    Immer erst load"linecmd.prg",8 und run. Dann load"ltst4.prg",8 und run oder editieren.


    Edit: ltst4.prg wurde um 1:23 Uhr nochmals aktualisert. Es fehlte noch eine Kleinigkeit.

  • Und hier noch eine Datei, die einfach nur "lt" heißt und nur einen einzigen Linienbefehl mit Positionskontrollen (Punkten) enthält.


    Das ist bequemer als ltst4.prg zu tippen und den ganzen Demo-Liniensalat zu haben. Man kann gleich was eigenes machen.


    Also load"lt",8 nach dem linecmd.prg gestartet wurde.

  • Anbei noch ein integrierter Funktionsplotter mit Transformationsgleichung für die Koordinatenumrechnung (gleich in Zeile 1 - xo/xu/yo/yu sind die x- und y-Grenzen nach oben und unten im eigenen System. Es wird dann auf BS-Koordinaten umgerechnet mit Funktionsaufruf fnx() und fny().


    Es sind enthalten:
    - f(y)=x^2
    - f(y)=sin(x)


    Dazu noch:
    - Kreis
    - Lange Querlinien von links oben nach rechts unten und rechts oben nach links unten mit nur einem Befehl pro linie


    Alle Linien wurden mit der Linienfunktion gezeichnet, also nicht durch Plotten einzelner Punkte.
    So ist z.B. der Kreis in Wirklichkeit ein 24-Eck. Dadurch ist der Bildaufbau schneller.


    Aus dem Zip:
    1.) load"linecmd",8
    2.) run
    3.) load"all",8
    4.) run


    Sobald ich den Bresenham in Asm neu gemacht habe, kommen die Linien auch schöner. Momentan sind sie bloß pixelgnenau, aber die in Asm verschluckten Anfangs- und Endpixel der Linien werden von Basic extra nachgeplottet. Der Bresenham sieht trotzdem nicht genau so aus, wie er aussehen müsste, wenn er nicht Start- und Endpixel verschlucken würde.


  • Sehr schön!

    Mit poke 49153,0 sorgt man dafür, dass jeder Aufruf des Linienprogramms dazu führt, dass die Bitmap gelöscht wird. poke 49153,1 sorgt dafür, dass der Bitmap-Inhalt stehen bleibt, auch nach Programmende und mit erneutem Ändern und run im Bild weiter gemalt werden kann.

    Warum nicht als gesonderter SYS-Einsprung? Am besten gleich vorn als Sprungtabelle (also z. B. SYS16387). Ansonsten wäre es aber auch schon schöner, eine Zeropage-Adresse zu nehmen (z. B. $02-$06).

    Auf USR habe ich vorerst verzichtet. Vielleicht mache ich demnächst mal einen eigenen BASIC Befehl.

    Es bietet sich dafür doch "SYS16384,X1,E,Y1,X2,B,Y2" förmlich an. Ich hab das mal spaßeshalber umgestrickt. Läuft gleich nochmal 10% schneller und die POKEs 102x können damit ja auch völlig entfallen. Soll ich das mal posten oder willst du es selber versuchen?

    Die schlechte Nachricht ist, dass ich mit dem Basic-Konstrukt ltst4.prg alle Schwächen im Asm-Algorithmus abfange und User-Eingaben passend verarbeite.

    16-Byte-Werte kann man auch problemlos mit dem SYS übergeben. Dann wäre es nur noch ein "SYS16384,X1,Y1,X2,Y2".

  • 16-Byte-Werte kann man auch problemlos mit dem SYS übergeben. Dann wäre es nur noch ein "SYS16384,X1,Y1,X2,Y2".

    Gesagt - getan: So ist es dann sogar 20% schneller. Nur der Part bei 5000 ff. ist noch etwas unschön. 3000 ff. könnte man auch noch umstricken.


    Bei mir sieht der BASIC-Teil jetzt so aus:

  • @ Hexworx!


    Stark! Ich schau mir das zu Hause in Ruhe an.


    Aktuelle Fehler des Asm-Bresenham:

    • Der Asm-Bresenham fängt immer an der richtigen x/y-Position an, er hört aber immer ein Pixel zu früh auf. Das betrifft das Zeichnen in alle 8 Richtungen
    • start-x-Koordinate 0 mag er nicht, sollte er aber. Das wird per Basic unschön abgefangen
    • Der Asm-Bresenham unterstützt zwar Linien, die x<255 anfangen und x>255 aufhören und vice versa, er untersützt aber kein Delta x von >255. Das Abzählen von Delta x und die Fehlerberechnung erfolgt (aus meiner eigenen Faulheit und Schiss, wieder ein neues Fass mit Bugs aufzumachen) 8-bittig


    Das alles mache ich heile in Asm. Das wird dann mit Deinen Verbesserungen kombiniert und man kann am C64 anständig in Basic v2 plotten und Lines ziehen. :-)

  • So, hier ist nun die neue Version, die 16bittige Fehlerberechnung und Abzählen von Delta x als schneller Richtung enthält. Der Code wurde komplett neu in Assembler geschrieben und basiert auf einer Basic 7.0 Vorlage für den C128, die @Mike mir zur Verfügung gestellt hat. Danke Mike! Die neue Routine ist auch um 92 Bytes kürzer obwohl sie mehr kann.


    Die Basic-Krücken mit dem künstlichen Linien verlängern bei Delta x > 255 fallen nun weg, genauso wie das Extra-Plotten von Start- und Endpunkt im Vorgänger-Code - jetzt ist immer alles exakt, auch wenn x=255 über- oder unterschritten wird oder eine Linie eine x-Spannweite > 255 hat. Ich wage sogar zu sagen, die Linienroutine malt genauso wie der Line-Befehl im C128. Ich kann jedenfalls nicht mehr feststellen, dass beim C128 die Linien anders verlaufen. Vergleiche dazu auch die Bilder im Anhang.


    Nochmal zur Auffrischung die Bedienungsanleitung um eigene Linien und Punkte in V2 Basic am C64 zu benutzen:


    1.) load"lineload",8
    2.) run
    3.) load"all2",8
    4.) run


    Bevor ihr neu "loaded", poke49153,0 (return) eingeben um die Bitmap zu löschen. Tut ihr das nicht, malt sich das neue Bld ins alte, das dann noch da ist.
    Nach dem ersten Linienefehl im eigenen Listing immer ein poke 49153,1 nachschieben (einmalig), damit beim erneuten Aufruf der Linienroutine der Bs nicht gelköscht wird.


    Statt "all2" könnt ihr auch die anderen Dateien nehmen wie "pat2" oder "niko2". Ab Zeile 995 beginnt der Standard-Sockel, der bei jeder Datei gleich ist. Ab 1000 wird zurück ins Basic gesprungen, 2000 zieht Linien und 3000 plottet Punkte. Die Farbsetzung des Rahmens habe ich nur zum Testen drin, damit man sieht dass alles pixelgenau ist.


    In den ersten Zeilen 1-5 von jedem Programm findet man u.a. eine Koordinatentransformationsgleichung bei der man xoben/xunten/yoben/yunten als Koordinatensystemgrenzen festlegen kann. Der C64 transformiert das dann auf BS-Koordinaten.


    Zwischen Zeile 5 und 995 kann sich also jeder austoben wie er mag und den Krempel dazwischen löschen.


    Der Unterschied ist mit bloßem Auge nicht groß, für mich fühlt er sich aber sehr groß an, es ist nun etwas "richtiges".


    Danke an Mike und MJ für die nette Hilfestellung! Danke an Hexworx für die kreative Beschleunigung. Ich habe im Asm-Listing nochmal den Sprung in die Punktsetzroutine beim Linien ziehen kürzer gemacht, ich finde es geht jetzt gut, bin für Verbesserungen natürlich offen und werde auch in Zukunft weiter optimieren.

  • Super!


    poke49153,0 (return) eingeben um die Bitmap zu löschen. Tut ihr das nicht, malt sich das neue Bld ins alte, das dann noch da ist.
    Nach dem ersten Linienefehl im eigenen Listing immer ein poke 49153,1 nachschieben (einmalig), damit beim erneuten Aufruf der Linienroutine der Bs nicht gelköscht wird.

    Iiiieh..., ist das immer noch drin... :D


    ich finde es geht jetzt gut

    Keine Frage.


    bin für Verbesserungen natürlich offen und werde auch in Zukunft weiter optimieren

    Siehe #13.