立方体をマウスで拡大縮小する

立方体をマウスで拡大縮小する

 次は立方体の表示を拡大・縮小できるようにしたいと思います。これもマウスをセンサーにして動かすことをしてみます。
大きなフレームをビューポートの窓で覗くみたいなことをします。

変更しないのは、その1で作成した、GCanvas.javaとその3で修正したCubeVert.javaのみです。
 最初は、GFrameとViewpクラスの修正です。
GFrameは、マウスのホィールを回すことで拡大・縮小をします。フレームの横のスクロールバーは表示しないようにしました。
回転の計算指令もここへ移動しました。
拡大・縮小するためのマウスホィールの処理を追加しました。
また1秒タイマーのアクションイベントも追加しています。これは、拡大したときにフレームの表示がリペイントでは追随しないので無理矢理設定したものです。パネルのラベル文字を変化させるとかフレームの変更が発生するとうまく追随するみたい。素人なのでよく分かっていない。


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.JButton;

public class GFrame extends JFrame{

		  private MaPane MPane = null;
		  private GCanvas GCan = null;
		  private int DispWidth;
		  private int DispHeight;
		  private JPanel jPanel1 = new JPanel();
		  private BorderLayout borderLayout1 = new BorderLayout();
		  private JScrollPane jScrollPane1 = new JScrollPane();
		  private JButton jButton1 = new JButton();
		  private JToolBar jToolBar1 = new JToolBar();
		  /**
		   * メインフレームを作成する。
		   */
		  
		  public GFrame(Dimension Size){
		    this.DispWidth = Size.width;
		    this.DispHeight = Size.height;
		    // フレームの作成
		    initial();
		  }

		  /**
		   * フレームのイニシャル
		   */
		  void initial(){
		    // キャンバスの作成
		    GCan = new GCanvas();
		    MPane = new MaPane(GCan);	//add
		    // フレームの初期化
		    jInit();
		    jScrollPane1.setViewportView(MPane);
		    Viewp.setViewport(jScrollPane1.getViewport());
		    setSize(DispWidth, DispHeight);
		    setVisible(true);
		    MPane.setLayerSize();
		    // 再描画
		    GCan.repaint();
		  }

