Technische Universität München Institut für Informatik Lehrstuhl für Computer Graphik & Visualisierung WS 2009 Praktikum: Grundlagen der Programmierung Aufgabenblatt 9 Prof. R. Westermann, R. Fraedrich, R. Fülöp, H.G. Menz 7. Januar 2010 Abgabe: In KW 2/3 (14. Januar 20. Januar) vor der Übung. Generische Datenstrukturen 9.1 (Ü) Allgemeine Fragen (a) Was ist eine generische Klasse? (b) In dem Package de.tum.ws2009.grprog.uebungsblatt09 finden Sie die Klasse GenericItem die einen generischen Eintrag in einer einfach verketteten Liste darstellt. Wie unterscheidet sich diese generische Klasse von einer nicht-generischen Klasse? public class GenericItem <T > { public T value ; public GenericItem <T > previous ; public GenericItem <T > next ; public GenericItem ( T i) { value = i; (c) Welche Änderungen müssen an der Klasse GenericSingleLinkedList vorgenommen werden, dass beliebige Datentypen in ihr gespeichert werden können? public class GenericSingleLinkedList { private GenericItem head = null ; public void add ( String v) { GenericItem item = new GenericItem ( v); item. next = head ; head = item ; public String tostring () { String result = "["; for ( GenericItem curr = head ; curr!= null ; curr = curr. next ) { result += curr. value ; 1
if ( curr. next!= null ) result += ", "; return result + "]"; public static void main ( String [] args ) { GenericSingleLinkedList list = new GenericSingleLinkedList (); list. add (" String 1"); System. out. println ( list ); list. add (" String 2"); System. out. println ( list ); list. add (" String 3"); System. out. println ( list ); (d) Wie wird nun eine GenericSingleLinkedList von Strings angelegt? Auf was müssen Sie achten, wenn Sie eine Liste mit Integer Werten anlegen möchten? (e) Java bietet eine große Menge an fertig implementierten generischen Datentypen (Collection) an, so zum Beispiel: HashSet für eine nicht sortierte Menge von Objekten, ArrayList und LinkedList für Listen von Elementen und Schlangen, wie zum Beispiel den PriorityQueue. Sie sind alle in Java API (http://java.sun.com/javase/6/docs/api/) beschrieben. Wie legt man eine LinkedList von Strings und Integers an? (f) Die Klasse Collections enthält viele Hilfsfunktionen, die den Umgang mit den Collections vereinfachen. ˆ Was machen die Methoden frequency(...), addall(...) und swap(...)? ˆ Welche Eigenschaften muss eine Collection erfüllen um mit sort(...) sortiert werden zu können? (Schlagen Sie hierzu selbstständig in der Dokumentation nach.) (g) Was macht folgende Schleife? Was muss setofstrings erfüllen, damit dies funktioniert? Set < String > setofstrings = new HashSet < String >(); [...] for ( String s : setofstrings ) { System. out. println (s); 9.2 (Ü) Anwendung von Generics In den letzten Übungsblättern mussten Sie oft eine Anzahl von Elementen verwalten, die in Arrays oder selbst implementierten Datenstrukturen gespeichert waren. Im Folgenden sollen Sie nun bei einigen der Projekte die Java Collections verwenden. Adressbuch Erstellen Sie, wie in Übungsblatt 8, ein Adressbuch in der Klasse Addressbook. Die benötigten Klassen, u.a. für Firmenkontakte Firm, Kollegen Colleague und private Kontakte PrivateContact 2
befinden sich im Package de.tum.ws2009.grprog.uebungsblatt08.solution.addressbook. Ersetzen Sie nun in der Klasse Addressbook die Liste AddressbookItemList mit den Einträgen des Adressbuches durch eine LinkedList die AddressbookItems akzeptiert und korrigieren Sie auftretenden Fehler. Verwenden Sie nun die Klasse Collections um das Adressbuch zu sortieren. Erstellen Sie hierzu die folgende neue Methode in Addressbook: public void sortbyname () { Collections. sort ( items ); In der aktuellen Version können zwei Adressbucheinträge jedoch noch nicht miteinander verglichen werden. Stellen Sie sicher, dass die Klasse AddressbookItem das Interface Comparable implementiert. Ersetzen Sie außerdem in Klasse Person die dort für die Kontakteeinträge verwendetete ContactItemList durch eine LinkedList. Krankenhaus Mit Hilfe von generischen Datentypen lässt sich die Krankenhausaufgabe aus Übungsblatt 7 sehr schön mit einer PriorityQueue und einer normalen Schlange ArrayDeque lösen. In einem Priority Queue werden die Elemente sortiert gespeichert. Gewährleisten Sie zunächst, dass Patienten entsprechend der Zahlungswahrscheinlichkeit ihrer Krankenkassen verglichen werden können. Gehen Sie dabei ähnlich wie bei den AddressbookItems vor. Achten Sie auf den korrekten Rückgabewert der compareto Methode. Erstellen Sie in der Klasse WaitingRoom anschließend zwei Schlangen: Eine für die Notfälle und eine für die normale Fälle wie in Aufgabe 7.3 gefordert. Implementieren Sie nun die Methoden void add(patient p), void addemergency(patient p), Patient get() und String[] tostringarray(). 9.3 (Ü) Java Collections Framework Das Java Collections Framework ist eine Sammlung von Klassen und Interfaces rund um verschiedene Container-ADTs. Einen Einstieg in die zugehörige Dokumentation finden Sie z.b. unter http://java.sun.com/docs/books/tutorial/collections/index.html. Zeichnen Sie ein Klassen-Diagramm, das folgende Interfaces und Klassen umfasst: ˆ Interfaces: Iterable, List, Collection, Queue, Deque, Set, Iterator, ListIterator ˆ Klassen: AbstractCollection, AbstractQueue, PriorityQueue, ArrayDeque, AbstractSet, HashSet, AbstractList, ArrayList, AbstractSequentialList, LinkedList Die meisten davon finden Sie im Package java.util. Sie müssen in dieser Aufgabe keine Methoden oder Attribute einzeichnen. Beschränken Sie sich auf die Namen der Interfaces / Klassen und deren Beziehungen untereinander. Sie müssen außerdem keine transitiven Beziehungen einzeichnen. Beispiel: Wegen List Collection 3
und Collection Iterable gilt auch List Iterable, aber Letzteres müssen Sie nicht einzeichnen. 9.4 (H) Binärbaum (5 Punkte) In dem Eclipse-Projekt zu diesem Blatt finden Sie eine Klasse BinaryTree. Sie sollte Ihnen aus Blatt 6 bekannt vorkommen. Die Klasse speichert Elemente vom Typ Friend. Versehen Sie BinaryTree mit einem Typparameter T, so dass allgemein Elemente des Typs T gespeichert werden können. Hinweis: Möglicherweise muss T auf gewisse Typen beschränkt werden. 9.5 (H) Dreiecksmesh (15 Punkte) Mit dem Raytracer in Übungsblatt 8 können bisher nur Kugeln und Cuboide gerendert werden. Um aufwendigere Szenen zu erzeugen, könnte man nun z.b. sogenannte Dreiecksnetze (Meshes) einbinden. Im einfachsten Fall besteht ein solches Mesh aus einer Dreieckswolke, also einer Menge von nicht untereinander verknüpften Dreiecken. Jedes Dreieck ist durch drei Vertices (Raumpunkte) und die Oberflächennormalen und Texturkoordinaten in jeder dieser Ecken bestimmt. In der Klasse BottleOBJ finden Sie nun ein String Array mit einem solchen Dreiecksnetz. Konvertieren Sie das in dem Array kodierte Dreiecksnetz in eine Liste von Dreiecken vom Typ Triangle. Bei der Kodierung des Dreiecksnetz handelt es sich um eine vereinfachte Form des OBJ Dateiformats das von vielen 3D Programmen akzeptiert wird. Das Mesh liegt als Indexliste vor. Dies bedeutet, dass alle Vertices (Raumpunkte), Oberflächennormalen und Texturkoordinaten in verschiedenen Listen gespeichert wurden. In einer weiteren Liste wird schließlich definiert wie die Einträge der obigen Listen die Dreieck definieren. Das String Array ist also wie folgt aufgebaut. Eine Zeile mit einem am Anfang ist ein Kommentar und kann, wie eine leere Zeile auch, ignoriert werden. Die relevanten Zeilen beginnen entweder mit einem v, vn, vt oder f. Jede Zeile mit einem v am Anfang enthält einen Vertex mit den Koordinaten x, y und z. v <x> <y> <z> Jede Zeile mit einem vn speichert eine Normale, wobei diese nicht zwingend die Länge 1 haben muss, d.h. der Vektor muss noch normiert werden. Eine Normale hat auch wieder drei Komponenten. vn <x> <y> <z> Die Texturkoordinaten werden durch Zeilen mit vt angegeben und haben nur zwei Komponenten. vt <u> <v> Die Zugehörigkeit der Vertices, Normalen und Texturkoordinaten zu den Dreiecken wird durch die Zeilen mit einem f am Anfang festgelegt und enthält drei Triple die wie folgt zu lesen sind: 4
f v1/t1/n1 v2/t2/n2 v3/t3/n3 wobei vi den Index in der Vertexliste, ni den Index in der List mit den Normalen und ti den Index in der Texturkoordinatenliste angibt. (Für i {1, 2, 3.) Bitte beachten Sie, dass bei den eben genannten Indizes mit 1 zu zählen begonnen wird und nicht wie bei Arrays oder Listen bei 0. Ein einzelnes Dreieck mit den Eckpunkten (0,0,0), (0,1,0) und (1,0,0) könnte also z.b. durch folgende Zeilen repräsentiert werden: # vertices v 0 0 0 v 0 1 0 v 1 0 0 # normals vn 0 0 1 # textur coordinates vt 0 0 vt 0 1 vt 0.5 1 # faces f 1/3/1 2/1/1 3/2/1 Beachten Sie, dass in diesem Fall für alle drei Eckpunkte die Normalen gleich sind, also nur ein Eintrag für die Normalen vorhanden ist. Die letzte Zeile enthält schließlich die Zuordnung der Einträge in den Listen zu dem Dreieck. In diesem Fall wird also das Dreieck aus den Vertexeinträgen mit den Indizes 1, 2 und 3 zusammengesetzt (die ersten drei Zahlen nach dem f). Die Texturkoordinaten sind durch die jeweils zweiten Indexeinträge gegeben. Die Indizes zu den Normalen in diesen Punkten sind durch die Zahlen an dritter Stelle gegeben (1 1 1). Alle Eckpunkte dieses Dreiecks haben also die gleiche Normale. Ein Beispiel mit zwei Dreiecken könnte also so aussehen: v 0 0 0 v 0 1 0 vt 0 0 vt 0 1 v 1 0 0 vn 0 0 1 v 1 1 0 vt 0.5 1 f 1/3/1 2/1/1 3/2/1 f 2/3/1 3/1/1 4/2/1 Beachten Sie bitte, dass die Vertices, Normalen, Texturkoordinaten und Dreiecke nicht zwingend getrennt in separaten Blöcken definiert werden müssen. 5
In den Klassen TriangleOBJ und TwoTrianglesOBJ finden Sie weitere Objekte mit denen Sie Ihre Implementierung debuggen und testen können. Achten Sie beim Erstellen des Meshes darauf, dass in dem String Array die Normalen per Vertex gegeben sind. In der Klasse Triangle ist aber nur die Normale für die Dreiecksfläche gefordert. Sie können die Dreiecksnormale einfach durch mitteln der Vertexnormalen bestimmen. Anmerkung 1: Sie können die Texturkorrdinaten ignorieren, da die gegebene Dreiecksklasse keine Texturinformation speichert. Anmerkung 2: Zum Lösen dieser Aufgabe benötigen Sie keinen funktionsfähigen Raytracer. Sie können sich das von Ihnen generierte Dreiecksmesh auch mit dem Renderer in Package de.tum.ws2009.grprog.uebungsblatt09.trianglemesh.display ansehen. Konfigurieren Sie dazu Eclipse wie in der Datei README FIRST.txt beschrieben und starten Sie dann die Klasse DisplayOBJ. Tip: Sehen Sie sich die Dokumentation der Collections und der String Klasse an. Verwenden Sie die gegebene Klasse Triangle in Package de.tum.ws2009.grprog.uebungsblatt08.solution.raytracer.primitives (in raycaster.jar) für Ihre Lösung. Implementieren Sie alle durch das Interface de.tum.ws2009.grprog.uebungsblatt09.solution.trianglemesh.trianglemeshinterface geforderten Methoden in der Klasse de.tum.ws2009.grprog.uebungsblatt09.solution.trianglemesh.trianglemesh. 6