# 2d Ansicht mit Java 3d



## JKL (23. Dez 2012)

Hallo,
nun habe ich mich doch erstmals in einem Forum angemeldet, da ich zu dem Thema wenig gefunden habe und hoffe das man mir hier helfen kann.
Ich habe ein Zeichenprogramm geschrieben, in welchem ich drei 2d-Ansichten und eine 3d-Ansicht integriert habe. Die Geometrien bestehen aus Kontrollpunkten, diese können innerhalb der 2d-Ansichten per Mausklickt erstellt/ausgewählt werden und per drag&drop verschoben werden.
Nun merke ich, dass bei aufwändigen Geometrien (bestehen im Grunde aus sehr vielen Linien) die 2d-Ansichten extrem langsam werden, wenn ich einen Punkt anklicke oder verschiebe. Die 3d-Ansicht bleibt jedoch ziemlich fix und wird schnell neu gezeichnet. Von daher kann ich davon ausgehen das meine Geometrien schnell neu berechnet werden nur das zeichnen innerhalb meiner 2d-Panels zu lange dauert. Daher würde ich gerne 2d-Ansichten mittels Java3d basteln. Jedoch weiß ich nicht wie man in Java3d Mausklicks auswertet bzw. prüft ob man ein Objekt angeklickt hat. Bisher habe ich immer nur die Standard MouseListener von Java3d zum zoomen verschieben und rotieren der Kamera angemeldet. Ich weiß zwar das man da irgendwie einen Strahl in seine Scene schießen kann, aber ebend nur "irgendwie".
Weiterhin sollen die 2d-Ansichten auch keine Perspektive haben ( da habe ich mir schon Gedanken zu gemacht, man könnte ja einfach durch einen "Transform3d" eine 2d-Projektion auf seine "TransformGroup" anwenden, aber vielleicht gibt es ja noch eine einfachere Methode).

Ich bin auch für ganz andere Vorschläge offen, vielleicht ist mein 2d-Panel auch einfach ineffizient und ich könnte Java3d im Hinblick auf die 2d-Ansichten ganz umgehen.

Also denn, ich bin gespannt und freue mich auf eure Antworten!


----------



## Marco13 (23. Dez 2012)

Beschreib' mal genauer... *rat:* In J3D zeichnest du ein Drahtgittermodell, und in den Panels machst du viele g.drawLine(compute2D(...)) aufrufe?

Das mit dem Picking könnte schwierig werden, aber für den 2D-Fall musst du es ja schon haben...?!


Die Perspektive rausnehmen... sollte im Idealfall schon mit view.setProjectionPolicy(View.PARALLEL_PROJECTION); gehen.


----------



## JKL (23. Dez 2012)

Hallo Marco13,
die Antwort kam ja fix, vielen Dank dafür.

Ich werde mal versuchen meinen Programmaufbau etwas genauer beschreiben.

g.drawLine verwende ich nicht, zumindest nicht direkt. Meine Geometrie-Klassen haben Jeweils einen Shape (für Java2d) und eine Branchgroup (für Java3d) als Attribut. Diese werden bei Veränderung der Geometrie innerhalb der Geometrie-Klasse in EINER Methode neu berechnet, also ist die Berechnung der Shapes nicht das Problem, da die 3d-Ansicht ja trotzdem noch fix ist.
Meine Paint-Methoden (eine für 2d, eine für 3d) liegen innerhalb von anderen Klassen und bestehen im Grunde nur aus einer Schleife über alle Geometrien. In dieser wird dann g.draw(shape) bzw. addBranchGroup aufgerufen. Da selbst bei einer "komplexen" Geometrie (sagen wir ein Shape mit 100*100 Linien) schon Probleme auftreten ist diese Schleife auch nicht das Problem. 
Daher ist die Schwäche wohl das g.draw(shape) von Graphics2d. Wahrscheinlich wird innerhalb von g.draw(Shape) tausende Male drawLine aufgerufen. Aber das weiß ich nicht genau.
Sollte das ganze immer noch unverständlich sein kann ich dir auch gerne ein Klassen-Diagramm oder mein Programm zukommen lassen, soweit du noch gewillt bist mir zu helfen .

Das Anklicken in Java2d gestaltet sich ja im Grunde recht einfach. Java2d kann abfragen ob ein Punkt sich innerhalb eines Shapes befindet, wenn man weiß wie das klappt ist das schnell gemacht. Bei Java3d wüsste ich nicht wie das funktioniert(hier schaffe ich es ja nicht einmal Mausklick, der innerhalb der 3d-Umgebung getätigt wird überhaupt zu registrieren. An der stelle hab ich nur MouseTranslate und MouseRotate zum Drehen der Szene verwendet und diese sind von Java3d schon vorgegeben und müssen einfach an die BranchGroup gehangen werden).

setProjectonPolicy werd ich mir morgen mal anschaun, danke für den Tipp.

Ich hoffe auf Rückmeldung und wünsche schonmal Frohe Feiertage.