		  /**
		   * イベント初期化処理。
		   */
		  private void jInit() {
		    this.addWindowListener(new java.awt.event.WindowAdapter() {
		      public void windowClosing(WindowEvent e) {
		        this_windowClosing(e);
		      }
		    });
		    this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		    this.setTitle("ここはタイトル(^o^) ^o^ ^o^");
		    jButton1.setToolTipText("Reset");
		    jButton1.setBorder(null);
		    jButton1.setText("Reset");
		    jButton1.addActionListener(new java.awt.event.ActionListener() {
		      public void actionPerformed(ActionEvent e) {
		        jButton1_actionPerformed(e);
		      }
		    });	
		    jToolBar1.add(jButton1, null);
		    jPanel1.setLayout(borderLayout1);
		    this.getContentPane().add(jPanel1,  BorderLayout.CENTER);
		    jPanel1.add(jToolBar1, BorderLayout.NORTH);
		    jPanel1.add(jScrollPane1, BorderLayout.CENTER);

		// マウスアクションリスナー
		    jScrollPane1.addMouseListener(new java.awt.event.MouseAdapter() {
		    	public void mousePressed(MouseEvent e) {
		    		jScrollPane1_mousePressed(e);
		    	}
		    });
		    jScrollPane1.setWheelScrollingEnabled(false);
		    jScrollPane1.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
		        public void mouseDragged(MouseEvent e) {
		          jScrollPane1_mouseDragged(e);
		        }
		      });
		    jScrollPane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		    jScrollPane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
		    jScrollPane1.setWheelScrollingEnabled(false);
		    jScrollPane1.addMouseWheelListener(new MouseWheelListener() {
		      public void mouseWheelMoved(MouseWheelEvent e) {
		        jScrollPane1_mouseWheelMoved(e);
		      }
		    });
		  }

		  /**
		   * クローズボタンアクションリスナ
		   */
		  private void this_windowClosing(WindowEvent e) {
		    // 確認ダイアログの表示
			  if(JOptionPane.showConfirmDialog(this, "アプリケーションを終了します",
					  this.getTitle(),
					  JOptionPane.OK_CANCEL_OPTION)
					  != JOptionPane.YES_OPTION) { // OK
				  return;
			  }
			  System.exit(0);
		  }
		  
		  /**
		   * resetボタンアクションリスナ		   * 
		   */
		  void jButton1_actionPerformed(ActionEvent e) {
			  CubeVert.setangl();
			  Viewp.setFactor(Viewp.initFactor);
			  MPane.setLayerSize();
			  GCan.repaint();
		  }
		  
		  /**
		   * マウスアクションリスナ
		   */	
		  // マウスプレスイベント
		  private void jScrollPane1_mousePressed(MouseEvent e) {
			  del_x=e.getX();
			  del_y=e.getY();
		  }

		  // マウスドラッグイベント。
		  private void jScrollPane1_mouseDragged(MouseEvent e) {
			  double dx=e.getX()-del_x;
			  double dy=e.getY()-del_y;
			  del_x=e.getX();
			  del_y=e.getY();
			  phi=dy*sense;
			  theta = dx*sense;
			  CubeVert.setangl(phi,theta);
			  CubeVert.rotate();
			  GCan.repaint(); 
		  }
		  
		  //マウスホイールイベント
		private void jScrollPane1_mouseWheelMoved(MouseWheelEvent e) {
		    // 地図倍率変更
		    Viewp.changeFactor(0.1* e.getWheelRotation());
		    MPane.setLayerSize();
		    GCan.repaint();             // 地図描画
		   
		}
		public void Reset1sec() {
			javax.swing.Timer tm;
			tm = new javax.swing.Timer(1000, new ActionListener(){
				public void actionPerformed (ActionEvent e) {
			        if(tmh){jButton1.setText("Reset");}else{jButton1.setText("   ");}
			        if(tmh){jButton1.setForeground(Color.red);}else{jButton1.setForeground(Color.gray);}
					if(tmh){tmh=false;}else{tmh=true;}     	    		
				}
			});
		    // タイマ起動
			tm.start();				
	}
		
	boolean tmh=true;	
	double phi=0,theta=0;
	double sense=0.01;
		  
	double del_x=DrawGraphi.Center_x;
	double del_y=DrawGraphi.Center_y;
	
}

次は、Viewpクラスです。拡大・縮小で画面表示を変化させないといけないので、色々な変更を加えています。
拡大・縮小の比率データ、フレームの拡大縮小とビューポートの大きさと、表示との関係などを制御しています。
ほぼ全部変更ですので、一式載せます。


import java.awt.Point;
import java.awt.geom.Rectangle2D;

import javax.swing.JViewport;

public class Viewp {
	  // 表示倍率 初期値
	  static double initFactor = 1.0;
	  /** 表示倍率 */
	  static double Factor = 1.0;	//倍率
	  static double miniFactor=0.3; 
	  /** フレームサイズ(Frame_i×倍率)。 */
	  static Data2d FrameSize = new Data2d(1600.0d,1600.0d);   // 表示サイズ(FremeDist×倍率)
	  /** フレームサイズ(初期値)。 */
	  static Data2d FrameSize_i = new Data2d(1600.0d,1600.0d); 

	  static JViewport viewPort;// ビューポート
	  private Viewp(){
	  }

	  static void setViewport(JViewport v){      //ビューポートの設定
	    viewPort = v;
	  }

	  //フレームサイズの設定を行う。
	  static void setFrameSize_i(double x, double y){
//	    FrameSize_i.setData2d(x, y);
	    setFrameSize(x*Factor, y*Factor);
	  }

