# Rotation auf Knopfdruck



## Highchiller (2. Jul 2011)

Hallo Zusammen.

Ich habe eine TransformGroup in der ein Model steckt. Nun würde ich gern, dass wenn ich einen Button drücken, dieses Model sich dreht. (obendrein noch um die z-Achse)

Dank einem einleuchtenden Tutorial hab ich ein klein wenig über RotationInterpolator in Erfahrung bringen können. Aber das reicht irgendwie nicht aus für das, was ich vorhabe.

Wie könnte ich einen RotationInterpolator auf Knopfdruck verwenden? (und wie geb ich ihm an WIE er zu rotieren hat? Im moment macht der das nur um die X-Achse ohne das ich ihm das gesagt hätte)


----------



## Marco13 (2. Jul 2011)

Hm. Den Interpolator braucht man da nicht unbedingt. Einfach in der actionPerformed die Transform3D der TransformGroup neu setzen...?!

EDIT: Oder soll die Rotation dann starten und kontinuierlich weiterlaufen?!


----------



## Runtime (3. Jul 2011)

Mithilfe dieses Konstruktors kannst du die Achse angeben. Das Objekt dreht sich dann um die y-Achse des von "axisOfTransform" definierte Koordinatensystem.


----------



## Highchiller (3. Jul 2011)

Ich hab mir das wie bei so einer Google-Earth Karte vorgestellt.
Wenn ich auf den Button klicke, dreht er in einer sanften Animation die Darstellung um 90° nach links. Oder halt rechts, oder oben/unten... je nachdem was ich drücke.
So ungefähr zumindest.

Ich glaub ich muss mich erst mal genauer mit "Alpha" beshäftigen um davon einen Eindruck zu bekommen.


----------



## truesoul (3. Jul 2011)

Hallo.
Schreibe dir doch eine eigene Animationsklasse. Und wie Marco schon erwähnt hat musst du doch einfach nur die Transform3D der TransformGroup neu setzen. Und um daraus eine Animation zu machen einfach eine Klasse die z.B von Thread erbt. Da kannst du dann wunderbar nach deinen Wünschen anpassen. 

Sprich: 
// x y z werden in einer Schleife erhöht oder anders rum
transform3D.setTranslation(new Vector3d(x, y, z));
transformGroup.setTransform(transform3D);

Ansonsten Java3D Api lesen was für Interpolatoren gibt.


----------



## Highchiller (3. Jul 2011)

ok ich habs jetzt einigermaßen hinbekommen.
Jetzt aber noch mal eine Frage in ähnlicher Richtung.

Ich hab den Button jetzt gedrückt, dann dreht sich das Ding hübsch um meine gestellten 90°.
ABER, wenn ich den Button jetzt noch mal drücke, rückt er den Stein erst in die alte Position und dreht dann seine 90°. Er macht also genau das gleiche.
Ich will aber das er von seiner jetzigen Position aus, weiter dreht. Wie kann ich das erreichen?

Ich dachte mir würde irgendwie diese LocalToVWorld weiterhelfen. Aber irgendwie versteh ich das nicht.
Ich versteh allgemein nicht was die ganzen void get-Methoden sollen die obendrein noch einen Parameter übergeben bekommen.


----------



## Marco13 (4. Jul 2011)

Alpha wäre auch nicht so falsch, aber so eine "einfache" Animation kann man auch per Hand machen (Alpha kann so hübsch schneller werden und am Ende abbremsen und so...). Poste am besten mal ein bißchen Code. Allgemein könntest du entweder den winkel irgendwo speichern und bei jedem Klick um 90° erhöhen, oder ... naja, getLocalToVWorld würde auch gehen, sollte man aber nicht brauchen.

Die getter mit Parameter schreiben im allgemeinen das Ergebnis in den Parameter, z.B.
getTransform(transform3D);
schreibt das Ergebnis (was man eigentlich als Rückgabe erwarten würde) in den Parameter. Wurde früher wohl in erster Linie gemacht, um Objekterzeugungen zu sparen.


----------



## Highchiller (4. Jul 2011)

Na klar zeig ich gern mal her:

```
private ActionListener aclRotNeg = new ActionListener() {
		public void actionPerformed(ActionEvent e){			
			for (int i = 0; i <= 90; i++){
				PiecesT3D.setRotation(new AxisAngle4f(0f,0f,1f, (float) Math.toRadians(90*index - i)));
				try{
					Thread.sleep(10);
				} catch (InterruptedException exp){}
				PiecesTG.setTransform(PiecesT3D);
			}
			if (index == 3) index = 0;
			else index--;
		}
	};
```

