Web 上で操作可能な仮想柔物体の変形操作システムの試作 2004 年 7 月 31 日 幸島 明男 1.はじめに バーチャルリアリティの有望なアプリケーションの一つとして,家具や不動産,自動車な どの商品紹介のインターネット上の Web ページが挙げられる.現在,多くの自動車メーカ が,車の 3 次元 CG モデルを,さまざまな角度から眺められるインタラクティブな Web ペ ージを提供している.しかしながら,このようなインタラクティブな商品提示は雰囲気を 楽しむには優れているが,実際の商品に“触れて”いるというような強い印象は残らない ものである.このような Web ページを,バーチャルリアリティの技術を用いて高度化する ことで,より訴求力のある商品紹介が可能にはならないだろうか.視覚情報だけではなく, 触覚情報などの感覚情報を補うことで,ユーザにより強い印象を残せることが期待される. そこで,本リポートでは,このような仮想現実感の技術を用いた商品紹介の Web ページ の実現に向けた最初の一歩として,Web 上で操作可能な柔物体の変形操作システムを提案 する.従来の Web 上の商品紹介ページの 3 次元 CG モデルの多くが剛体であり,柔物体を 操作するようなものは見当たらない.加えられる力に応じて,その形状をさまざまに変化 させる柔物体を提示する技術を用いることで,従来の剛体モデルでは困難であったやわら かい商品の紹介や,触覚情報を伴う商品紹介が実現できる. 以下では,作成した仮想柔物体の変形操作システムの内容について述べる.まず,作成 したシステムにおける柔物体のモデルについて述べる.本研究では,柔物体のモデルとし ては,もっとも良く知られているバネモデル(Mass-Spring-Damper Model) を用いてモデ ル化を行っている.次に,システムの実装について述べる.本システムでは,Web 上で仮 想物体を操作する機能を実現するために,Java3D を用いて実装を行った.最後に,システ ムの実際の動作例と操作方法について述べる.本システムは, http://www.carc.aist.go.jp/~sashima/vr/ にアクセスすることで,実際に動作させてみることが可能である1.作成したシステムの ソースリストは末尾に添付した. 1 Web ブラウザに加えて Java と Java3D のインストールが必要です(3 章参照). 2. 柔物体と操作のモデル化 柔物体のモデル化の手法としては,有限要素法とバネモデル(Mass-Spring-Damper Model) の 2 つが知られている.バネモデルは,有限要素法に比較して少ない計算量で変形の計算 が可能なため,実時間性の要求されるアプリケーションで,頻繁に用いられる手法である. そこで,本研究においてもバネモデルを用いてモデル化を行う. バネモデルでは,複数の質点がバネとダンパーによって相互に接続されたものとして, 柔物体をモデル化する.バネとダンパーは並列に接続されており,各質点 i にかかる力 Fi は,以下の運動方程式により表現できる. ⎧ ⎛ L ⎞ ⎫⎪ ⎪ ij ⎟ Fi = −∑ ⎨k ⎜1 − rij + Dvij ⎬ + mg ⎜ rij ⎟⎠ j ⎪ ⎪⎭ ⎩ ⎝ ここで k, Lij, rij, D, vij, m, g は,それぞれバネ定数,バネの自然長,質点 j に対する質 点 i の相対位置,ダンパー定数,質点 j に対する質点 i の相対速度,質点の質量,重力加速 度である. 本研究では,ユーザの柔物体の操作は,物体を押す行動に限定した.物体を押す行動は, 接触面に存在する複数の質点に対して外力 Fexternal を加える過程としてモデル化することが できる2.具体的には,以下のような運動方程式で表現できる. ⎧ ⎛ L ⎞ ⎫⎪ ⎪ ⎜ ij ⎟ Fi = −∑ ⎨k 1 − r + Dvij ⎬ + mg + Fexternal ⎜ ⎟ ij r j ⎪ ij ⎪⎭ ⎠ ⎩ ⎝ なお,外力 Fexternal はユーザが,徐々に力を加えていくことを想定して,⊿t 秒ごとに⊿F ずつ線形に増加するものとした. 上記の運動方程式を数値的に解くことで,質点の位置の時間変化が計算でき,物体の変 形が計算できる.運動方程式の数値計算はオイラー法により行う.具体的には,以下の式 により⊿t 秒後の質点 i の位置 ri と速度 vi を計算できる. Fi (t ) ∆t m ri (t + ∆t ) = ri (t ) + vi (t )∆t vi (t + ∆t ) = vi (t ) + このような運動方程式に基づいて,立方体を二つ重ねた形状の柔物体のモデルを構築し た(図 1 参照). 2実装上は,一度に力を加えられるのは, 一つの質点のみに限定した. 図 1 システムの動作画面(柔物体のバネモデル) 図 1 の青い箱(12 個)が質点の位置を表し,赤い円柱(50 本)が質点間を結ぶバネを表してい る. 質点は立方体の各頂点に存在するものとした.リンクは各質点間の全結合ではなく, 全結合された立方体2つを接合し,その接合面で2つのリンクと4つの質点を共有すると いう形にした.物理パラメータは,ばね定数:4.0 (N/m),ダンパー定数:0.15 (N/(m/s)),質 点の質量:0.05(Kg),重力加速度 9.80665(m/s2))とした.バネの自然長は質点間の初期位置 間の距離を自然長として与えた.⊿t は 25(ms)である.ユーザの加える外力は初期値を 0 (N)として,⊿t ごとに 0.01(N)ずつ増加していくものとした. 3. 実装 Web 上で操作可能な 3 次元 CG モデルを実現するには,Internet Explorer などの Web ブ ラウザ内に,3 次元グラフィックを描画することが必要である.現在,Web ブラウザを用 いた 3 次元グラフィックの描画システムとしては,VRML のプラグインや Java (+Java3D) のアプレットの利用が考えられる.ここでは,変形の計算プログラミングとの融合の容易 性から,Java と Java3D を用いて実装を行った.Java3D は,オブジェクト指向の 3 次元 グラフィック API を提供する Java のグラフィックライブラリであり,シーングラフと呼 ばれる木構造を用いて仮想環境内の物体を記述することができる.物体の挙動はイベント ハンドラのクラス Behavior のサブクラスを定義することで実現される.作成したプログラ ムのソースリストは末尾に添付した. オブジェクト指向により,質点,リンクなどがクラ スとして自然に記述できるため,拡張性の高いプログラムが作成可能である3. 具体的には SpringObject クラスにおいて,質点,リンクを定義することで,異なる構造の柔物体の変 形計算を行うことができる. 開発環境: ・ JDK1.4.2 SE ・ Java3D API 1.3.1(DirectX Version) ・ Browser: Internet Explorer 6.0 ・ OS: Windows XP ・ CPU: Pentium4 1GHz ・ Memory: 760 M Byte, ・ Graphics: Intel 855GM ・ 4.インストールと実行 本プログラムの実行には Java(JDK1.4.2 SE)および Java3D(Java3D API 1.3.1)のイ ンストールが必要である.Java との Runtime および Java と Java3D は以下の URL から 入手できる.(2004 年 7 月 31 日現在) ・ Java: http://java.sun.com/j2se/1.4.2/download.html ・ Java3D: http://java.sun.com/products/java-media/3D/download.html インストール後は下記の URL にアクセスすると図 1 に示したような Java のアプレットが 実行される4.実行されると,仮想柔物体の形状が質点の自重で変形するの様子が描画され るはずである. http://www.carc.aist.go.jp/~sashima/vr/ 3 しかし,行列演算は,オブジェクト指向になじまないのか,あまりきれいに書けないため, 若干中途半端になるようだ. 4 本リポート提出時の添付ファイル(zip)を解凍し,フォルダ中の index.html を開いても実 行できます. 操作 操作はマウスの左ボタンのみで行う.画面中の青い箱(質点)の上にマウスのポインタを移動 し,そこで左ボタンを押すと,その質点に対して鉛直下向きの力が働きつづける.ボタン を押している間は下向きの力が働きつづけ,ボタンを離すと力は働かなくなる.そのため, ボタンを押している間は変形をつづけ,離すと元の状態に戻ろうとする挙動がみられる. さらに力を加え続けると(地面との摩擦がないため)柔物体は横倒しになる(図2参照). 図2 マウス操作による質点への外力の付加 マウスで選択することで,任意の質点にいつでも力を加えることができる5.図3は,図 2 に示した操作で,横倒しになった柔物体の右端に力を加えている様子を示している. 図3 横倒しの物体へ外力を加えた様子 5.今後の課題 ここで提案したモデルでは,ヤング率など実際の物体との対応関係については検討してい ない.また,物体の表面やテクスチャの表現に関しても考慮しなかった.研究の目的から すると,こういった側面の検討も不可欠である.現実の物体との対応付けは今後の課題と したい. パラメータの設定によって形状が不安定になる場合があった.今回は,数値計算をオイ ラー法で行ったが,ルンゲクッタ法で行うことで改善が見られるかもしれない.体積や表 面積などの制約を,ヒューリスティックな制約として用いた計算アルゴリズムも検討する 余地がある.データファイルの読み込みやパラメータの設定パネルなどを追加することで, より一般性を持ったシステムが実現できる. 6.おわりに Java3D を用いて柔物体の変形操作を行うシステムを作成した.バネモデルによるモデル化 を行ったため,力覚フィードバックデバイスとの接続は容易であると予想される.機会が あれば接続を試してみたい. 7.参考文献 [1] 広内哲夫, Java3D グラフィックス, セレンディップ, 2004 [2] 河合裕文, 松宮雅俊, 竹村治雄, 横矢直和, 疎密バネモデルを用いた柔物体の仮想環境 下での視覚および力覚提示手法の提案, 信学技報, IE2000-167, Vol.100, No.607, pp.71-76, 2001 3 次元物体を射影された 2 次元画像中の点から選択するため, 質点を選択しにくい/選 択できない場合があります.少しマウスを移動したり,選択する質点を変えたりしてくだ さい. 5 付録:プログラムリスト package vr; import import import import import import import import import import java.applet.*; java.util.*; javax.media.j3d.*; javax.vecmath.*; java.awt.*; java.awt.event.*; com.sun.j3d.utils.applet.*; com.sun.j3d.utils.geometry.*; com.sun.j3d.utils.picking.*; com.sun.j3d.utils.universe.*; // 質点のクラス class Particle { boolean selected = false; Hashtable linkLength = new Hashtable(); Vector3f velocity = new Vector3f(0.0f, 0.0f, 0.0f); Vector3f force = null; double m = 0.05f; Vector3f position = null; boolean movable = true; TransformGroup view = new TransformGroup(); Vector linkedParticles = new Vector(); Box box = null; public Particle(Vector3f p, boolean movable) { this.movable = movable; this.view = createBoxView(); this.setLocation(p); } // 箱(質点)の描画 private TransformGroup createBoxView() { TransformGroup tg = new TransformGroup(); tg.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); tg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); Material matY = new Material(); matY.setDiffuseColor(new Color3f(0.0f, 0.0f, 1.0f)); // matY.setCapability(Material.ALLOW_COMPONENT_READ|Material.ALLOW_COMPON ENT_WRITE); Appearance ap = new Appearance(); ap.setMaterial(matY); ap.setCapability(Appearance.ALLOW_MATERIAL_READ | Appearance.ALLOW_MATERIAL_WRITE); int flag = Box.ENABLE_GEOMETRY_PICKING | Box.GENERATE_NORMALS; box = new Box(0.1f, 0.1f, 0.1f, flag, ap); // box.setCapability(Shape3D.ALLOW_APPEARANCE_READ|Shape3D.ALLOW_APPEARAN CE_WRITE); box.setUserData(this); tg.addChild(box); return tg; } public void addlinkedParticle(Particle p) { Vector3f tmp = new Vector3f(); tmp.sub(this.position, p.position); Float len = new Float(tmp.length()); linkLength.put(p, len); linkedParticles.add(p); } public TransformGroup getView() { return view; } public void setLocation(Vector3f translation) { // Location if (translation.y <= 0.0) { translation.y = 0.0f; velocity.y = 0.0f; } this.position = translation; // View Transform3D transform2 = new Transform3D(); transform2.setTranslation(translation); view.setTransform(transform2); } } // 質点間のリンクのクラス class Link { Particle p0, p1 = null; double l; TransformGroup view = null; Vector3f yOrient = new Vector3f(0.0f, 1.0f, 0.0f); Transform3D origin = null; public Link(Particle p0, Particle p1) { this.p0 = p0; this.p1 = p1; view = createCylinderView(); updateView(); } // 円柱の描画 private TransformGroup createCylinderView() { TransformGroup tg = new TransformGroup(); tg.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); tg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); Material matY = new Material(); matY.setDiffuseColor(new Color3f(1.0f, 0.0f, 0.0f)); Appearance ap = new Appearance(); ap.setMaterial(matY); Cylinder cyl = new Cylinder(0.05f, 1.0f, Cylinder.GENERATE_NORMALS, 10, 10, ap); tg.addChild(cyl); Transform3D transform = new Transform3D(); tg.getTransform(transform); origin = transform; return tg; } public void updateView() { Vector3f tmp = new Vector3f(); Vector3f housen = new Vector3f(); tmp.sub(p0.position, p1.position); float len = tmp.length(); housen.cross(yOrient, tmp); Transform3D transform0 = new Transform3D(origin); //view.getTransform(transform0); Transform3D transform1 = new Transform3D(); transform1.setScale(new Vector3d(1.0d, (double) len, 1.0d)); transform1.mul(transform0); AxisAngle4f angle = new AxisAngle4f(housen, yOrient.angle(tmp)); Transform3D transform2 = new Transform3D(); transform2.setRotation(angle); transform2.mul(transform1); Transform3D transform3 = new Transform3D(); transform3.setTranslation(new Vector3f( (p0.position.x + p1.position.x) / 2.0f, (p0.position.y + p1.position.y) / 2.0f, (p0.position.z + p1.position.z) / 2.0f)); transform3.mul(transform2); view.setTransform(transform3); } public TransformGroup getView() { return view; } } // 質点とリンク,およびその構造を定義するクラス class SpringBox { Vector links = null; Vector particles = null; TransformGroup view = null; public static Vector3f externalForce = null; Particle selectedParticle = null; // ばね定数とダンパー定数 double k = 4.0f; float d = 0.15f; // ⊿T float deltaX = 0.025f; // 重力 static Vector3f g = new Vector3f(0.0f, 9.80665f, 0.0f); public SpringBox() { view = new TransformGroup(); externalForce = new Vector3f(0.0f, 0.0f, 0.0f); links = new Vector(); particles = new Vector(); Particle Particle Particle Particle Particle Particle Particle Particle Particle Particle Particle Particle p0 = new Particle(new Vector3f(0.0f, 0.0f, 0.0f), true); p1 = new Particle(new Vector3f(0.0f, 0.0f, 1.0f), true); p2 = new Particle(new Vector3f(1.0f, 0.0f, 1.0f), true); p3 = new Particle(new Vector3f(1.0f, 0.0f, 0.0f), true); p4 = new Particle(new Vector3f(0.0f, 1.0f, 0.0f), true); p5 = new Particle(new Vector3f(0.0f, 1.0f, 1.0f), true); p6 = new Particle(new Vector3f(1.0f, 1.0f, 1.0f), true); p7 = new Particle(new Vector3f(1.0f, 1.0f, 0.0f), true); p8 = new Particle(new Vector3f(0.0f, 2.0f, 0.0f), true); p9 = new Particle(new Vector3f(0.0f, 2.0f, 1.0f), true); p10 = new Particle(new Vector3f(1.0f, 2.0f, 1.0f), true); p11 = new Particle(new Vector3f(1.0f, 2.0f, 0.0f), true); addParticle(p0); addParticle(p1); addParticle(p2); addParticle(p3); addParticle(p4); addParticle(p5); addParticle(p6); addParticle(p7); addParticle(p8); addParticle(p9); addParticle(p10); addParticle(p11); addLink(p0, addLink(p0, addLink(p0, addLink(p1, addLink(p1, addLink(p2, addLink(p4, addLink(p4, addLink(p5, addLink(p5, addLink(p6, addLink(p7, p1); p2); p3); p2); p3); p3); p5); p6); p6); p7); p7); p4); addLink(p4, addLink(p4, addLink(p4, addLink(p4, addLink(p5, p0); p1); p2); p3); p0); addLink(p5, addLink(p5, addLink(p5, addLink(p6, addLink(p6, addLink(p6, addLink(p6, addLink(p7, addLink(p7, addLink(p7, addLink(p7, p1); p2); p3); p0); p1); p2); p3); p0); p1); p2); p3); addLink(p4, addLink(p4, addLink(p4, addLink(p4, addLink(p5, addLink(p5, addLink(p5, addLink(p5, addLink(p6, addLink(p6, addLink(p6, addLink(p6, addLink(p7, addLink(p7, addLink(p7, addLink(p7, p11); p8); p9); p10); p8); p9); p10); p11); p9); p10); p11); p8); p10); p11); p9); p8); addLink(p8, p9); addLink(p8, p10); addLink(p8, p11); addLink(p9, p10); addLink(p9, p11); addLink(p10, p11); } // オイラー法 void calc() { for (int i = 0; i < particles.size(); i++) { Particle p = (Particle) particles.get(i); if (p.movable) { Vector3f f = new Vector3f(0.0f, 0.0f, 0.0f); for (int j = 0; j < p.linkedParticles.size(); j++) { Particle pj = (Particle) p.linkedParticles.get(j); Vector3f vij = new Vector3f(); vij.sub(p.velocity, pj.velocity); vij.scale(d); f.sub(vij); Vector3f rij = new Vector3f(); rij.sub(p.position, pj.position); // System.out.println("rij: " + rij); float len = rij.length(); // System.out.println(i + "rij: " + rij + " " + len); float origLength = ( (Float) p.linkLength.get(pj)).floatValue(); rij.scale( (float) (k * (origLength / len - 1.0f))); f.add(rij); // System.out.println(i + " f: " + f); } Vector3f mg = new Vector3f(g); mg.scale( (float) p.m); f.sub(mg); if (this.selectedParticle == p) { System.out.println(i + "," + externalForce); f.add(externalForce); } p.force = f; } // System.out.println("pos: " + p.position); } for (int i = 0; i < particles.size(); i++) { Particle p = (Particle) particles.get(i); if (p.movable) { Vector3f f = new Vector3f(p.force); f.scale( (float) (deltaX / p.m)); Vector3f v = new Vector3f(p.velocity); v.add(f); p.velocity = new Vector3f(v); Vector3f p1 = new Vector3f(); v.scale(deltaX); p1.add(p.position, v); p.setLocation(new Vector3f(p1)); } } for (int i = 0; i < links.size(); i++) { Link l = (Link) links.get(i); l.updateView(); } } void addParticle(Particle p0) { particles.add(p0); view.addChild(p0.getView()); } void addLink(Particle p0, Particle p1) { Link l = new Link(p0, p1); p0.addlinkedParticle(p1); p1.addlinkedParticle(p0); links.add(l); view.addChild(l.view); } public TransformGroup getView() { return view; } // 外力の負荷とリセット public synchronized void addForce(Vector3f v) { externalForce.x += v.x; externalForce.y += v.y; externalForce.z += v.z; } public void resetForce() { externalForce = new Vector3f(0.0f, 0.0f, 0.0f); } } // Graphics とユーザインターフェイス public class World extends Applet { static private boolean pushFlag = false; static private double radius = 300.0d; Canvas3D canvas = Canvas3D(SimpleUniverse.getPreferredConfiguration()); private PickCanvas pickCanvas; new public void init() { SimpleUniverse universe = createUniverse(canvas); BranchGroup bg = new BranchGroup(); DirectionalLight light = new DirectionalLight(new Color3f(1.0f, 1.0f, 1.0f), new Vector3f(0.0f, -0.5f, -1.0f)); BoundingSphere bounds = new BoundingSphere( new Point3d(0.0, 0.0, 0.0), radius); light.setInfluencingBounds(bounds); bg.addChild(light); //座標系の設定 TransformGroup tg = new TransformGroup(); tg.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); tg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); SpringBox spObj = new SpringBox(); SpringBehavior spring = new SpringBehavior(this, spObj); tg.addChild(spring.tg); KeyCheck key = new KeyCheck(this, spObj); tg.addChild(key.tg); tg = setviewpoint(tg); bg.addChild(tg); bg.addChild(spring); bg.addChild(key); this.add(canvas); pickCanvas = new PickCanvas(canvas, bg); pickCanvas.setMode(PickCanvas.BOUNDS); canvas.addMouseListener(new MyMouseAdapter(spObj)); universe.addBranchGraph(bg); } // ビューポイント private TransformGroup setviewpoint(TransformGroup tg) { Transform3D transform1 = new Transform3D(); tg.getTransform(transform1); Transform3D transform2 = new Transform3D(); transform2.setTranslation(new Vector3f(0.0f, -1.5f, -3.5f)); transform2.mul(transform1); AxisAngle4f angle = new AxisAngle4f(new Vector3f(0.0f, 1.0f, 0.0f), 0.6f); Transform3D transform3 = new Transform3D(); transform2.setRotation(angle); transform3.mul(transform2); tg.setTransform(transform3); return tg; } // 仮想世界の作成 private SimpleUniverse createUniverse(Canvas3D canvas) { this.setLayout(new BorderLayout()); this.add(canvas, BorderLayout.CENTER); SimpleUniverse universe = new SimpleUniverse(canvas); ViewingPlatform vp = universe.getViewingPlatform(); vp.setNominalViewingTransform(); return universe; } // 地面の描画 private TransformGroup createGround() { TransformGroup tg = new TransformGroup(); Material matY = new Material(); matY.setDiffuseColor(new Color3f(1.0f, 1.0f, 1.0f)); Appearance ap = new Appearance(); ap.setMaterial(matY); Box ground = new Box(100.f, 0.02f, 100.0f, Box.GENERATE_NORMALS, ap); tg.addChild(ground); Transform3D transform1 = new Transform3D(); tg.getTransform(transform1); Transform3D transform2 = new Transform3D(); transform2.setTranslation(new Vector3f(0.0f, -0.14f, 0.0f)); transform2.mul(transform1); tg.setTransform(transform2); return tg; } // 移動 public TransformGroup setLocation(TransformGroup translation) { Transform3D transform2 = new Transform3D(); transform2.setTranslation(translation); tg.setTransform(transform2); return tg; } tg, Vector3f // 構造の時間変化の制御(タイマー割り込み) class SpringBehavior extends Behavior { //変数の宣言 public TransformGroup tg = null; private WakeupCondition keyin = null; WakeupOnElapsedTime wakeTimes = null; private float angle = 0.5f; private World world = null; private SpringBox springObject = null; double deltaX = 0.01; double xStart = 0.00; double yStart = 1.0; double zStart = 0.0; double yLimit = 0.25; double x = xStart; double y = yStart; double z = zStart; double[] y_z = new double[2]; public SpringBehavior(World w, SpringBox sp) { this.world = w; springObject = sp; tg = new TransformGroup(); TransformGroup ground = createGround(); tg.addChild(springObject.getView()); tg.addChild(ground); BoundingSphere bounds = new BoundingSphere( new Point3d(0.0, 0.0, 0.0), 100.); this.setSchedulingBounds(bounds); } public void initialize() { wakeTimes = new WakeupOnElapsedTime(10); wakeupOn(wakeTimes); } public void processStimulus(Enumeration enum) { if (world.pushFlag) { springObject.addForce(new Vector3f(0.0f, -0.01f, 0.0f)); } springObject.calc(); wakeupOn(wakeTimes); } } // Mouse の割り込み処理 class KeyCheck extends Behavior { //変数の宣言 public TransformGroup tg = null; private WakeupCondition keyin = null; WakeupOnElapsedFrames wakeTimes = null; SpringBox spobj = null; private World w = null; public KeyCheck(World w, SpringBox spobj) { this.w = w; this.spobj = spobj; tg = new TransformGroup(); //影響範囲の設定 BoundingSphere bounds = new BoundingSphere( new Point3d(0.0, 0.0, 0.0), 100.); this.setSchedulingBounds(bounds); } public void initialize() { //起動条件の設定 WakeupCriterion[] events = new WakeupCriterion[2]; events[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED); events[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED); keyin = new WakeupOr(events); wakeupOn(keyin); } public void processStimulus(Enumeration enum) { if (w.pushFlag) { // System.out.println("Key Released"); spobj.resetForce(); spobj.selectedParticle = null; w.pushFlag = false; } else { w.pushFlag = true; } wakeupOn(keyin); } } // Mouse クリックの制御クラス(オブジェクトのピック) class MyMouseAdapter extends MouseAdapter { SpringBox spObj = null; public MyMouseAdapter(SpringBox spObj) { super(); this.spObj = spObj; } public void mouseReleased(MouseEvent evnt) { Object obj = getObject(evnt.getX(), evnt.getY()); if (obj == null) { return; } delete(obj); } public void mousePressed(MouseEvent evnt) { Object obj = getObject(evnt.getX(), evnt.getY()); if (obj == null) { return; } add(obj); } // ピックされたオブジェクトの登録と削除 public void add(Object obj) { if (obj instanceof Particle) { Particle p = (Particle) obj; spObj.selectedParticle = p; System.out.println("Selected: " + p); } else { } } public void delete(Object obj) { System.out.println("UnSelected..."); spObj.selectedParticle = null; } // Pick されたオブジェクトの取り出し private Object getObject(int x, int y) { pickCanvas.setShapeLocation(x, y); PickResult result = pickCanvas.pickClosest(); if (result != null) { Node node = result.getNode(PickResult.PRIMITIVE); Object obj = node.getUserData(); return obj; } return null; } } // メイン public static void main(String[] args) { World example = new World(); MainFrame frame = new MainFrame(example, 600, 480); } }
© Copyright 2024 ExpyDoc