	//reset処理
	 //倍率の設定を行う
	  static void setFactor(double dx){
	    // 倍率更新
	    Factor=dx;
	    // フレームサイズ更新
	    setFrameSize(FrameSize_i.x*Factor, FrameSize_i.y*Factor);
	    setViewPort(); //ビューを中心に移動する
	  }
	  
		 //ビューを中心に移動する
	static void setViewPort(){
	    double difx = FrameSize.x/2 - viewPort.getWidth()/2;
	    double dify = FrameSize.y/2 - viewPort.getHeight()/2;
	    if(difx < 0){difx=0;}
	    if(dify < 0){dify=0;}
	    // ビューポートを移動
	    Point p = viewPort.getViewPosition();
	    p.setLocation(difx, dify);
	    viewPort.setViewPosition(p);
	}
	  
	 //倍率を変更する
	static void changeFactor(double f){
		double size;
		size = Factor+f;
	    if(size > miniFactor){
	      // 倍率変更
	      setFactor(Factor+f);
	    }
	  }

	  //フレームサイズの設定を行う
	  static void setFrameSize(double dx, double dy){
	    FrameSize.setData2d(dx, dy);
	  }
	  
//フレームサイズXを取得する。
	  static int getFrameSizeX(){
	    return (int) FrameSize.x;
	  }

//フレームサイズYを取得する。
	  static int getFrameSizeY(){
	    return (int) FrameSize.y;
	  }
}

/**
 * xy位置情報クラス
 */
class Data2d {
	double x=0.0;
	double y=0.0;

  public Data2d(){ }

  // double 位置情報
  public Data2d(double x,double y){
    this.x = x;
    this.y = y;
  }
  public void setData2d(double x,double y){
	    this.x = x;
	    this.y = y;
  }
}

大きな変更をしたのは、上の二つのクラスファイルのみです。
以下はマイナーな修正です。 最初はMaPaneクラスです。レイヤサイズの再設定を固定値ではなく可変としました。


import java.awt.Dimension;
import javax.swing.JLayeredPane;

public class MaPane extends JLayeredPane {

	private GCanvas GCan;

	/**
	* キャンバスを作成する。
	*/
	MaPane(GCanvas GCan){
		this.GCan = GCan;
	    this.add(GCan, new Integer(0));
	}
	  /**
	   * レイヤサイズを再設定
	   */
	  void setLayerSize(){
		  this.setPreferredSize(new Dimension((int)Viewp.getFrameSizeX(),(int)Viewp.getFrameSizeY()));
		  GCan.setBounds(0,0,(int)Viewp.getFrameSizeX(),(int)Viewp.getFrameSizeY());
	  }
}

 次は、DrawGraphiクラスです。
データをintで線を描いたりしていましたが、面倒になってきたので、intではなくdoubleで処理することにしました。その関係でg2.drawLine関数を変更しています。
その他は、フレームの大きさからフレームの中心を求めて、中心を基準に絵を描いています。


import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;

public class DrawGraphi {
		static double Center_x,Center_y;
		static int Canvas_w,Canvas_h;
		static double Factor;
		
	DrawGraphi(){
		initial();
	}

	void initial(){
		CubeVert.setModelData();
		CubeVert.rotate();
	}
	