Wie wir sehen ruf ich von einem Button diesen ActionListener auf. Dann wird das ganze Ding gedreht. Im Moment ist das also SEHR SEHR einfach gelöst.

Index ist dabei eine globale Variable um zu checken in welchem Quadranten ich bin. Sozusagen.

Als ich das mit Alpha probiert habe, hab ich das auch alles in den ActionListener gepackt. Das komische war, es ging nur einmal. 
Aber so eine wahnsinnig tolle Animation brauch ich jetzt auch nicht.


----------



## truesoul (4. Jul 2011)

Hi Highchiller

Speicherst du denn irgendwo die aktuelle Position? sehe da nur index aber dieses erhöhst du nicht in deiner forschleife. 



> Als ich das mit Alpha probiert habe, hab ich das auch alles in den ActionListener gepackt. Das komische war, es ging nur einmal.
> Aber so eine wahnsinnig tolle Animation brauch ich jetzt auch nicht.


Die Animation ging nur einmal oder wie meinst du das?
Wenn das der Fall ist schau dir mal die Konstruktoren von Alpha an, da kannst du wenn ich mich richtig erinner, angeben wie oft die Animation laufen soll. 
	
	
	
	





```
new Alpha(-1, 60000);
```
 war es glaube ich. Alpha
Beispiel: 

```
public BranchGroup createSceneGraph()
{

BranchGroup objRoot = new BranchGroup();

TransformGroup objSpin = new TransformGroup ;
objSpin.setCapabilities(TransformGroup.
ALLOW_TRANSFORM_WRITE) ;

Alpha alpha = new Alpha(-1, 60000) ;

RotationInterpolator rotInt = new
RotationInterpolator(alpha, objSpin) ;
rotInt.setSchedulingBounds(new Boundingsphere()) ;

objRoot.addChild(objSpin) ;
objSpin.addChild(new Clock()) ;
objRoot.addChild(rotInt) ;
objRoot.compile() ;
return objRoot ;
}
```


----------



## Highchiller (4. Jul 2011)

Naja ein Kreis hat 360°. Also 4 mal 90°. In "index" wird also gespeichert, der wie vielte 90° Winkel erreicht wurde. 0,1,2 oder 3.


Mit "einmal" meinte ich nicht wie oft er gedreht hat.
Ich hab das "drehen" in ein ActionListener gepackt und den dann mit einem Button aufgerufen.
Wenn ich den Button gedrückt habe. Ist er fein rotiert. Hab ich den Button dann noch mal gedrückt, passierte nix... Aber das soll nich das Problem sein hier


----------



## truesoul (4. Jul 2011)

Das (float) Math.toRadians(90*index - i) habe ich wohl vollkommen übersehen.

Habe mal hier ein Beispiel:

```
package java3d;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Enumeration;
import javax.media.j3d.*;
import javax.swing.*;
import javax.vecmath.Point3d;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.SimpleUniverse;
import javax.vecmath.AxisAngle4f;

public class RotationsBeispiel extends Applet { 
  TransformGroup objTrans;

  float angle = 0.0f;

  Transform3D trans = new Transform3D();

  JButton rotateB = new JButton("Start");

  private SimpleUniverse u = null;

  public BranchGroup createSceneGraph() {
    // Create the root of the branch graph
    BranchGroup objRoot = new BranchGroup();

    objTrans = new TransformGroup();
    objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    objRoot.addChild(objTrans);

    objTrans.addChild(new ColorCube(0.4));

    AWTInteractionBehavior awtBehavior = new AWTInteractionBehavior(
        objTrans);
    rotateB.addActionListener(awtBehavior);
    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
        100.0);
    awtBehavior.setSchedulingBounds(bounds);
    objRoot.addChild(awtBehavior);

    return objRoot;
  }

  public RotationsBeispiel() {
  }

  public void init() {
    setLayout(new BorderLayout());
    GraphicsConfiguration config = SimpleUniverse
        .getPreferredConfiguration();

    Canvas3D c = new Canvas3D(config);
    add("Center", c);

    JPanel p = new JPanel();
    p.add(rotateB);
    add("North", p);

    BranchGroup scene = createSceneGraph();
    scene.setCapability(BranchGroup.ALLOW_BOUNDS_READ);
    u = new SimpleUniverse(c);

    u.getViewingPlatform().setNominalViewingTransform();

    u.addBranchGraph(scene);

  }

  public void destroy() {
    u.cleanup();
  }

  public static void main(String[] args) {
    new MainFrame(new RotationsBeispiel(), 256, 256);
  }
}

class AWTInteractionBehavior extends Behavior implements ActionListener {

  private TransformGroup transformGroup;

  private Transform3D trans = new Transform3D();

  private WakeupCriterion criterion;

  private float angle = 0.0f;

  // create a new AWTInteractionBehavior
  public AWTInteractionBehavior(TransformGroup tg) {
    transformGroup = tg;
  }

  public void initialize() {
    criterion = new WakeupOnBehaviorPost(this, MouseEvent.MOUSE_CLICKED);
    wakeupOn(criterion);
  }

  public void processStimulus(Enumeration criteria) {
    new Thread(){

            @Override
            public void run() {
                for(int i = 0;i < 9;i++){

                        angle += Math.toRadians(10.0);
                        trans.setRotation(new AxisAngle4f(1f,0f,1f, (float) angle));
                        transformGroup.setTransform(trans);
                        try{
                            sleep(100);
                        }catch(InterruptedException exp){}
                }
            }
        
    }.start(); 

    wakeupOn(criterion);
  }

  // when the mouse is clicked, postId for the behavior
  public void actionPerformed(ActionEvent e) {
    postId(MouseEvent.MOUSE_CLICKED);
  }
}
```
Das führt jedes mal eine Rotation um 90° durch (auf x und z Achse). 
Mfg


----------



## Marco13 (4. Jul 2011)

Wie truesoul schon indirekt gezeigt hat sollte die Rotation auf jeden Fall von einem eigenen Thread gemacht werden (und nicht direkt mit einer for-Schleife in actionPerformed).

Abgesehen davon wäre eine von vielen möglichen Strukturen in Anlehnung an den ursprünglichen Code

```
class TheClass
{
    private int currentAngleDeg = 0;


    void somewhere()
    {
        button.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                doRotation(90);
            }
        });
    }


    private void doRotation(final int deltaDeg)
    {
        Thread t = new Thread(new Runnable()
        {
            public void run()
            {
                for (int i=0; i<deltaDeg; i++)
                {
                    currentAngleDeg += 1;
                    setzeRotationSoWieVorher(currentAngleDeg);
                }
            }
        });
        t.start();
    }

}
```

Von Feinheiten (wie z.B. dass der Button disabled sein sollte, während die Animation läuft) mal abgesehen...


----------



## Highchiller (4. Jul 2011)

Ahhh... ich bin grad vor 5min darauf gestoßen mich mal mit Threads zu beschäftigen. Davon hatte ich bisher noch gar keine Ahnung.

Vielen Dank  Das hilft sicher ungemein weiter, beschäftige ich mich später mal mit


----------



## Highchiller (6. Jul 2011)

Ich hab gelesen das ein Alpha Object quasi nachdem kompilieren startet.

Wie schreib ich mir jetzt also einen "Trigger" für Alpha der nicht von der Zeit abhängt, sondern von einer Aktion.
Zum Beispiel wird dank eines Observers immer eine update() Methode gestartet, wenn was passiert. Wie kann ich nun in dieser update() Methode das Alpha immer wieder neu triggern lassen?
Versteht ihr wo mein Problem liegt?


----------



## Marco13 (6. Jul 2011)

Ggf. kann man immer ein neues Alpha erstellen (mit passenden Zeiten), oder ganz auf das Alpha verzeichten, wenn es nur um einfache Animationen geht. Poste vielleicht mal mehr Code vom aktuellen Stand, idealerweise ein KSKB.


----------



## Highchiller (6. Jul 2011)

Ähm. Was meint denn KSKB? 

Also der ganze Code ist recht umfangreich und euch nur Ausschnitte zu zeigen würde wohl mehr Fragen aufwerfen als welche zu beantworten. Daher ist das recht schwer.

Also ich hab "damals" in ein ActionListener ein Alpha Objekt erstellt und halt ausgeführt. Was dazu führte, dass es einmal klappte. Bei abermaligen Klick auf den Button sprang das Alpha nicht mehr an, daher dacht ich würde es nicht klappen.

Ich schreib mal ein Stück Code und teste (mit Alpha), das kann ich euch dann schicken.
Bis später.


