//
// Plot3D.java, Frank Buß 03/1999
//
// Demonstriert die Highfield-Klasse.
//

// imports für GraphicsConfiguration, BorderLayout und Applet
import java.awt.GraphicsConfiguration;
import java.awt.BorderLayout;
import java.applet.Applet;

// imports für verschiendene Knoten-Klassen
import javax.media.j3d.*;
import javax.media.j3d.Light.*;

// imports für verschiedene Vektor-Klassen
import javax.vecmath.*;

// Utility Klassen machen einem das Leben leichter
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.applet.MainFrame;

/**
 * Die Klasse <code>Plot3D</code> zeigt mit Hilfe der Klasse Highfield
 * eine Sinus- und ein Cosinusschwingung mit verschiedenen
 * Wellenl&auml;ngen f&uuml;r x und y Richtung an. Je n&auml;her die
 * Wellen dabei den Kanten des Anzeigebereichs kommen, umso mehr werden
 * sie ged&auml;mpft.
 *
 * @see         Highfield
 * @version     1.1 03/1999
 * @author      Frank Bu&szlig;
 */
public class Plot3D extends Applet {

	/**
	 * Erzeugt ein <code>BranchGroup</code>-Objekt mit einem
	 * angeh&auml;ngtem <code>TransformGroup</code>-Objekt f&uuml;r
	 * die 45 Grad Schr&auml;gstellung der daran angeh&auml;ngten
	 * rotierenden <code>TransformGroup</code> mit einem Highfield.
	 *
	 * @return Die Szene.
	 */
	private BranchGroup createScene() {
		// Demo Daten erzeugen
		int detail = 40;
		float values[][] = new float[detail + 1][detail + 1];
		for (int x = 0; x < detail + 1; x++) {
			float fx = (float) x / (float) detail;
			for (int y = 0; y < detail + 1; y++) {
				float fy = (float) y / (float) detail;
				values[x][y] =
					(float) (Math.sin(Math.PI * fx)
						* Math.sin(Math.PI * fy)
						* Math.sin(Math.PI * fx * 3.1f)
						* Math.cos(Math.PI * fy * 5f));
			}
		}

		// neuen Körper damit erzeugen
		Shape3D plot = new Highfield(values);

		// Äußeres ändern
		Color3f objColor = new Color3f(1f, 1f, 1f);
		Color3f black = new Color3f(0, 0, 0);
		Appearance app = new Appearance();
		app.setMaterial(new Material(objColor, black, objColor, black, 20f));
		plot.setAppearance(app);

		// Gruppe für die Animation anlegen und Körper hinzufügen
		TransformGroup rotatingGroup = new TransformGroup();
		rotatingGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		rotatingGroup.addChild(plot);

		// bounds-Objekt anlegen
		Bounds bounds =
			new BoundingBox(
				new Point3d(-10, -10, -10),
				new Point3d(10, 10, 10));

		// Rotationsanimation erzeugen
		Alpha rotationAlpha = new Alpha(-1, 10000);
		Transform3D rotAxis = new Transform3D();
		rotAxis.rotX(Math.PI / 2f);
		RotationInterpolator rotator =
			new RotationInterpolator(rotationAlpha, rotatingGroup);
		rotator.setAxisOfRotation(rotAxis);
		rotator.setSchedulingBounds(bounds);
		rotatingGroup.addChild(rotator);

		// Transformationsmatrix erzeugen und um 45 Grad drehen
		Transform3D trans = new Transform3D();
		trans.rotX(Math.PI / 180 * -45);

		// Translationsmatrix anlegen und initialisieren
		Transform3D move = new Transform3D();
		move.set(new Vector3f(0, 1, -.8f));

		// Rotations und Translationsmatrizen kombinieren und Gruppe anlegen
		trans.mul(move);
		TransformGroup staticGroup = new TransformGroup(trans);

		// Gruppe mit dem drehenden Diagramm hinzufügen
		staticGroup.addChild(rotatingGroup);

		// Wurzel erzeugen
		BranchGroup root = new BranchGroup();

		// Gruppe hinzufügen
		root.addChild(staticGroup);

		// Hintergrund setzen
		Background bg = new Background(new Color3f(1f, 1f, 1f));
		bg.setApplicationBounds(bounds);
		root.addChild(bg);

		// Licht erzeugen und ebenfalls der Wurzel hinzufügen
		Color3f lightColor = new Color3f(.5f, .5f, 1f);
		Vector3f lightDirection = new Vector3f(-1f, -2f, -5f);
		DirectionalLight light =
			new DirectionalLight(lightColor, lightDirection);
		light.setInfluencingBounds(bounds);
		root.addChild(light);

		// optimieren
		root.compile();

		// Szene zurückgeben
		return root;
	}

	/**
	 * Zeigt eine Szene in einem Applet (oder MainFraim) an.
	 *
	 * @param root Die Szene.
	 */
	private void showScene(BranchGroup root) {
		// ein 3D-Canvas mit Default-Konfiguration erzeugen
		GraphicsConfiguration c = SimpleUniverse.getPreferredConfiguration();
		Canvas3D canvas = new Canvas3D(c);

		// Universum erzeugen und Szene einhängen
		SimpleUniverse universe = new SimpleUniverse(canvas);
		universe.addBranchGraph(root);

		// etwas zurücktreten, damit alles sichtbar wird
		universe.getViewingPlatform().setNominalViewingTransform();

		// 3D-Canvas dem Applet hinzufügen
		setLayout(new BorderLayout());
		add(BorderLayout.CENTER, canvas);
	}

	/**
	 * Erzeugt die Szene und zeigt sie an.
	 *
	 * @see #createScene
	 * @see #showScene
	 */
	public Plot3D() {
		BranchGroup root = createScene();
		showScene(root);
	}

	/**
	 * main-Methode zum Starten als Applikation.
	 *
	 * @param args Kommandozeilenargumente.
	 */
	public static void main(String[] args) {
		new MainFrame(new Plot3D(), 400, 300);
	}

}