----------



## Marco13 (23. Dez 2012)

Klassendiagramme sind was für Erstsemestler und Manager. Die Leute dazwischen verwenden [c]Code[/c] :smoke: 

Sehe ich das richtig, dass die Shapes bei jeder Veränderung (d.h. immer wenn sich die Maus bewegt) neu aufgebaut werden? Geht es um ein Rechtecksgitter mit 100x100 Punkten, irgendein Bezierflächending vielleicht? 
Wie das Shape#contains da reinspielt kann ich mir gerade nicht ganz vorstellen, aber auf einem SEHR komplexen Shape (also einem mit 100x100 Linien) kann auch das sehr teuer sein.

Picking in Java3D ist so eine Sache. Aber nicht speziell darauf bezogen, sondern ganz allgemein in bezug auf Java3D: Bei 3DJava gibt es viele Beispiele zu Java3D (genug für mich, um seit Jahren den Eindruck aufrecht zu erhalten, ich wüßte was über Java3D  ). Eine Websuche nach [c]java2s java3d picking[/c] usw. liefert meistens KSKBs, die sehr hilfreich sein können. Ohne sie konkret gelesen zu haben: Sowas wie Tick Tock Picking : Game3DJava oder IntersectTest.java : 6.0-JDK-Modulesjava-3dorgjdesktopj3dexamplespickingJava Open Source könnte man sich mal ansehen (speziell falls sich die Frage mit dem Picking noch stellt, sobald das mit dem 2D klarer ist)


----------



## JKL (23. Dez 2012)

Abermals danke.

Deine Einstellung zu Klassendiagrammen hätte ich gerade im ersten Semester auch so unterschrieben heute sehe ich das ein wenig anders. Jedenfalls wenn es sich nicht das eigene Programm handelt . Aber ich bin ja auch kein Informatiker.

Ja es geht um Rechteck-Gitter, unter anderem auch um Bezierflächen. Im 2d realisiere ich das mit einem "GeneralPath" und im 3d mit einem "IndexedLineArray". Die werden auch bei jeder Bewegung (MouseDragged) neu berechnet. Contains wird jedoch nur einmal (MousePressed) ausgewertet. Das ist aber denke ich auch nicht das große Hauptproblem. Auch wenn ich die Koordinaten per Eingabe (ohne Maus) verändere sind die 2d-Panels im Vergleich zu dem 3d-Panel langsam. 3d nutzt wahrscheinlich die Hardware besser aus.


----------



## Marco13 (23. Dez 2012)

Hmnaja... der GeneralPath muss ja komplett neu aufgebaut werden. Es gibt (im Gegensatz zum IndexedTriangleArray) ja keine Methode wie "setVertexPosition(index, position)". Wobei noch nicht ganz klar ist, was du wie aktualisierst. Poste am besten mal ein paar repräsentative Codestücke (ich weiß ja nicht, wie weit du schon eingrenzen konntest, WO genau die Zeit flöten geht...)


----------



## JKL (23. Dez 2012)

Also am meisten Zeit geht glaube ich in dieser Methode (Paint-Methode 2d) flöten, im Programmier-Eifer kommentiere ich allerdings immer sehr wenig. Unwichtige Passagen lasse ich der Übersicht halber mal raus und ersetze sie durch "..."