----------



## Highchiller (6. Jul 2011)

So wie versprochen jetzt mein Code.

Also erst mal den Codeausschnitt Nummer 1:

```
/**
	 * Update 3D-World.
	 * Create all pieces in universe and clip it to live group
	 */
	@Override
	public void update(Observable observable, Object notification){		
		// enabled buttons
		setEnabledComp(true);
		
		PiecesTG = new TransformGroup( PiecesT3D );
		// sets capabilities to transform and add transform group
		PiecesTG.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );
		PiecesTG.setCapability( TransformGroup.ALLOW_TRANSFORM_READ );
		PiecesTG.setCapability( TransformGroup.ALLOW_CHILDREN_EXTEND );
		PiecesTG.setCapability( TransformGroup.ALLOW_CHILDREN_READ );
		PiecesTG.setCapability( TransformGroup.ALLOW_CHILDREN_WRITE );
		
		// clip all pieces to pieces transform group
		createSceneGraph();
		
		// sets transformation informations
		PiecesTG.setTransform( PiecesT3D );
		
		// remove pieces branch group
		try{ RootBG.removeChild( piecesBG ); } 
		catch (Exception exp){} 
		
		if (notification instanceof IntroductionNotificationInterface)
		{
			int index = ((IntroductionNotificationInterface) notification).getPieceIndex();
			Piece piece = board.getPiece(index);
			PieceGraph3D pieceGraph = getGraph( piece );
			
			PiecesTG.addChild( pieceGraph.introAnimation(5) );
		}
		
		// gimmick for action listeners
		if (observable != null && notification != null){
			// create new pieces branch group
			piecesBG = new BranchGroup();
			piecesBG.setCapability(BranchGroup.ALLOW_DETACH);
			
			// clip pieces to branch group and compile
			piecesBG.addChild( PiecesTG );
			piecesBG.compile();
			
			// clip pieces branch group to live group
			RootBG.addChild( piecesBG );
			
			// sets momently camera position
			setViewingPosition(zoomSlider.getValue());
		}
	}//end update
	
	/**
	 * Create all pieces and clip this pieces to pieces transform group.
	 */
	private void createSceneGraph(){
		try{
			// create list of 3D pieces
			pieceGraphList = createPieces( board.getIntroducedPieces() );
			
			// add all 3D pieces to group
			for (int i = 0; i < pieceGraphList.size(); i++)
				PiecesTG.addChild( pieceGraphList.get(i).transGroup );
		} catch (NoSuchElementException exp){}
	}
```

Mal abgesehen von meinem schlechten Englisch:
update: Diese Methode wird halt immer aufgerufen wenn was passiert. 
Es gibt die Möglichkeit, dass ein Piece gelegt wird oder ein Piece bewegt wird. So viel zur Vorkenntnis.

CreateSceneGraph(): dient nun dazu, meine Objekte einer TransformGroup namens PiecesTG hinzuzufügen.

Wenn jetzt ein Piece gelegt wird, soll das von oben an seine Position fallen. Also such ich mir den Graphen zum Piece und rufe dazu die Methode introAnimation() auf. Die sieht wie folgt aus:

```
public PositionInterpolator introAnimation(){
		Alpha alpha = new Alpha(1, Alpha.DECREASING_ENABLE, 1000, 1000,
				0, 0, 0,
				2000, 1000, 0);
		
		Transform3D translate = new Transform3D();
		translate.setTranslation(new Vector3f(0f,0f,-1f));
		
		PositionInterpolator posInt = new PositionInterpolator(
				alpha, transGroup, translate, piece3D.getSourcePoint().x, 10);
		posInt.setSchedulingBounds(new BoundingSphere(new Point3d(0f,0f,0f), 100000));
		
		return posInt;
	}
```
Hier erstelle ich nun ein Alpha-Object. (die 1s Wartezeit vor dem Start, rührt daher dass er noch mehr berechnet und man ansonsten die Animation gar nicht mitbekommen würde, weil sie längst vorbei wär bevor man überhaupt was sieht)
Dann ein Transform3D-Object von dem ich annahm, dass er die Art der Translation angibt.
Dann der PositionInterpolator mit den angegebenen Werten.
Dieser wird dann zurückgegeben.

Dann wird alles zu PiecesTG hinzugefügt, PiecesTG wiederum zu RootBG und RootBG dann zum Universum. Dann geht das alles.