	public void Draw(Graphics g){
		Graphics2D g2 = (Graphics2D)g;
		vtt=CubeVert.getVt_num();
		Canvas_w = Viewp.getFrameSizeX();
		Canvas_h = Viewp.getFrameSizeY();
		Center_x= Canvas_w/2;
		Center_y= Canvas_h/2;
		Viewp. setViewPort();
		Factor=Viewp.Factor;
		int x0,y0;
		int g_Scale=100;
		int line[][]=new int[8][2];
		g2.setBackground(backColor);
		g2.clearRect(0,0,Canvas_w,Canvas_h);
//		g2.setColor(dc);
//		CubeVert.rotate();
		for(int i=0;i<vtt;i++){
			x0=(int)((CubeVert.vt[i].x) * g_Scale) ;
			y0=(int)((CubeVert.vt[i].y) * g_Scale) ;
			line[i][0]=x0;line[i][1]=y0;
		}

		g2.setColor(new Color(255,255,255));	//white
		for(int j=0;j< vtt;j++){
			if(j==3){
	            g2.draw(new Line2D.Double(Center_x+line[j][0]*Factor,Center_y-line[j][1]*Factor,Center_x+line[j-3][0]*Factor,Center_y-line[j-3][1]*Factor));
				
			}else if(j==7){	//うまくor処理ができないので分離した
	            g2.draw(new Line2D.Double(Center_x+line[j][0]*Factor,Center_y-line[j][1]*Factor,Center_x+line[j-3][0]*Factor,Center_y-line[j-3][1]*Factor));
			}else{
	            g2.draw(new Line2D.Double(Center_x+line[j][0]*Factor,Center_y-line[j][1]*Factor,Center_x+line[j+1][0]*Factor,Center_y-line[j+1][1]*Factor));
			}
		}
		for(int i=0;i<vtt/2;i++){
            g2.draw(new Line2D.Double(Center_x+line[i][0]*Factor,Center_y-line[i][1]*Factor,Center_x+line[i+4][0]*Factor,Center_y-line[i+4][1]*Factor));
		}
		
		//xyz line
		g2.setColor(new Color(100,255,255));	//シアン
		
		for(int i=0;i<3;i++){
			if(i==1){
				g2.setColor(new Color(0,255,0));	//green
			}else if(i==2){
				g2.setColor(new Color(0,0,255));	//blue
			}
			x0=(int)((CubeVert.ax[i].x) * g_Scale) ;
			y0=(int)((CubeVert.ax[i].y) * g_Scale) ;
            g2.draw(new Line2D.Double(Center_x,Center_y,Center_x+x0*Factor,Center_y-y0*Factor));
		}
	  }
		int vtt=0;
		private Color dc =new Color(255,0,255);	//pink
		private Color backColor = new Color(0,0,80);//dark blue
}

 最後は、mainです。
GFrameに1秒タイマー処理を追加するための処理を追加しています。


import java.awt.Dimension;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.metal.MetalLookAndFeel;

public class MainCubeTest_01 {

	  /**
	   * メイン処理。
	   **/

	  public static void main(String[] args){
	    // スクリーンサイズを設定する
		// Dimension ScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
		Dimension ScreenSize =new Dimension(600,600);
		  
	    // フレームの作成
	    GFrame gframe= new GFrame (ScreenSize);
	    try {
	      UIManager.setLookAndFeel(new MetalLookAndFeel());
	      SwingUtilities.updateComponentTreeUI(gframe);
	    }catch(Exception e){
	      System.out.println(e);
	      System.exit(1);
	    }
	    gframe.Reset1sec();
	  }
}



実行すると、右図のようになります。
マウスホィールを回すと拡大・縮小しましたでしょうか?。リセットボタンが1秒ごとに赤い色に変化していますでしょうか?
濃い青い色の部分が、フレームの大きさです。縮小すると濃い青い色が縮小したままになります。

hello

拡大すると右図のようになります。
濃い青い色の部分が、一瞬小さくなってから元に戻るのが分かると思います。1秒タイマ-を待って元に戻ります。これをしないと小さくなったままになります。
フレームの中心がビューポートの中心になるようにしていますので、拡大すると中心を基準にして絵が大きくなります。四角の隅が見えなくなってしまいます。
拡大しても表示中心を変化させると、色々な部分を表示させることができます。これを実現するために、次はマウスを左ダブルクリックすることで中心を変化させてみましょう。








ちとプログラムファイルが増えてきましたので、javaファイルを添付しておきましょう。