アニメーションに挑戦

Java 3D には、アニメーションを実現するためのクラスとして Interpolator が用意されています。

Interpolater

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    +--javax.media.j3d.Behavior
                          |
                          +--javax.media.j3d.Interpolator

クラス宣言

public abstract class Interpolator
extends Behavior

コンストラクター

public Interpolator()

public Interpolator(Alpha alpha) // この Interpolator で使用する Alpha

InterpolatorBehavior のサブクラスです。

Behavior と同様、initialize(), processStimulus()wakeupOn() を使って起動条件を設定することもできますが、Interpolator ではアニメーションが簡単にできるように機能が拡張されています。

Interpolator でのアニメーションには javax.media.j3d.Alpha クラスを使用します。

註:
javax.media.j3d.Alpha は、RGBA カラーモデルで不透明の度合を表す「アルファ」とは関係ありません。

Alpha は、0.0〜1.0 の範囲の値 (アルファ値) を持っています。アルファ値は、時間の変化に応じて 0.0〜1.0 の範囲で変化して行きます。

Interpolator では、変化するアルファ値に応じて、色、距離、座標、大きさ、回転などを補間して行きます。

0.0 が状態の開始、1.0 が状態の終了 (の値) と言っても良いでしょう。

Interpolator の使用方法は次のようになります。

  1. Alpha を生成する。Alpha に、時間の変化に応じた状態の変化を設定する
  2. Interpolator を生成する。コンストラクターの引数に Alpha を指定する
  3. setSchedulingBounds() メソッドで、スケジューリングが有効になる領域 (Bounds) を設定する。
  4. InterpolatorBranchGroupaddChild() する

Behavior のような起動条件の設定は不要です。Alpha を設定し、setSchedulingBounds() でスケジューリングが有効になる領域を設定し、BranchGroupaddChild() するだけで Interpolator の動作が開始します。

Alphah2

javax.media.j3d.Alpha は、時間の変化に応じて 0.0〜1.0 の範囲の値 (アルファ値) を変化させます。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.Alpha

クラス宣言

public class Alpha
extends NodeComponent

コンストラクター (一部)

public Alpha()

public Alpha(int loopCount,                    // 繰り返しの回数
             int mode,                         // 変化モード(増加、減少、または両方)
             long triggerTime,                 // 最初の起動開始までのミリ秒
             long phaseDelayDuration,          // 最初の起動開始後に本当に変化を開始するまでのミリ秒
             long increasingAlphaDuration,     // 増加に要するミリ秒
             long increasingAlphaRampDuration, // 増加が加速するミリ秒
             long alphaAtOneDuration,          // 1.0 を保持するミリ秒
             long decreasingAlphaDuration,     // 減少に要するミリ秒
             long decreasingAlphaRampDuration, // 減少が加速するミリ秒
             long alphaAtZeroDuration)         // 0.0 を保持するミリ秒

定数フィールド

public static final int INCREASING_ENABLE // 増加を有効にする
public static final int DECREASING_ENABLE // 減少を有効にする

メソッド (一部)

public void setStartTime(long startTime) // 開始基準時刻

loopCount に繰り返し回数を指定します。-1 を指定すると無限に繰り返します。デフォルト値は -1 です。
loopCount を変更しても、Alpha の開始時刻を変更しないと繰り返し回数の変更が反映されません。開始時刻は、すべての実行の基準になります。開始時刻は setStartTime() で変更します。たとえば、即座に変更を反映したいときは現在時刻を開始時刻にします。

  alpha.setStartTime(System.currentTimeMillis()); // 現在時刻から開始
  alhpa.setLoopCount(1);                          // 1回だけ

mode には、定数 INCREASING_ENABLE, DECREASING_ENABLE を指定します。それぞれ増加、減少を有効にします。デフォルト値は INCLEASING_ENABLE です。

INCLEASING_ENABLE.gif DECLEASING_ENABLE.gif
INCLEASING_ENABLE DECLEASING_ENABLE

両方を OR した値を指定すると増加も減少も有効になります。

INCLEASING_DECLEASING_ENABLE.gif

triggerTime は、Interpolator が最初に起動されるまでの時間 (トリガー時間) をミリ秒で指定します。デフォルト値は 0 です。

triggerTime.gif

phaseDelayDuration は、最初に起動されてから、本当に変化を開始するまでの遅延時間をミリ秒で指定します。デフォルト値は 0 です。

phaseDelayDuration.gif

increasingAlphaDuration は、0.0 から 1.0 までの増加に要する時間をミリ秒で指定します。

increasingAlphaDuration.gif

increasingAlphaRampDuration は、増加が加速される時間をミリ秒で指定します。この時間が increasingAlphaDuration1/2 よりも小さいとき、最初は加速し、次に一定時間同じ増加率を保ち、最後に減速します。

ramp_less_half.gif

この時間が increasingAlphaDuration1/2 以上のときは 1/2 とみなされます。1/2 以上のときは増加率が一定になる時間は無く、はじめ加速し、次に減速して行きます。

ramp.gif

この時間が 0 のときは常に同じ増加率で増加します。デフォルト値は 0 です。

ramp_zero.gif

alphaAtOneDuration は、増加終了後 1.0 の状態を保持する時間をミリ秒で指定します。デフォルト値は 0 です。

alphaAtOneDuration.gif

decreasingAlphaDuration は、1.0 から 0.0 への減少に要する時間をミリ秒で指定します。デフォルト値は 0 です。

decreasingAlphaDuration.gif

decreasingAlphaRampDuration は、減少が加速される時間をミリ秒で指定します。この時間が decreasingAlphaDuration1/2 よりも小さいとき、最初は加速し、次に一定時間同じ減少率を保ち、最後に減速します。

decreasing_ramp_less_half.gif

この時間が decreasingAlphaDuration1/2 以上のときは 1/2 とみなされます。1/2 以上のときは増加率が一定になる時間は無く、はじめ減少が加速し、次に減少が減速して行きます。

decreasing_ramp_.gif

この時間が 0 のときは常に同じ減少率で減少します。デフォルト値は 0 です。

decreasing_ramp_zero.gif

alphaAtZeroDuration は、減少が終了してから 0.0 の状態を保持する時間をミリ秒で指定します。デフォルト値は 0 です。

alphaAtZeroDuration.gif

すべての値の関係は次の図のようになります。

alpha.gif

■■AlphaPanel

Aplpha の状態を変更するための GUI クラス AlphaPanel を書きました。

AlphaPanel.gif

AlphaPanel の使用方法は次のようになります。

  1. AlphaPanel を生成する
  2. AlphaPanel#getAlpha()Alpha を取得する
  3. Interpolator を生成する。コンストラクター引数に AlphaPanel#getAlpha() で取得した Alpha を指定する

AlphaPanel のソースコードはCD-ROMを参照してください。GUI 構築が大半なので詳しい説明は省略します。

AlphaPatel.java

javax.media.j3d.ColorInterpolator

javax.media.j3d.ColorInterpolatorjavax.media.j3d.MaterialdiffuseColor を変化させます。


