[vtkusers] VTK and JavaFX?
nagao saichi
nagao-saichi at jcom.home.ne.jp
Sat May 20 10:15:16 EDT 2017
I created SimpleVTKFX and SimpleVTKFX.
Idea of this class is vtk rendering image copy to javafx ImageView
pane by interval timmer. Copy method is , image file create by
vtkPNGWriter , after read from file to javaFX ImageView.
comment include japanese.
/****************************** SimpleVTKFX */
package vtk.sample;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.Callable;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import vtk.vtkActor;
import vtk.vtkConeSource;
import vtk.vtkDataSetMapper;
import vtk.vtkFXCanvas;
import vtk.vtkPolyDataMapper;
import vtk.vtkShrinkFilter;
import vtk.vtkSphereSource;
/**
* <p>
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version. A HTML version of the GNU General Public License can
be
* seen at http://www.gnu.org/licenses/gpl.html
* </p>
* <p>
* This program is distributed in the hope that it will be useful, but
WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
* </p>
*
* @author SaichiNagao
* date: 2017/05/12
*/
public class SimpleVTKFX extends Application {
vtkFXCanvas pane = null;
// -----------------------------------------------------------------
// copy from Demo.java
public class PipelineBuilder implements Callable<vtkActor> {
private vtkActor actor;
private vtkDataSetMapper mapper;
private vtkShrinkFilter shrink;
private vtkSphereSource sphere;
@Override
public vtkActor call() throws Exception {
// New
actor = new vtkActor();
mapper = new vtkDataSetMapper();
shrink = new vtkShrinkFilter();
sphere = new vtkSphereSource();
// Settings
sphere.SetPhiResolution(30);
sphere.SetThetaResolution(30);
double[] center = new double[3];
sphere.SetCenter(GetRandomCenter(center));
actor.GetProperty().SetColor(Math.random(), Math.random(),
Math.random());
shrink.SetShrinkFactor(0.8);
// Binding
shrink.SetInputConnection(sphere.GetOutputPort());
mapper.SetInputConnection(shrink.GetOutputPort());
// Update
actor.SetMapper(mapper);
// Wait some time
Thread.sleep((long) (Math.random() * 50));
// Return
return actor;
}
public double[] GetRandomCenter(double[] center) {
for (int i = 0; i < 3; i++) {
center[i] = Math.random() * 10;
}
return center;
}
}
// -----------------------------------------------------------------
@Override
public void start(Stage stage) throws Exception {
pane = new vtkFXCanvas();
// build VTK Pipeline
vtkConeSource cone = new vtkConeSource();
cone.SetResolution(16);
vtkPolyDataMapper coneMapper = new vtkPolyDataMapper();
coneMapper.SetInputConnection(cone.GetOutputPort());
vtkActor coneActor = new vtkActor();
coneActor.SetMapper(coneMapper);
pane.addActor(coneActor);
pane.getCanvas3D().addToPlaneWidget(coneActor);
pane.getCanvas3D().addToBoxWidget(coneActor);
/*
// more vtk objects add to canvas
PipelineBuilder bp = new PipelineBuilder();
for(int i=0;i< 100;i++) {
pane.addActor(bp.call());
}
*/
BorderPane border = new BorderPane();
Button exit = new Button("Exit");
EventHandler<MouseEvent>handler = ( event ) -> stop();
exit.addEventHandler(MouseEvent.MOUSE_CLICKED, handler);
border.setCenter(pane);
border.setBottom(exit);
BorderPane.setAlignment(exit, Pos.CENTER);
Scene thiscene = new Scene(border, 400, 400);
pane.renderStart();
stage.setScene(thiscene);
stage.setTitle("VTK FXCanvas Test...");
stage.show();
}
@Override
public void stop() {
pane.finalize();
System.exit(0);
}
// -----------------------------------------------------------------
public static void main(String[] args) throws InterruptedException,
InvocationTargetException {
launch(args);
}
}
/***************************** SimpleVTKFX*/
package vtk;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseWheelEvent;
import java.io.File;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javafx.embed.swing.SwingNode;
import javafx.event.EventHandler;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.StackPane;
/**
* JavaFXのアプリケーションにVTKCanvasを表示させるためのクラス。このクラスは、
* StackPaneを継承し、VTKCanvasをのせるためのSwingNodeと表示させるためのImageView
* を保存する。JavaFXでは、直接AWTを表示することができ無いので、いったんSwingNode
* に乗せたVTKCanvasでレンダリングを行い、タイマーを使用して、一定間隔で描画領域を
* イメージとして、ImageViewで表示する。マウスイベントは、ImageViewで捕捉し、
* AWTのイベントに変換してVTKCanvasのメソッドを実行する。
* <p>
* vtkCanvasでマウスによる入力は、直接は受け付けられないが、表示であれば、上記方法で
* 実現可能である。
* </p>
* <p>
* This class can controll Widget and draw actors. TrackballCamera
* interaction enable. vtkCanvas sources are not change.
* Resize pane is bad, resize smaller is bad.
* </p>
* <p>
* Idea of this class is vtk rendering image copy to javafx ImageView
* pane by interval timmer. Copy method is , image file create by
* vtkPNGWriter , after read from file to javaFX ImageView.
* </p>
* <p>
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version. A HTML version of the GNU General Public License can
be
* seen at http://www.gnu.org/licenses/gpl.html
* </p>
* <p>
* This program is distributed in the hope that it will be useful, but
WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
* </p>
*
* @author SaichiNagao
* date: 2017/05/12
*/
public class vtkFXCanvas extends StackPane {
//
// -----------------------------------------------------------------
// Load VTK library and print which library was not properly loaded
static {
if (!vtkNativeLibrary.LoadAllNativeLibraries()) {
for (vtkNativeLibrary lib : vtkNativeLibrary.values()) {
if (!lib.IsLoaded()) {
System.out.println(lib.GetLibraryName() + " not loaded");
}
}
}
vtkNativeLibrary.DisableOutputWindow(null);
}
// -----------------------------------------------------------------
/**
* vtkのレンダリングを行うパネル
*/
private vtkCanvas canvas3D = new vtkCanvas();
/**
* vtkCanvasのイメージをFXのImageViewにコピーする方法は、ファイルを介して
* 行うのが、簡単で早いみたい。一旦イメージファイルに保存して、ファイルから
* FXのImageViewに表示する。
*/
vtkPNGWriter writer = new vtkPNGWriter();
File tempFile = new File("__temp__.png");
String uri = tempFile.toURI().toString();
/**
* StackPaneに実装するノード、SwingNodeは、vtkCanvasをのせるために
* 使用する。
*/
SwingNode swingNode = new SwingNode();
/**
* vtkCanvasのレンダリングされたイメージを表示するために、ImageView
* を使用する。
*/
ImageView imageNode = new ImageView();
private Timer repaintimer;
/**
* 再描画時間間隔、イメージのコピーの時間により調整される。
*/
private int repaintInterval = 200;
/**
* イメージの設定フラグ
*/
private int setimageflag = -1;
/**
* コンストラクタ
*/
public vtkFXCanvas() {
EventHandler<MouseEvent>mHandler0 = ( event ) -> mousePressed(event);
EventHandler<MouseEvent>mHandler1 = ( event ) -> mouseDragged(event);
EventHandler<MouseEvent>mHandler2 = ( event ) -> mouseReleased(event);
EventHandler<ScrollEvent>mHandler3 = ( event ) ->
mouseWheelMoved(event);
EventHandler<MouseEvent>mHandler4 = ( event ) -> mouseMoved(event);
EventHandler<MouseEvent>mHandler5 = ( event ) -> mouseEntered(event);
EventHandler<MouseEvent>mHandler6 = ( event ) -> mouseExited(event);
EventHandler<KeyEvent>kHandler0 = ( event ) -> keyTyped(event);
// EventHandler<KeyEvent>kHandler1 = ( event ) -> keyPressed(event);
// EventHandler<KeyEvent>kHandler2 = ( event ) -> keyReleased(event);
imageNode.addEventHandler(MouseEvent.MOUSE_PRESSED , mHandler0 );
imageNode.addEventHandler(MouseEvent.MOUSE_DRAGGED , mHandler1 );
imageNode.addEventHandler(MouseEvent.MOUSE_RELEASED , mHandler2 );
imageNode.addEventHandler(ScrollEvent.SCROLL , mHandler3 );
imageNode.addEventHandler(MouseEvent.MOUSE_MOVED , mHandler4 );
imageNode.addEventHandler(MouseEvent.MOUSE_ENTERED , mHandler5 );
imageNode.addEventHandler(MouseEvent.MOUSE_EXITED , mHandler6 );
/*
* キーイベントは、swingNodeにキーフォーカスがあると、
* vtkCanvasに送られる。
* swingNode exists keyfocus , keyevent sends to vtkCanvas
*/
// imageNode.addEventHandler(KeyEvent.KEY_TYPED , kHandler1 );
// imageNode.addEventHandler(KeyEvent.KEY_PRESSED , kHandler0 );
// imageNode.addEventHandler(KeyEvent.KEY_RELEASED , kHandler2 );
// only for setimage method execute at key type.
swingNode.addEventHandler(KeyEvent.KEY_TYPED , kHandler0 );
getChildren().add(swingNode);
getChildren().add(imageNode);
// can not see , but must be true
swingNode.setVisible(true);
imageNode.setFocusTraversable(false);
swingNode.setFocusTraversable(true);
imageNode.setVisible(true);
imageNode.autosize();
// imageNode.requestFocus();
JPanel jcomp = new JPanel();
jcomp.setLayout(new BorderLayout());
jcomp.add(canvas3D, BorderLayout.CENTER);
swingNode.setContent(jcomp);
/*
* 以下の行を入れないと、小さくしたときリサイズしない。
* 最小のサイズが、起動した時のサイズになっている。
* if below line does not insert , then smaller resize is bad.
*/
setMinSize(1, 1);
}
/**
* マウスの押下イベント処理メソッド
* @param e FXのマウスイベントオブジェクト
*/
private void mousePressed(MouseEvent e) {
setimageflag = 1;
canvas3D.mousePressed(toSwingEvent(e));
}
/**
* マウスのドラッグイベント処理メソッド
* @param e FXのマウスイベントオブジェクト
*/
private void mouseDragged(MouseEvent e) {
setimageflag = 2;
canvas3D.mouseDragged(toSwingEvent(e));
}
/**
* マウスのリリースイベント処理メソッド
* @param e FXのマウスイベントオブジェクト
*/
private void mouseReleased(MouseEvent e) {
setimageflag = 4;
canvas3D.mouseReleased(toSwingEvent(e));
}
/**
* マウスの出域イベント処理メソッド
* @param e FXのマウスイベントオブジェクト
*/
private void mouseEntered(MouseEvent e) {
setimageflag = 8;
canvas3D.mouseEntered(toSwingEvent(e));
}
/**
* マウスの入域イベント処理メソッド
* @param e FXのマウスイベントオブジェクト
*/
private void mouseExited(MouseEvent e) {
setimageflag = 16;
canvas3D.mouseExited(toSwingEvent(e));
}
/**
* マウスのムーブイベント処理メソッド
* @param e FXのマウスイベントオブジェクト
*/
private void mouseMoved(MouseEvent e) {
// not repaint on mouse Move
setimageflag = 0;
canvas3D.mouseMoved(toSwingEvent(e));
}
/**
* マウスのスクロールイベント処理メソッド
* @param e FXのマウスイベントオブジェクト
*/
private void mouseWheelMoved(ScrollEvent e) {
int modifiers = 0;
int scrollType = MouseWheelEvent.WHEEL_UNIT_SCROLL;
int scrollAmount = e.getTouchCount();
int wheelRotation = (int)e.getDeltaY();
// System.out.println("wheelRotation="+wheelRotation);
if(e.isShiftDown()) modifiers+= java.awt.event.InputEvent.SHIFT_MASK;
if(e.isAltDown()) modifiers+= java.awt.event.InputEvent.ALT_MASK;
if(e.isControlDown()) modifiers+= java.awt.event.InputEvent.CTRL_MASK;
MouseWheelEvent mwe = new MouseWheelEvent(canvas3D,
MouseWheelEvent.WHEEL_UNIT_SCROLL,
System.currentTimeMillis(),
modifiers, (int)e.getX(), (int)e.getY(), e.getTouchCount(),
false, scrollType, scrollAmount, wheelRotation);
setimageflag = 64;
canvas3D.mouseWheelMoved(mwe);
}
/**
* FXのマウスイベントオブジェクトをSwingのマウスイベントオブジェクトに変換する。
* @param e FXのマウスイベントオブジェクト
* @return Swingのマウスイベントオブジェクト
*/
private java.awt.event.MouseEvent toSwingEvent(MouseEvent e) {
int modifiers = 0;
int button = 0;
if(e.isShiftDown()) modifiers+= java.awt.event.InputEvent.SHIFT_MASK;
if(e.isAltDown()) modifiers+= java.awt.event.InputEvent.ALT_MASK;
if(e.isControlDown()) modifiers+= java.awt.event.InputEvent.CTRL_MASK;
if(e.getButton() == MouseButton.PRIMARY) modifiers +=
java.awt.event.InputEvent.BUTTON1_MASK;
if(e.getButton() == MouseButton.MIDDLE) modifiers +=
java.awt.event.InputEvent.BUTTON2_MASK;
if(e.getButton() == MouseButton.SECONDARY) modifiers +=
java.awt.event.InputEvent.BUTTON3_MASK;
if(e.getButton() == MouseButton.PRIMARY) button = 0;
if(e.getButton() == MouseButton.MIDDLE) button = 1;
if(e.getButton() == MouseButton.SECONDARY) button = 2;
// System.out.println("button="+button+", modifiers="+modifiers+",
setimageflag="+setimageflag);
// System.out.println("X="+e.getX()+", Y="+e.getY()+",
getClickCount="+e.getClickCount());
java.awt.event.MouseEvent event = new java.awt.event.MouseEvent(canvas3D,
button, System.currentTimeMillis(), modifiers,
(int)e.getX(), (int)e.getY(), e.getClickCount(), false);
return event;
}
/**
* キータイプイベント処理メソッド
* @param e FXのキーイベント
*/
private void keyTyped(KeyEvent e) {
setimageflag = 128;
canvas3D.keyTyped(toSwingEvent(e));
}
/**
* キー押下イベント処理メソッド
* @param e FXのキーイベント
*/
private void keyPressed(KeyEvent e) {
setimageflag = 256;
canvas3D.keyPressed(toSwingEvent(e));
}
/**
* キーリリースイベント処理メソッド
* @param e FXのキーイベント
*/
private void keyReleased(KeyEvent e) {
setimageflag = 512;
canvas3D.keyReleased(toSwingEvent(e));
}
/**
* FXのキーベントオブジェクトをSwingのキーイベントオブジェクトに変換する。
* @param e FXのキーイベントオブジェクト
* @return Swingのキーイベントオブジェクト
*/
private java.awt.event.KeyEvent toSwingEvent(KeyEvent e) {
int id = 0;
long when = System.currentTimeMillis();
int modifiers = 0;
if(e.isShiftDown()) modifiers+= java.awt.event.InputEvent.SHIFT_MASK;
if(e.isControlDown()) modifiers+= java.awt.event.InputEvent.CTRL_MASK;
if(e.isAltDown()) modifiers+= java.awt.event.InputEvent.ALT_MASK;
char keychar = e.getCharacter().charAt(0);
int keycode = Integer.valueOf(keychar);
// System.out.println("keyChar="+keychar+", keyCode="+keycode+",
setimageflag="+setimageflag);
java.awt.event.KeyEvent event = new java.awt.event.KeyEvent(canvas3D,
id, when, modifiers, keycode, keychar);
return event;
}
/**
* vtkCanvasのイメージを、FXのImageViewにコピーする。ファイル渡しで
* コピーする。
*/
public synchronized void setImage() {
long st = System.currentTimeMillis();
vtkWindowToImageFilter w2if = new vtkWindowToImageFilter();
canvas3D.lock();
/**
* 以下の行を入力すると、Widgetの操作が正常に行うことができる。
* insert below line , then Widget controll is fine.
*/
canvas3D.getRenderWindowInteractor().SetEnableRender(false);
w2if.SetInput(canvas3D.GetRenderWindow());
w2if.Update();
canvas3D.unlock();
writer.SetInput(w2if.GetOutput());
writer.SetFileName(tempFile.getPath());
writer.Write();
javafx.scene.image.Image img = new javafx.scene.image.Image(uri);
imageNode.setImage(img);
// imageNode.setImage(createWritableImage());
long en = System.currentTimeMillis();
long diff = en-st+10;
if(repaintInterval < diff) {
// System.out.println("Copy imege time="+(diff-10)+" msec");
repaintInterval = (int)diff;
} else {
repaintimer.setDelay(repaintInterval-=5);
// System.out.println("repaint interval ="+repaintInterval);
}
// System.out.println("setimageflag="+setimageflag);
}
/**
* サイズの変更可能。
*/
public boolean isResizable() {
return true;
}
/**
* 描画領域の大きさを設定する。
* @param width 幅
* @param height 高さ
*/
public void setSize(int width, int height) {
canvas3D.setSize(width, height);
}
/**
* 描画領域のSwingオブジェクトを取得する。
* @return vtkCanvas
*/
public vtkCanvas getCanvas3D() {
return canvas3D;
}
/**
* 再描画タイマーオブジェクトの取得
* @return タイマーオブジェクト
*/
public Timer getRepaintimer() {
return repaintimer;
}
/**
* 表示アクターの追加
* @param actor 表示するアクター
*/
public void addActor(vtkActor actor) {
canvas3D.GetRenderer().AddActor(actor);
}
/**
* イメージノードの大きさをシーンに合わせます。タイマーを作成し
* 起動します。このメソッドは、シーンを作成した後に実行します。
* <p>
* This method execute after scene created.
* </p>
*/
public void renderStart() {
imageNode.fitWidthProperty().bind(widthProperty());
imageNode.fitHeightProperty().bind(heightProperty());
/*
* イメージコピータイマーの定義
*/
repaintimer = new Timer(repaintInterval, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(setimageflag == -1) {
setImage();
setimageflag = 1;
canvas3D.GetRenderer().ResetCamera();
} else if(setimageflag > 0) {
setImage();
setimageflag = 0;
SwingUtilities.invokeLater(() -> {
canvas3D.repaint();
// System.out.println("repaint");
});
}
}
});
repaintimer.setInitialDelay(800);
repaintimer.start();
}
/**
* 終了処理、タイマーの停止とイメージファイルの消去
*/
public void finalize() {
try {
super.finalize();
repaintimer.stop();
tempFile.delete();
setimageflag = -1;
} catch (Throwable e) {
e.printStackTrace();
}
}
}
--
View this message in context: http://vtk.1045678.n5.nabble.com/VTK-and-JavaFX-tp5728083p5743332.html
Sent from the VTK - Users mailing list archive at Nabble.com.
More information about the vtkusers
mailing list