Jetzt treten folgende Probleme auf.
1) Verständnisfrage... Wenn ich bei Alpha statt den beiden 1000, 1000 nur eines von beiden Wartezeiten auf 1000 setze, wartet Alpha überhaupt nicht, sondern legt sofort los. Warum denn das? oO
2) Die Translation findet überhaupt nicht entlang der Z-Achse statt wie ich wollte, sondern entlang der X-Achse (wieso auch immer, schließlich hab ich extra das Transform3D-Objekt erstellt)
3) Das Problem was ich letzte mal schon erwähnt habe. Sobald ich ein Piece "introduce" funktioniert erst mal alles "einwandfrei" (mal abgesehen von den beiden obigen Punkten) aber sobald ich das nächste Piece introduce, wird die Animation gar nicht gestartet, das Piece liegt zwar an richtiger Position, aber die Animation fehlt.
(das hab ich gemeint, als ich meinte, wenn ich den Button abermals drücke, funktioniert die Animation nicht mehr)

So... Ich hoffe ihr könnte helfen. Langsam wirds etwas eng mit der Zeit ~.~
Danke an alle die Helfen und drüber schauen


----------



## Highchiller (6. Jul 2011)

Ok. 2) konnte ich beheben. Ich hab gefunden dass man lieber einen PositionPathInterpolator verwenden soll, wenn es um die Z-Achse geht.
Das sieht bei mir jetzt so aus:

```
public PositionPathInterpolator introAnimation(){
		Alpha alpha = new Alpha(1, Alpha.DECREASING_ENABLE, 1500, 1500,
				0, 0, 0,
				2000, 1500, 0);
		
		float[] knots = {0f,0.7f,1f};
		
		float[] xyz = {piece3D.getSourcePoint().x, piece3D.getSourcePoint().y, piece3D.getSourcePoint().z};
		
		Point3f[] positions = {
				piece3D.getSourcePoint(),
				new Point3f(xyz[0], xyz[1], xyz[2]+5),
				new Point3f(xyz[0], xyz[1], xyz[2]+10)
				};
		
		PositionPathInterpolator posPathInt = new PositionPathInterpolator(
				alpha, transGroup, trans3D, knots, positions);
		posPathInt.setSchedulingBounds(new BoundingSphere(new Point3d(0f,0f,0f), 100000));
		
		return posPathInt;
	}
```

Warum ich bei Alpha immernoch beide "Warte-Werte" setzen muss, damit er auch wirklich wartet, versteh ich nicht.
Dafür geht "fällt" der Stein (das Piece) jetzt aber entlang der Z-Achse so wie er soll.

Das Problem, dass der nächste Stein der fallen soll, aber nicht fällt, bleibt weiterhin bestehen.
Warum reagiert die Animation nich, wenn er noch mal loslegen soll *grübel*


----------



## Marco13 (6. Jul 2011)

Zufälligerweise steht in diesem Thread http://www.java-forum.org/spiele-mu...587-java3d-probleme-animation.html#post653983 nicht nur, was ein KSKB ist, sondern auch ein bißchen was über Alphas. Auch das hier könnte interessant sein: http://www.java-forum.org/spiele-mu...uche-kleine-interpolatorhilfe.html#post671663 - Man kann dem Alpha mit sowas wie
alpha.setStartTime(System.currentTimeMillis());
eine beliebige Startzeit zuweisen. Wenn's nicht hilft, ... ein KSKB (  ) wäre halt gut, damit man's schnell testen kann.


----------



## Highchiller (6. Jul 2011)

*KREISCH* ES FUNZT :applaus:

Also das einfügen mit den setStartTime()
Damit werden jetzt auch alle Pieces fein säuberlich einsortiert wie sie sollen. Sehr geil :applaus:

Was das KSKB angeht... ähh...
Das dürfte sich extrem schwierig gestalten. Ich hab bestimmt 50 Klassen.
Da lässt sich schwerlich was "kurz" angeben.
Wie wärs wenn ich dir das mal als zip. schicke. Da drin liegt die jar (die kannst einfach via doppelklick starten) und die benötigten bilder für das spiel...
Dann auf den Spatenklicken um ein "offlineSpiel" zu starten und dann bissl rumdrücken. Magst das haben? Dann schick ich dir das mal als PM


----------



## Marco13 (6. Jul 2011)

Lad' es lieber irgendwo hoch wenn es fertig ist, dann können es sich alle ansehen.


----------