クラス宣言

public class ColorInterpolator
extends Interpolator

コンストラクター

public ColorInterpolator(Alpha alpha,        // 変化の元になる Alpha
                         Material target)    // 変更対象の Material

public ColorInterpolator(Alpha alpha,        // 変化の元になる Alpha
                         Material target,    // 変更対象の Material
                         Color3f startColor, // 初期の DiffuseColor
                         Color3f endColor)   // 終了時の DiffuseColor

ColorInterpolator のコンストラクターでは、変更対象となる Material を指定します。変更の対象となる色は diffuseColor のみです。変化開始時の diffuseColor と、変化終了時の diffuseColor を設定し、Alpha0.0〜1.0 の範囲で変化すると diffuseColor が変化開始色〜変化終了色 の間で変化します。

diffuseColor 以外の色を変化させたい場合には自分で独自の Interpolator を書くことになると思います。

このサンプルの実行結果は次のようになります。

ColorInterpolatorTes.gif

Alpha や色を変化させてみてください。値の変更は即座に反映されます。loopCount を変更した場合は [Stop] ボタンを押して ColorInterpolator を停止 (setEnabled(false)) させ、再度 [Start] ボタンを押してください。

ハート型のオブジェクトは別クラスとして書きました。ソースコードは次の通りです。頂点座標の配列と頂点 index の配列を用意し、com.sun.j3d.utils.geometry.GeometryInfoIndexedTriangleArray を構築しています。

Heart.java
   1 // Java 3D Test Program
   2 // Heart.java
   3 //   Copyright (c) 1999 ENDO Yasuyuki
   4 //                      mailto:yasuyuki@javaopen.org
   5 //                      http://www.javaopen.org/j3dbook/index.html
   6 
   7 
   8 import javax.media.j3d.*;
   9 import javax.vecmath.*;
  10 import com.sun.j3d.utils.geometry.*;
  11 
  12 public class Heart {
  13   protected GeometryInfo ginfo = null;
  14   public GeometryArray getGeometry() { return ginfo.getGeometryArray(); }
  15   public Vector3f[] getNormals() { return ginfo.getNormals(); }
  16   
  17   public Heart() {
  18     float[] vertices = {
  19         -0.35553592f, 0.23813197f, 0.2910625f, 
  20         -0.07661505f, 0.31729084f, 0.12777443f, 
               :
               :
 350         0.028356554f, 0.24565041f, -0.18884426f, 
 351         0.11708972f, 0.18016575f, -0.259885f, 
 352     };
 353     
 354     int[] indices = {
 355         3, 9, 1, 
 356         9, 8, 1, 
               :
               :
1015         331, 325, 137, 
1016         137, 325, 136, 
1017     };
1018     ginfo = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
1019     ginfo.setCoordinates(vertices);
1020     ginfo.setCoordinateIndices(indices);
1021     NormalGenerator ngen = new NormalGenerator();
1022     ngen.generateNormals(ginfo);
1023   }
1024 }

ColorInterpolatorTest のソースコードは次のようになります。GUI 構築のため AlphaPanel, TuplePanel を使用しました。