[JAVA=42]
@Override
    public void paint(Graphics g){
        shapes.clear();
        this.g2=(Graphics2D) g;
        ...
        int i=0;
        g2.setStroke(stroke);
        Stroke newstroke=new BasicStroke(thickness+10);
        for(Iterator<Geometrie> e=control.dataIterator();e.hasNext(){
            Geometrie geo=e.next();
            Shape shape=geo.getPaint(ansicht);
            MyAppearance app=control.getAppearance(i);
            if(app.visable){
                g2.setColor(app.getColor());
                g2.draw(shape);           
            }
            shapes.add(newstroke.createStrokedShape(shape));
            i++;
        }
        ...
    }
[/Java]

Hier habe ich mir einmal die Zeiten, die für die relevanten Methoden gebraucht werden (für eine Geometrie mit 100*100 Linien).

Berechnung des IndexedLineArrays bzw. GeneralPath:0.031s
Rendern der Shapes (Paint-Methode 2d xy):2.231s
Rendern der Shapes (Paint-Methode 2d yz):0.296s
Rendern der Shapes (Paint-Methode 2d zx:0.593s
Rendern der Shapes (Paint-Methode 3d):0.0s

Bei einem der drei Plots macht das nichtmal 0.5 fps und bei 3d ist die Zeit bei ca 30 fps (und da ist die Berechnung der GeneralPath's für die 2d-Ansichten mit drinne).

Die dreateStrokedShape-Zeile benötige ich nachher für mein picking. Das Ausblenden dieser Zeile macht wenig aus.


----------



## Marco13 (23. Dez 2012)

Seltsam. Ich hätte bei dem geposteten jetzt als erstes vermutet, dass "createStrokedShape" das teuerste ist. Dabei kann sehr (SEHR!!!) viel "Geometrie" entstehen....

Die Zeiten und den Code kann ich nicht ganz in Einklang bringen (und ... die Frage, wie du das gemessen hast, wäre auch mal interessant  Die Zeiten sehen aus, als könnte da schlicht auch der JIT was beigetragen haben.... ). 

Heißt das jetzt dass EIN Zeichenvorgang ca. 3 Sekunden dauert, und davon ca. 2.2 Sekunden beim Rendern EINER Ansicht draufgehen (wobei EINE Ansicht genau einem Durchlauf der geposteten Methode entspricht)? Nur aus Neugier: Wenn du das "Paint-Methode 2d xy" wegläßt, wie sehen die Zeiten dann aus?


----------



## JKL (23. Dez 2012)

Wie ich das messe .  Ich fürchte hier Spott zu ernten, nunja ich erstelle mir am Anfang und am Ende der Methode ein "Date" und ziehe die von einander ab und lass mir das in der Konsole auswerfen, keine Ahnung ob das legitim ist . JIT-Compiler sagt mir jetzt nichts, habe zwar gerade mal bei wiki reingeschaut aber da kann ich nichts zu sagen.
Das gerade xy am längsten dauert, hängt glaube ich von meiner Test Geometrie ab. Würde ich die einmal um 90 Grad drehen sieht das bestimmt anders aus. Die verschienden 2d Panele nutzen ja alle die selbe Paint Methode und greifen nur auf einen anderen Shape der Geometrie zu.


----------



## Marco13 (24. Dez 2012)

Solche Messungen sind eben immer schwierig. Das mit dem Date... naja, eher würde man

```
long before = System.nanoTime();
rechne();
long after = System.nanoTime();
System.out.println("Duration "+((after-before)*1e-6)+"ms");
```
machen, aber auch das hat nur in bestimmten Fällen Aussagekraft...

Wie sieht denn das 'getPaint' von
Shape shape=geo.getPaint(ansicht);
aus? Kann man genauer eingrenzen, wo die Zeit verbraten wird? (Und sei es, indem man obige Zeile mal durch ein
Shape shape=new Line2D.Double(x0,y0,x1,y1);
ersetzt und schaut, ob es dann immernoch 2.2 Sekunden dauert...)


----------



## JKL (24. Dez 2012)

Also wenn ich ne kleine Geometrie habe was mit 10*10 Linien, dann kommt da mit meiner Zeitmessung immer nur 0.0s vlt. mal 0.001s oder sowas raus.
Das getPaint spuckt nur in Abhängigkeit von dem Integer ansicht den jeweiligen shape der Geometrie aus.
Also im grunde nur case return ... .
Ich habe gerade mal die draw(shape) Zeile auskommentiert und dieselbe 100*100 Geometrie durchlaufen lassen wie oben und es dauerte nur noch Milisekunden. Natürlich sieht man so absolut nix. Draw wird bei nem großen GeneralPath wohl einfach zu langsam :shock:. Vielleicht befasse ich mich doch mit dem Picking in 3d :rtfm:, schaden kanns ja nicht.


----------



## Marco13 (24. Dez 2012)

OK, wenn das so weit eingegrenzt und damit deine Vermutung vorläufig bestätigt ist, kann man sich überlegen, was man tun kann. Entweder das ganze wirklich mit J3D machen, oder... falls das geht... mal ein KSKB erstellen, was NUR so eine Beispielgeometrie zeichnet (hast du noch irgendwie Antialiasing an, oder das Graphics2D mit rotate/scale transformiert oder so...?)


----------



## JKL (24. Dez 2012)

Scale und Translate benutze ich. Daran hatte ich gar nicht gedacht, dass muss java2d dann ja auch wieder bei jeder Linie noch mal extra machen. Aber drum rum komme ich da ja auch nicht, es sei denn ich skaliere schon immer sämtliche Eingabedaten. Das fände ich jedoch äußerst unelegant. Antialiasing hab ich nicht an, sofern das nicht standardmäßig der Fall ist.
Ich probiere mal zwischen Weihnachten und Neujahr ein wenig rum und schreibe vielleicht ein KSKB (netter Ausdruck ) das ich dann auch komplett posten würde.
Bis hierhin ersteinmal vielen Dank für dein Interesse und deine Hilfe.


----------



## Marco13 (24. Dez 2012)

FALLS das mit vergleichsweise wenig aufwand machbar ist, wäre es einen Versuch wert, nicht das Graphics2D zu transformieren, sondern die durchzuführenden Transformationen in einer AffineTransform zu "sammeln", und dann mit 
s=affineTransform.createTransformedShape(s);
ein transformiertes Shape zu erstellen. OB das schneller oder langsamer ist, ist im voraus schwer zu sagen und hängt vermutlich auch von zu vielen Faktoren ab, als dass man eine definitive Aussage machen könnte, aber man kann mal schauen.


----------