ColorInrepolatorTest.java
  1  // Java 3D Test Applet
  2  // ColorInterpolatorTest.java
  3  //   Copyright (c) 1999 ENDO Yasuyuki
  4  //                      mailto:yasuyuki@javaopen.org
  5  //                      http://www.javaopen.org/j3dbook/index.html
  6  
  7  import java.applet.*;
  8  import java.awt.*;
  9  import java.awt.event.*;
 10  import javax.media.j3d.*;
 11  import javax.vecmath.*;
 12  import com.sun.j3d.utils.applet.MainFrame;
 13  import com.sun.j3d.utils.universe.SimpleUniverse;
 14  import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
 15  
 16  public class ColorInterpolatorTest extends Applet {
 17    private AlphaPanel apanel = null;
 18  
 19    private Appearance app = null;
 20    private Color3f scolor = new Color3f(1.0f, 0.0f, 0.0f);
 21    private Color3f ecolor = new Color3f(0.0f, 0.0f, 1.0f);
 22    private ColorInterpolator cinterp = null;.................(1)
 23  
 24    public ColorInterpolatorTest() {
 25      this.setLayout(new BorderLayout());
 26      
 27      apanel = new AlphaPanel();
 28      this.add(apanel, BorderLayout.NORTH);
 29  
 30      Panel cpanel = new Panel();
 31      cpanel.setLayout( new GridLayout(2, 1) );
 32      this.add(cpanel, BorderLayout.SOUTH);
 33      
 34      Panel[] cpanels = new Panel[2];
 35      for (int i=0; i<2; i++) {
 36        cpanels[i] = new Panel();
 37        cpanel.add(cpanels[i]);
 38      }
 39      
 40      final Button sbutton = new Button("Stop");             ┐
 41      sbutton.addActionListener( new ActionListener() {      │
 42        public void actionPerformed(ActionEvent e) {         │
 43          String blabel = sbutton.getLabel();                │
 44          if (blabel.equals("Start")) {                      │
 45            cinterp.setEnable(true);.....................(3) │
 46            apanel.reset();..............................(4) │
 47                                                             │
 48            sbutton.setLabel("Stop");                        ├(2)
 49          } else if (blabel.equals("Stop")) {                │
 50            cinterp.setEnable(false);                        │
 51                                                             │  
 52            sbutton.setLabel("Start");                       │
 53          }                                                  │
 54        }                                                    │
 55      });                                                    │
 56      cpanels[0].add(sbutton);                               ┘
 57  
 58      cpanels[0].add( new Label("Start -") );               ┐
 59      TuplePanel stp = new TuplePanel(scolor);              │
 60      stp.addTupleEventListener( new TupleEventListener() { │
 61        public void tupleStateChanged(TupleEvent e) {       │
 62          Color3f color = e.getColor3f();                   ├(5)
 63          cinterp.setStartColor(color);.................(6) │
 64        }                                                   │
 65      });                                                   │
 66      cpanels[0].add(stp);                                  ┘
 67  
 68      cpanels[1].add( new Label("End -") );                 ┐
 69      TuplePanel etp = new TuplePanel(ecolor);              │
 70      etp.addTupleEventListener( new TupleEventListener() { │
 71        public void tupleStateChanged(TupleEvent e) {       ├(7)
 72          Color3f color = e.getColor3f();                   │
 73          cinterp.setEndColor(color);                       │
 74        }                                                   │ 
 75      });                                                   ┘
 76      cpanels[1].add(etp);
 77      
 78      GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
 79      Canvas3D canvas = new Canvas3D(config);
 80      this.add(canvas, BorderLayout.CENTER);
 81      
 82      SimpleUniverse universe = new SimpleUniverse(canvas);
 83      universe.getViewingPlatform().setNominalViewingTransform();
 84  
 85      BranchGroup scene = createSceneGraph();
 86      universe.addBranchGraph(scene);
 87    }
 88    
 89    private BranchGroup createSceneGraph() {
 90      BranchGroup root = new BranchGroup();
 91      root.setCapability(BranchGroup.ALLOW_DETACH);
 92      
 93      DirectionalLight light =
 94        new DirectionalLight( new Color3f(1.0f, 1.0f, 1.0f),
 95                              new Vector3f(-0.57f, -0.57f, -0.57f) );
 96      BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
 97      light.setInfluencingBounds(bounds);
 98      root.addChild(light);
 99      
100      AmbientLight alight = new AmbientLight();
101      alight.setInfluencingBounds(bounds);
102      root.addChild(alight);
103  
104      TransformGroup trans = new TransformGroup();
105      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
106      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
107      trans.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
108      root.addChild(trans);
109      
110      MouseRotate rotator = new MouseRotate();
111      rotator.setSchedulingBounds(bounds);
112      rotator.setTransformGroup(trans);
113      root.addChild(rotator);
114      
115      Heart heart = new Heart();
116      app = createAppearance();
117      Shape3D shape = new Shape3D( heart.getGeometry(), app );
118      trans.addChild(shape);
119      
120      cinterp = createInterpolator( apanel.getAlpha(), app.getMaterial() );...(8)
121      root.addChild(cinterp);
122  
123      return root;
124    }
125  
126    private ColorInterpolator createInterpolator(Alpha alpha, Material mat) {       ┐
127      ColorInterpolator cinterp =                                                   │
128        new ColorInterpolator(alpha, mat, scolor, ecolor);.....................(10) ├(9)
129      cinterp.setSchedulingBounds(new BoundingSphere(new Point3d(), 100.0));...(11) │
130      return cinterp;                                                               │
131    }                                                                               ┘

(1)で ColorInterpolator のインスタンスを保持するための変数を確保しています。

(2)で [Stop]/[Start] ボタンを構築しています。最初は "Stop" が表示され、ボタンがクリックされると "Start" に変化します。ボタンクリックの処理では、Button#getLabel() で得たラベル文字列を判断して、"Start" なら開始処理、"Stop" なら停止処理を行っています。

(3)の開始処理ではsetEnable(true)ColorInterpolator を有効 (動作できる状態) にしています。

(4)でAlphaPanel#reset() を使って、Alpha の開始時刻を現在時刻に再設定しています。この処理をしないと loopCount の値を変更してもうまく反映されません。ここでは AlphaPanel#reset() を使用しましたが、Alpha を直接使う場合は Alpha#setStartTime(System.currentTimeMillis()) を実行してください。

(5)でTuplePanel を使って startColor を変更するための 3つの TextField を構築しています。(6)では、TuplePanel への入力によって変更された色を ColorInterpolator#setStartColor() メソッドで設定しています。

(7)では、(5)の startColor のための処理と同様に endoColor のための GUIを TuplePanel で構築しています。

(8)で ColorInterpolator を生成しています。引数は、AlphaPanel#getAlpha() で取得した Alpha と、変更対象の Material です。実際の処理は (9)の createColorInterpolator() メソッドで行っています。

(10)で ColorInterpolator を生成しています。(11)でスケジューリングが有効になる範囲を設定しています。

javax.media.j3d.TransparencyInterpolator

javax.media.j3d.TransparencyInterpolatorjavax.media.j3d.TransparencyAttributes の透明度 (transparency) を変化させます。


クラス宣言

public class TransparencyInterpolator
extends Interpolator

コンストラクター

public TransparencyInterpolator(Alpha alpha,                   // 変化の元になる Alpha
                                TransparencyAttributes target) // 変更対象の TransparencyAttributes

public TransparencyInterpolator(Alpha alpha,                   // 変化の元になる Alpha
                                TransparencyAttributes target, // 変更対象の TransparencyAttributes
                                float minimumTransparency,     // 初期の透明度
                                float maximumTransparency)     // 終了時の透明度

TransparencyInterpolator のコンストラクターでは、変更対象の TransparencyAttributes、変化開始時の透明度 ( minimumTransparency)、変化終了時の透明度 (maximumTransparency)を指定します。minimumTransparency のデフォルト値は 0.0fmaximumTransparencyのデフォルト値は 1.0f です。

サンプルの実行結果を見てください。

TransparencyInterpolatorTest.gif

サンプルでは、Background に空のグラフィックを適用しています。

TextField に透明度を入力して、変化開始時と終了時の透明度を変更できます。

このサンプルのソースコードはCD-ROMを参照してください。

TransparencyInterpolatorTest.java

javax.media.j3d.ScaleInterpolator

javax.media.j3d.ScaleInterpolatorTransformGroupscale を変化させます。


クラス宣言

public class ScaleInterpolator
extends Interpolator

コンストラクター

public ScaleInterpolator(Alpha alpha,           // 変化の元になる Alpha
                         TransformGroup target) // 変更対象の TransformGroup

public ScaleInterpolator(Alpha alpha,             // 変化の元になる Alpha
                         TransformGroup target,   // 変更対象の TransformGroup
                         Transform3D axisOfScale, // 拡大縮小の対象軸
                         float minimumScale,      // 初期の拡大縮小率
                         float maximumScale)      // 終了時の拡大縮小率

ScaleInterpolator のコンストラクターでは、変更対象の TransformGroup、変化開始時の拡大縮小率、変化終了時の拡大縮小率を指定します。

変化開始時の拡大縮小率のデフォルト値は 0.1、変化終了時の拡大縮小率のデフォルト値は 1.0 です。

サンプルの実行結果は次のようになります。

ScaleInterpolatorTest.gif

このサンプルのソースはCD-ROMを参照してください。

ScaleInterporatorTest.java

javax.media.j3d.PositionInterpolator

javax.media.j3d.PositionInterpolator は、TransformGroup の X軸方向への移動距離を変化させます。


クラス宣言

public class PositionInterpolator
extends Interpolator

コンストラクター

public PositionInterpolator(Alpha alpha,           // 変化の元になる Alpha
                            TransformGroup target) // 変更対象の TransformGroup

public PositionInterpolator(Alpha alpha,                   // 変化の元になる Alpha
                            TransformGroup target,         // 変更対象の TransformGroup
                            Transform3D axisOfTranslation, // 移動方向軸
                            float startPosition,           // 開始位置(開始距離)
                            float endPosition)             // 終了位置(終了距離)

PositionInterpolator は、TransformGroup の X軸方向への移動距離を変化させます。コンストラクター引数には変更対象の TransformGroup、移動方向の軸となる Transfom3D、開始距離、終了距離を指定します。開始距離のデフォルト値は 0.0、終了距離のデフォルト値は 1.0 です。

axisOfTranslation に指定した Transform3D に回転を与て、任意の方向に移動させることができます。

サンプルプログラムの実行結果を見てください。

PositionInterpolatorTest.gif

中央にある X, Y, Z軸は、回転対象の TransformGroupaddChild() されています。X, Y, Z軸を、マウスの左ボタンによるドラッグで回転させることができます。軸の中央の ColorCubePositionInterpolator によって移動します。グリッド線と、奥にあるハート型は移動しません。

このサンプルのソースコードはCD-ROMを参照してください。

PositionInterpolatorTest.java

javax.media.j3d.RotationInterpolator

javax.media.j3d.RotationInterpolator は、変更対象の TransformGroup を Y軸を中心に回転させます。


クラス宣言

public class RotationInterpolator
extends Interpolator

コンストラクター

public RotationInterpolator(Alpha alpha,           // 変化の元になる Alpha     
                            TransformGroup target) // 変更対象の TransformGroup

public RotationInterpolator(Alpha alpha,                // 変化の元になる Alpha
                            TransformGroup target,      // 変更対象の TransformGroup
                            Transform3D axisOfRotation, // 回転軸
                            float minimumAngle,         // 初期の回転角 (ラジアン)
                            float maximumAngle)         // 終了時の回転角 (ラジアン)

RotanionInterpolatorは、変更対象の TransformGroup を、Y軸を中心に回転させます。コンストラクター引数には変更対象の TransformGroup、回転の軸となる Transform3D、回転開始時の回転角、回転終了時の回転角を指定します。回転開始時の回転角のデフォルト値は 0.0 ラジアン、回転終了時の回転角のデフォルト値は ラジアン (360°) です。

サンプルプログラムの実行結果を見てください。

RotationInterpolatorTest.gif

青い太線が回転しています。初期値では 1000ミリ秒 (1秒) で 1回転します。マウスの左ボタンドラッグで全体を回転させることができます。

このサンプルのソースコードは次の通りです。

RotataionInterpolatorTest.java (一部)
 81    private BranchGroup createSceneGraph() {
          :
          :
150      Transform3D rt3d = new Transform3D();
151      rt3d.rotX( -(Math.PI / 2.0) );.................................(1)
152      rinterp =
153        new RotationInterpolator( apanel.getAlpha(), rtrans, rt3d,
154                                  0.0f, (float)Math.PI * 2.0f );.....(2)
155      rinterp.setSchedulingBounds(bounds);...........................(3)
156
root.addChild(rinterp);.................................................(4)
157      
158      return root;
159    }

(1)で回転軸となる Transform3D に対して X軸を中心とした 1/2π (90°) の回転を与えています。

(2)で RotationInterpolator を生成しています。引数には、(1)で回転させた Transform3D を指定して、Y軸を中心とした回転を 90°傾けています。青い太線は変更対象の TransformGroup の Z軸を中心に回転することになります。(3)でスケジューリングが有効になる領域を生成し、(4)で BranchGroupaddChild() しています。

javax.media.j3d.SwitchValueInterpolator

javax.media.j3d.SwitchValueInterpolatorSwitch が有効にする子 Node を変化させます。


クラス宣言

public class SwitchValueInterpolator
extends Interpolator

コンストラクター

public SwitchValueInterpolator(Alpha alpha,   // 変化の元になる Alpha     
                               Switch target) // 変更対象の Switch

public SwitchValueInterpolator(Alpha alpha,         // 変化の元になる Alpha     
                               Switch target,       // 変更対象の Switch
                               int firstChildIndex, // 最初に表示する子ノードの index
                               int lastChildIndex)  // 最終的に表示する子ノードの index

SwitchValueInterpolator は、変更対象の Switch に対して、最初に表示させる Node の index と、最終的に表示させる Node の index を指定して、その間を順番に表示させます。表示される Node は常にどれか一つになります。

サンプルの実行結果を見てください。

SwitchValueInterpolatorTest.gif

5つの BoxSwitchaddChild() されています。左から順番に一つづつ表示されます。それぞれの Box をマウスの左ボタンのドラッグで回転させることができますが、[Stop] ボタンで停止させてからでないと回転させるのは難しいかもしれません。

このサンプルのソースはCD-ROMを参照してください。

SwitchValueInterpolatorTest.java

javax.media.j3d.PathInterpolator

Alpha 値は時間の経過とともに 0.0〜1.0 の範囲で変化します。javax.media.j3d.PathInterpolator は、0.0〜1.0 の範囲のどこかに変化点を設定し、その時点での状態をあらかじめ設定しておきます。たとえば、0.0 のときの位置座標と、1.0 のときの位置座標を設定しておけば、0.0 のときの位置から 1.0 のときの位置まで物体を移動させることができます。

0.0〜1.0 の範囲で変化点を複数用意すれば、そのそれぞれ間で状態を変化させることができます。たとえば、0.00.51.0 のときの位置座標を設定しておけば、はじめ 0.0 のときの位置から 0.5 のときの位置まで移動し、つぎに 0.5 のときの位置から 1.0 のときの位置まで移動します。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    +--javax.media.j3d.Behavior
                          |
                          +--javax.media.j3d.Interpolator
                                |
                                +--javax.media.j3d.PathInterpolator
                                      |
                                      +--javax.media.j3d.PositionPathInterpolator
                                      |
                                      +--javax.media.j3d.RotationPathInterpolator
                                      |
                                      +--javax.media.j3d.RotPosPathInterpolator
                                      |
                                      +--javax.media.j3d.RotPosScalePathInterpolator

クラス宣言

public abstract class PathInterpolator
extends Interpolator

コンストラクター

public PathInterpolator(Alpha alpha,   // 変化の元になる Alpha
                        float[] knots) // 変化点ごとの変化量 (Alpha値)

knots は変化点の Alpha値の配列です。最初の要素は 0.0f、最後の要素は 1.0f である必要があります。最も変化点が少ない場合、配列は 0.0f1.0f の 2つの要素数になります。配列の各要素は 0.0〜1.0 の範囲の値を持ち、一つ前の要素よりも大きい値である必要があります。

PathInterpolator は抽象クラス (abstract class) です。実際にはそのサブクラスである PositionInterpolator, RotationPathInterpolator, RotPosPathInterpolator, RotPosScalePathInterpolator などを使用します。

javax.media.j3d.PositionPathInterpolator

javax.media.j3d.PositionPathInterpolator は、変化点ごとに位置座標を設定し、その間を移動させる Interpolator です。


クラス宣言

public class PositionPathInterpolator
extends PathInterpolator

コンストラクター

public PositionPathInterpolator(Alpha alpha,                   // 変化の元になる Alpha     
                                TransformGroup target,         // 変更対象の TransformGroup
                                Transform3D axisOfTranslation, // 移動時の軸となる座標系
                                float[] knots,                 // 変化点ごとの変化量 (Alpha値)
                                Point3f[] positions)           // 変化点ごとの移動座標

コンストラクターの引数には、移動対象の TransformGroup である target を指定します。axisOfTranslation に指定した Transform3D に回転を与えれば、移動させる際の座標系を回転させることができます。

knots に変化点の Alpha値の配列を指定し、positions に変化点の位置座標を指定します。

サンプルの実行結果を見てください。

PositionPathTest.gif

変化点ごとの位置座標に色の付いた点を描画しています。ColorCube が設定した座標の間を移動して行きます。TextField に X, Y, Z座標を指定すると位置座標が変更できます。変化点ごとの Alpha値を変更すると、変化のタイミングが変化点ごとに変更できます。

マウスの左ボタンのドラッグで全体を回転させることができます。

このサンプルのソースコードは次の通りです。

PositionPathTest.java (一部)
212    private BranchGroup createSceneGraph() {
           :
           :
254      pverts = new Point3f[4];               ┐
255      pverts[0] = (Point3f)tp0.getTuple3f(); │
256      pverts[1] = (Point3f)tp1.getTuple3f(); ├(1)
257      pverts[2] = (Point3f)tp2.getTuple3f(); │
258      pverts[3] = (Point3f)tp3.getTuple3f(); ┘
259      pshape = createPoints(pverts);
260      mtrans.addChild(pshape);
261      
262      TransformGroup rtrans = createCube(0.05, new Vector3d(0.0, 0.0, 0.0));
263      mtrans.addChild(rtrans);
264  
265      Transform3D rt3d = new Transform3D();
266  
267      pinterp =
268        new PositionPathInterpolator( apanel.getAlpha(), rtrans, rt3d,
269                                      knots, pverts );..................(2)
270      pinterp.setSchedulingBounds(bounds);..............................(3)
271      pinterp.setEnable(false);.........................................(4)
272      root.addChild(pinterp);...........................................(5)
273      
274      return root;
275    }
276
277    private Shape3D createPoints(Point3f[] pverts) {
278      TransformGroup trans = new TransformGroup();
279      float[] colors = { 1.0f, 0.0f, 0.0f,   // red
280                         1.0f, 0.5f, 0.0f,   // orange
281                         1.0f, 1.0f, 0.0f,   // yellow
282                         0.5f, 1.0f, 0.0f }; // green
283      PointArray pgeom =
284        new PointArray( pverts.length,
285                        GeometryArray.COORDINATES | GeometryArray.COLOR_3);
286      pgeom.setCoordinates(0, pverts);
287      pgeom.setColors(0, colors);
288      Appearance papp = new Appearance();
289      papp.setPointAttributes( new PointAttributes(6.0f, false) );
290      return new Shape3D(pgeom, papp);
291    }
292
293    private TransformGroup createCube(double size, Vector3d position) {
294      Transform3D t3d = new Transform3D();
295      t3d.set(position);
296      TransformGroup trans = new TransformGroup(t3d);
297      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
298      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
299      trans.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
300      trans.addChild( new ColorCube(size) );
301      return trans;
302    }

(1)で変化点ごとの位置座標の配列 pverts_ を初期化しています。TuplePanelgetTuple3f() メソッドを使って Tuple3f を取り出し、Point3f にキャストして配列要素にそれぞれ代入しています。

TuplePanel ですが、PositionPathTest クラスの最初で宣言しています。

 25    private TuplePanel tp0 = new TuplePanel( new Point3f( 0.0f, -0.6f, 0.1f) );
 26    private TuplePanel tp1 = new TuplePanel( new Point3f(-0.4f, -0.2f, 0.1f) );
 27    private TuplePanel tp2 = new TuplePanel( new Point3f( 0.4f,  0.2f, 0.1f) );
 28    private TuplePanel tp3 = new TuplePanel( new Point3f( 0.0f,  0.6f, 0.1f) );

各点の X, Y, Z座標を入力するための TuplePanel を 4つ宣言し、それぞれに座標の初期値を与えています。

(2)で PositionPathInterpolator を生成しています。

(3)でスケジューリングが有効になる領域を設定し、(4)で setEnable(false) で変化を停止させています。[Start] ボタンを押すと変化を開始するようにしました。(5)で BranchGroupaddChild() しています。

javax.media.j3d.RotationPathInterpolator

javax.media.j3d.RotationPathInterpolator は、変化点ごとに回転値を設定し、その間で回転させる Interpolator です。


クラス宣言public class RotationPathInterpolator
extends PathInterpolator

コンストラクター

public RotationPathInterpolator(Alpha alpha,                // 変化の元になる Alpha     
                                TransformGroup target,      // 変更対象の TransformGroup
                                Transform3D axisOfRotation, // 回転時の軸となる座標系
                                float[] knots,              // 変化点ごとの変化量 (Alpha値)
                                Quat4f[] quats)             // 変化点ごとの回転値 (四元数) 
      

コンストラクターの引数には変更対象の TransformGroup、軸となる座標系の Transform3D を与えます。Transform3D に回転を与えておけば、回転の基準となる座標系をあらかじめ傾けておくことができます。

変化点ごとの回転値は四元数 Quat4f で与えます。AxisAngle4f なをど使用したい場合には、まず AxisAngle4f を生成し、Quat4fset(AxisAngle4f) メソッドで Quat4f に設定してください。

このサンプルの実行結果を見てください。

RotationPathTest.gif

変化点は 3点設定しました。手前にある小さい ColorCube それぞれをマウスの左ボタンのドラッグで回転させることができます。小さい ColorCube の回転を Quat4f に取得して、変化点ごとの回転値として設定しています。

[Start] ボタンを押すと中央の大きい ColorCube が回転します。手前の小さい ColorCube をマウスで回転させると、変化点ごとの回転値が即座に変更されます。

グリッドの四隅の立方体をマウスの左ボタンでドラッグすると、全体を回転させることができます。

このサンプルのソースはCD-ROMを参照してください。

RotationPathTest.java

javax.media.j3d.RotPosPathInterpolator

javax.media.j3d.RotPosPathInterpolator は、変化点ごとに位置座標と回転値を設定して、位置と回転を変化させることができる Interpolator です。


クラス宣言

public class RotPosPathInterpolator
extends PathInterpolator

コンストラクター

public RotPosPathInterpolator(Alpha alpha,              // 変化の元になる Alpha
                              TransformGroup target,    // 変更対象の TransformGroup   
                              Transform3D axisOfRotPos, // 移動、回転の軸になる座標系      
                              float[] knots,            // 変化点ごとの変化量 (Alpha値)
                              Quat4f[] quats,           // 変化点ごとの回転値 (四元数) 
                              Point3f[] positions)      // 変化点ごとの移動座標        

コンストラクターの引数では、変更対象の TransformGroup と、移動、回転の軸になる座標系の Transform3D を指定します。Transform3D に回転を与えると、移動、回転の軸になる座標系を傾けることができます。

knots には変化点ごとの Alpha値の配列を設定します。quats に変化点ごとの回転値を、positions に変化点ごとの位置座標を指定します。knots, quats, positions の要素数は同じである必要があります。

サンプルプログラムの実行結果を見てください。

RotPosPathTest.gif

下にある k0〜k3TextField に Alhpa値を入力すると、変化点ごとの Alpha値を変更することができます。

変化点ごとの位置座標に小さい ColorCube が置いてあり、マウスの左ボタンドラッグで回転させることができます。下にある TextField に X, Y, Z座標を入力すると変化点ごとの位置座標を変えることができます。小さい ColorCube の位置も移動します。

グリッドの四隅の立方体をマウスの左ボタンでドラッグすると、全体を回転させることができます。

このサンプルのソースはCD-ROMを参照してください。

RotPosPathTest.java

javax.media.j3d.RotPosScalePathInterpolator

javax.media.j3d.RotPosScalePathInterpolator は、変化点ごとに回転値、位置座標、拡大縮小倍率を設定し、その間を変化させる Interpolator です。


クラス宣言

public class RotPosScalePathInterpolator
extends PathInterpolator

コンストラクター

public RotPosScalePathInterpolator(Alpha alpha,                   // 変化の元になる Alpha
                                   TransformGroup target,         // 変更対象の TransformGroup   
                                   Transform3D axisOfRotPosScale, // 回転、移動、拡大縮小の軸になる座標系
                                   float[] knots,                 // 変化点ごとの変化量 (Alpha値)
                                   Quat4f[] quats,                // 変化点ごとの回転値 (四元数) 
                                   Point3f[] positions,           // 変化点ごとの移動座標        
                                   float[] scales)                // 変化点ごとの拡大縮小率

コンストラクターでは、変更対象となる TransformGroup と回転、移動、拡大縮小の軸になる Transform3D、変化点ごとの変化量になる Alpha値、変化点ごとの回転値、変化点ごとの位置座標、そして変化点ごとの拡大縮小率を指定します。拡大縮小率は 1.0f が等倍で、1.0f より小さければ縮小、1.0f より大きければ拡大です。

サンプルの実行結果を見てください。

RotPosScalePathTest.gif

Alpha値を k0〜k3TextField に入力すると変化点ごとの Alpha値を変更することができます。

変化点ごとの位置座標に小さい ColorCube が置いてあります。マウスの左ボタンのドラッグで ColorCube を回転させることができます。ColorCube の回転は、変化点ごとの回転値として即座に反映されます。

X, Y, Z座標を TextField に入力して、変化点ごとの位置座標を変更することができます。位置座標を変更すると、小さい ColorCube も移動します。

s0〜s3TextField に拡大縮小率を入力して、変化点ごとの拡大縮小率を変更することができます。拡大縮小率を変更すると、小さい ColorCube も拡大縮小されます。

このサンプルのソースはCD-ROMを参照してください。

RotPosScalePathTest.java

com.sun.j3d.utils.behaviors.interpolators.TCBSplinePatheInterpolator

com.sun.j3d.utils.behaviors.interpolators.TCBSplinePatheInterpolator は、変化点ごとに TCBKeyFrame オブジェクトを設定してその間をスプライン補間して変化させる Interpolator です。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    +--javax.media.j3d.Behavior
                          |
                          +--javax.media.j3d.Interpolator
                                |
                                +--com.sun.j3d.utils.behaviors.interpolators.TCBSplinePathInterpolator

クラス宣言

public abstract class TCBSplinePathInterpolator
extends Interpolator

コンストラクター

public TCBSplinePathInterpolator(Alpha alpha,        // 変化の元になる Alpha
                                 TCBKeyFrame[] keys) // 変化点ごとの TCBKeyFrame

コンストラクターでは TCBKeyFrame の配列を指定します。

クラス継承

java.lang.Object
  |
  +--com.sun.j3d.utils.behaviors.interpolators.TCBKeyFrame

クラス宣言

public class TCBKeyFrame
extends java.lang.Object

コンストラクター

public TCBKeyFrame(TCBKeyFrame kf) // 初期値となる TCBKeyFrame

public TCBKeyFrame(float k,     // 変化点ごとの変化量 (Alpha値)
                   int l,       // 一次補間かスプライン補間かのフラグ
                   Point3f pos, // 変化点ごとの移動座標
                   Quat4f q,    // 変化点ごとの回転値 (四元数)
                   Point3f s,   // 変化点ごとの拡大縮小率(x, y, z各軸)
                   float t,     // 張力 (tention)
                   float c,     // 連続性 (continuity)
                   float b)     // 傾斜 (bias)

コンストラクター引数は、変化点ごとの変化量 k、位置座標 pos、回転値 q、拡大縮小率 s などを指定します。拡大縮小率は X, Y, Z軸各軸ごとに指定できます。

フラグ l0 を指定するとスプライン補間が使用されます。1 を指定すると、スプライン補間ではなく一次補間が使用されます。デフォルト値は 0 です。

TCBスプラインを使ったスプライン補間では、t, c, b のそれぞれの値を -1.0〜+1.0 の範囲で変更することによって、変化点を通過する際の曲線の曲率や接線方向を様々に変化させることが可能になります。

t (tention:張力) は曲線のゆるやかさを変化させます。張力 (tention) が負の値のとき、曲線はゆるやかになります。

t_minus.gif

正の値のとき曲線はきつくなります。

t_plus.gif

c (continuity:連続性) は、変化点を通過する際の角度変化を急激にするか、ゆるやかにするかを左右します。連続性 (continuity) が 0 のとき角度はゆるやかになります。

c_zero.gif

0 以外のとき角度は急になります。

c_not_zero.gif

b (bias) は、変化点を通過する曲線の接線方向の傾斜を変化させます。傾斜 (bias) が負のとき変化点の開始側のカーブが大きくなり、終了側の傾斜が急になります。

b_minus.gif

正のときは変化の開始側の傾斜が急になり、終了側のカーブが大きくなります。

b_plus.gif

TCBSplinePathInterpolator は抽象クラスです。実際にはそのサブクラスの RotPosScaleTCBSplinePathInterpolator を使用します。

com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePathInterpolator

com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePathInterpolator は、変化点ごとに TCBKeyFrame を設定し、その間をスプライン補間して変化させる Interpolator です。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    +--javax.media.j3d.Behavior
                          |
                          +--javax.media.j3d.Interpolator
                                |
                                +--com.sun.j3d.utils.behaviors.interpolators.TCBSplinePathInterpolator
                                      |
                                      +--com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePathInterpolator

クラス宣言

public class RotPosScaleTCBSplinePathInterpolator
extends TCBSplinePathInterpolator

コンストラクター

public RotPosScaleTCBSplinePathInterpolator(Alpha alpha,                   // 変化の元になる Alpha
                                            TransformGroup target,         // 変更対象の TransformGroup
                                            Transform3D axisOfRotPosScale, // 変更時のローカル座標系
                                            TCBKeyFrame[] keys)            // 変化点ごとの TCBKeyFrame

コンストラクターでは変更対象の TransformGroup、変化の軸になる Transform3D、変化点ごとの TCBKeyFrame の配列を指定します。

サンプルの実行結果を見てください。

TCBSplinePathTest.gif

変化点ごとに小さい ColorCube が置いてあり、マウスの左ボタンドラッグで回転、中央ボタンドラッグで Z軸方向への移動、右ボタンドラッグで X、Y方向への移動ができます。

k0〜k3TextField に Alpha値を入力すると、変化点ごとの Alpha値を変更することができます。

t, c, b のそれぞれの TextField-1.0〜+1.0 の値を入力すると変化点ごとの張力、連続性、傾斜を変更することができます。

s0〜s3TextField に拡大縮小率を入力すると、変化点ごとの拡大縮小率を変更できます。拡大縮小率は X, Y, Z軸それぞれに設定することも可能ですが、ここではすべての軸に同じ拡大縮小率を適用しています。

[Gen] ボタンを押すと RotPosScaleTCBSplinePathInterpolator を生成し、変化を開始します。TCBSplinePathInterpolator には、動作の途中で TCBKeyFrame の値を変更するためのメソッドが無いのでこのようにしました。

このサンプルのソースは次の通りです。

TCBSplinePathTest.java (一部)
  1  // Java 3D Test Applet
  2  // TCBSplinePathTest.java
  3  //   Copyright (c) 1999 ENDO Yasuyuki
  4  //                      mailto:yasuyuki@javaopen.org
  5  //                      http://www.javaopen.org/j3dbook/index.html
  6  
  7  import java.applet.*;
  8  import java.awt.*;
  9  import java.awt.event.*;
 10  import javax.media.j3d.*;
 11  import javax.vecmath.*;
 12  import com.sun.j3d.utils.applet.MainFrame;
 13  import com.sun.j3d.utils.universe.SimpleUniverse;
 14  import com.sun.j3d.utils.behaviors.picking.PickRotateBehavior;
 15  import com.sun.j3d.utils.behaviors.picking.PickTranslateBehavior;
 16  import com.sun.j3d.utils.behaviors.picking.PickZoomBehavior;
 17  import com.sun.j3d.utils.behaviors.picking.PickObject;
 18  import com.sun.j3d.utils.behaviors.picking.PickingCallback;
 19  import com.sun.j3d.utils.geometry.ColorCube;
 20  import com.sun.j3d.utils.geometry.Primitive;
 21  import com.sun.j3d.utils.geometry.Box;
 22  import com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePathInterpolator;
 23  import com.sun.j3d.utils.behaviors.interpolators.TCBKeyFrame;
 24  
 25  public class TCBSplinePathTest extends Applet {
 26    private Canvas3D canvas = null;
 27    private SimpleUniverse universe = null;
 28    private BranchGroup scene = null;
 29    private Point3f[] points = null;
 30    private TCBKeyFrame[] keys = null;
 31    private TransformGroup[] strans = null;
 32    private AlphaPanel apanel = null;
 33    private TransformGroup ctrans = null;
 34    private BoundingSphere bounds = null;
 35    private RotPosScaleTCBSplinePathInterpolator rinterp = null;
         :
435    private BranchGroup createSceneGraph() {
436      BranchGroup root = new BranchGroup();
437      root.setCapability(BranchGroup.ALLOW_DETACH);
438      
439      bounds = new BoundingSphere(new Point3d(), 100.0);
440  
441      TransformGroup mtrans = new TransformGroup();
442      mtrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
443      mtrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
444      mtrans.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
445      mtrans.setUserData("mtrans");
446      root.addChild(mtrans);
447  
448      final TransformGroup rtrans = new TransformGroup();
449      rtrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
450      rtrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
451      root.addChild(rtrans);
452      
453      points = new Point3f[4];
454      points[0] = new Point3f( 0.0f, -0.6f, 0.1f);
455      points[1] = new Point3f(-0.4f, -0.2f, 0.1f);
456      points[2] = new Point3f( 0.4f,  0.2f, 0.1f);
457      points[3] = new Point3f( 0.0f,  0.6f, 0.1f);
458      
459      keys = new TCBKeyFrame[4];
460  
461      keys[0] =
462        new TCBKeyFrame(0.0f, 0, new Point3f(points[0]),
463                        new Quat4f(0.0f, 0.0f, 0.0f, 0.0f),
464                        new Point3f(1.0f, 1.0f, 1.0f), 0.0f, 0.0f, 0.0f);
465      keys[1] =
466        new TCBKeyFrame(0.4f, 0, new Point3f(points[1]),
467                        new Quat4f(0.0f, 0.0f, 0.0f, 0.0f),
468                        new Point3f(1.0f, 1.0f, 1.0f), 0.0f, 0.0f, 0.0f);
469      keys[2] =
470        new TCBKeyFrame(0.6f, 0, new Point3f(points[2]),
471                        new Quat4f(0.0f, 0.0f, 0.0f, 0.0f),
472                        new Point3f(1.0f, 1.0f, 1.0f), 0.0f, 0.0f, 0.0f);
473      keys[3] =
474        new TCBKeyFrame(1.0f, 0, new Point3f(points[3]),
475                        new Quat4f(0.0f, 0.0f, 0.0f, 0.0f),
476                        new Point3f(1.0f, 1.0f, 1.0f), 0.0f, 0.0f, 0.0f);
477  
478      TransformGroup[] ptrans = new TransformGroup[4];
479      for (int i=0; i<4; i++) {
480        Transform3D pt3d = new Transform3D();
481        pt3d.set( new Vector3f( (Tuple3f)keys[i].position ) );
482        ptrans[i] = new TransformGroup(pt3d);
483        ptrans[i].setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
484        ptrans[i].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
485        rtrans.addChild(ptrans[i]);
486      }
487  
488      strans = new TransformGroup[4];
489      for (int i=0; i<4; i++) {
490        strans[i] = createCube(0.05, new Vector3d(), true);
491        strans[i].setUserData("p" + Integer.toString(i));
492        ptrans[i].addChild(strans[i]);
493      }
494    
495      PickRotateBehavior mrotator =
496        new PickRotateBehavior(root, canvas, bounds, PickObject.USE_GEOMETRY);
497      mrotator.setupCallback( new PickingCallback() {
498        public void transformChanged(int type, TransformGroup trans) {
499          if (trans != null) {
500            String udata = (String)trans.getUserData();
501            if (udata != null) {
502              //System.out.println(udata);//DEBUG
503              Transform3D t3d = new Transform3D();
504              trans.getTransform(t3d);
505              if (udata.equals("mtrans")) {
506                rtrans.setTransform(t3d);
507              } else if (udata.equals("p0")) {
508                Quat4f quat = new Quat4f();
509                t3d.get(quat);
510                keys[0].quat = quat; // TEST
511              } else if (udata.equals("p1")) {
512                Quat4f quat = new Quat4f();
513                t3d.get(quat);
514                keys[1].quat = quat;
515              } else if (udata.equals("p2")) {
516                Quat4f quat = new Quat4f();
517                t3d.get(quat);
518                keys[2].quat = quat;
519              } else if (udata.equals("p3")) {
520                Quat4f quat = new Quat4f();
521                t3d.get(quat);
522                keys[3].quat = quat;
523              }
524            }
525          }
526        }
527      });
528      root.addChild(mrotator);
529  
530      PickTranslateBehavior translator =
531        new PickTranslateBehavior(root, canvas, bounds, PickObject.USE_GEOMETRY);
532      translator.setupCallback( new PickingCallback() {
533        public void transformChanged(int type, TransformGroup trans) {
534          if (trans != null) {
535            String udata = (String)trans.getUserData();
536            if (udata != null) {
537              //System.out.println(udata);//DEBUG
538              Transform3D t3d = new Transform3D();
539              trans.getTransform(t3d);
540              if (udata.equals("mtrans")) {
541                rtrans.setTransform(t3d);
542              } else if (udata.equals("p0")) {
543                Vector3f pos = new Vector3f();
544                t3d.get(pos);
545                keys[0].position.x = points[0].x + pos.x;
546                keys[0].position.y = points[0].y + pos.y;
547              } else if (udata.equals("p1")) {
548                Vector3f pos = new Vector3f();
549                t3d.get(pos);
550                keys[1].position.x = points[1].x + pos.x;
551                keys[1].position.y = points[1].y + pos.y;
552              } else if (udata.equals("p2")) {
553                Vector3f pos = new Vector3f();
554                t3d.get(pos);
555                keys[2].position.x = points[2].x + pos.x;
556                keys[2].position.y = points[2].y + pos.y;
557              } else if (udata.equals("p3")) {
558                Vector3f pos = new Vector3f();
559                t3d.get(pos);
560                keys[3].position.x = points[3].x + pos.x;
561                keys[3].position.y = points[3].y + pos.y;
562              }
563            }
564          }
565        }
566      });
567      root.addChild(translator);
568      
569      PickZoomBehavior zoomer =
570        new PickZoomBehavior(root, canvas, bounds, PickObject.USE_GEOMETRY);
571      zoomer.setupCallback( new PickingCallback() {
572        public void transformChanged(int type, TransformGroup trans) {
573          if (trans != null) {
574            String udata = (String)trans.getUserData();
575            if (udata != null) {
576              //System.out.println(udata);//DEBUG
577              Transform3D t3d = new Transform3D();
578              trans.getTransform(t3d);
579              if (udata.equals("mtrans")) {
580                rtrans.setTransform(t3d);
581              } else if (udata.equals("p0")) {
582                Vector3f pos = new Vector3f();
583                t3d.get(pos);
584                keys[0].position.z = points[0].z + pos.z;
585              } else if (udata.equals("p1")) {
586                Vector3f pos = new Vector3f();
587                t3d.get(pos);
588                keys[1].position.z = points[1].z + pos.z;
589              } else if (udata.equals("p2")) {
590                Vector3f pos = new Vector3f();
591                t3d.get(pos);
592                keys[2].position.z = points[2].z + pos.z;
593              } else if (udata.equals("p3")) {
594                Vector3f pos = new Vector3f();
595                t3d.get(pos);
596                keys[3].position.z = points[3].z + pos.z;
597              }
598            }
599          }
600        }
601      });
602      root.addChild(zoomer);
603      
604      double[] vertices = { -0.8, -0.8, 0.0,   
605                            -0.8,  0.8, 0.0,   
606                            -0.4, -0.8, 0.0,   
607                            -0.4,  0.8, 0.0,   
608                             0.0, -0.8, 0.0,   
609                             0.0,  0.8, 0.0,   
610                             0.4, -0.8, 0.0,   
611                             0.4,  0.8, 0.0,   
612                             0.8, -0.8, 0.0,   
613                             0.8,  0.8, 0.0,   
614                            -0.8, -0.8, 0.0,   
615                             0.8, -0.8, 0.0,   
616                            -0.8, -0.4, 0.0,   
617                             0.8, -0.4, 0.0,   
618                            -0.8,  0.0, 0.0,   
619                             0.8,  0.0, 0.0,   
620                            -0.8,  0.4, 0.0,   
621                             0.8,  0.4, 0.0,   
622                            -0.8,  0.8, 0.0,   
623                             0.8,  0.8, 0.0 }; 
624  
625      LineArray geom = new LineArray( vertices.length / 3, GeometryArray.COORDINATES);
626      geom.setCapability(GeometryArray.ALLOW_INTERSECT);
627      geom.setCoordinates(0, vertices);
628      Shape3D grid = new Shape3D(geom);
629      grid.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
630      mtrans.addChild(grid);
631      
632      mtrans.addChild( createCorner(0.05f, new Vector3d(-0.8, -0.8, 0.0)) );
633      mtrans.addChild( createCorner(0.05f, new Vector3d( 0.8, -0.8, 0.0)) );
634      mtrans.addChild( createCorner(0.05f, new Vector3d(-0.8,  0.8, 0.0)) );
635      mtrans.addChild( createCorner(0.05f, new Vector3d( 0.8,  0.8, 0.0)) );
636  
637      ctrans = createCube(0.05, new Vector3d(0.0, 0.0, 0.1), false);
638      rtrans.addChild(ctrans);
639  
640      rinterp = createInterpolator(apanel.getAlpha(), ctrans, keys, bounds);...(1)
641      rinterp.setEnable(false);................................................(2)
642      root.addChild(rinterp);..................................................(3)
643      
644      return root;
645    }
646    
647    private RotPosScaleTCBSplinePathInterpolator
648    createInterpolator( Alpha alpha, TransformGroup trans,
649                        TCBKeyFrame[] keys, Bounds bounds )
650    {
651       Transform3D t3d = new Transform3D();
652       RotPosScaleTCBSplinePathInterpolator rinterp =
653         new RotPosScaleTCBSplinePathInterpolator(alpha, trans, t3d, keys);...(4)
654       rinterp.setSchedulingBounds(bounds);...................................(5)
655       return rinterp;
656    }

private メソッド createInterpolator を実行して RotPosScaleTCBSplinePathInterpolator を生成しています(1)。(2)で setEnable(false) を使って変化を停止させています。(3)で BranchGroupaddChild() しています。

createInterpolator メソッドでは、(4)で RotPosScaleTCBSplinePathInterpolator を生成し、(5)でスケジューリングが有効になる領域を設定しています。このメソッドは [Gen] ボタンが押されるたびに実行され 、変更された TCBKeyFrame の配列をもとに新たな RotPosScaleTCBSplinePathInterpolator が生成されます。

[Gen] ボタンの構築は次のようにしました。

 53      final Button sbutton = new Button("Gen");
 54      sbutton.addActionListener( new ActionListener() {
 55        public void actionPerformed(ActionEvent e) {
 56          String blabel = sbutton.getLabel();
 57          if (blabel.equals("Gen")) {
 58            universe.getLocale().removeBranchGraph(scene);
 59            removeChild(scene, rinterp);
 60            rinterp = createInterpolator(apanel.getAlpha(), ctrans, keys, bounds);
 61            addChild(scene, rinterp);
 62            apanel.reset();
 63            universe.addBranchGraph(scene);
 64            sbutton.setLabel("Stop");
 65          } else if (blabel.equals("Stop")) {
 66            rinterp.setEnable(false);
 67            sbutton.setLabel("Gen");
 68          }
 69        }
 70      });
 71      ppanels[0].add(sbutton);
ENDO Yasuyuki