Thursday, April 17, 2014

Benchmarking operasi matriks pengganti loop di MATLAB

Dalam tulisan ini saya hanya sedikit mengulangi kembali beberapa hal yang saya peroleh dari pengalaman selama menggunakan MATLAB. Khususnya berkaitan dengan filosofi dari MATLAB itu sendiri yang merupakan laboratorium  matriks (matrix laboratory). Seperti kita ketahui dalam MATLAB semua operasi pemgrograman ditekankan dalam skema matriks. Jadi bagi pengguna yang terbiasa dengan bahasa lain, maka ada baiknya mempelajari beberapa operasi-operasi dasar dalam manipulasi matriks di MATLAB.

Sebenarnya tulisan ini hanya sekedar mengulangi apa yang sudah saya singgung di tulisan sebelum-nya mengenai seberapa besar pengaruh penggunaan operasi matriks dalam mereduksi waktu komputasi di MATLAB. Namun dalam tulisan ini saya lebih menekankan pada proses benchmarking-nya. Untuk membuktikan perbedaan besar antara menggunakan operasi matriks dengan menggunakan standar pemrograman biasa  (loop: for, while, dll) di MATLAB maka anda bisa mengetes potongan script berikut:
clear all ;
clc; 

% buat matriks M ukuran 5000 x 5000 yang elemennya bilangan bulat acak dari 1 sampai 10
M = randi(10,5000,5000);
% buat matriks N yang ukuran dan nilai elemennya sama dengan matriks M
% ingat, tanda  '='  di MATLAB bukan menandakan pointer kalo merujuk
% pada matriks. Jadi operasi pada elemen M tidak akan berpengaruh pada elemen N. 
N = M; 

[a,b] = size(M);
tic; 
for i=1:a,
    for j=1:b,
        if M(i,j) > 5
            M(i,j) = 20000; 
        end
    end
end
disp(['lama pake loop = ', num2str(toc)]);

tic; 
N(N>5) = 20000;
disp(['lama pake indeks = ', num2str(toc)]);

% cek apakah elemen kedua matriks sama... 
% tanda '==' pada MATLAB bukan menandakan cek objek, tapi cek elemen
if M==N,
    disp('yes..'); 
end
Hasil eksekusinya untuk situasi komputer saya (core i3 dengan ram 4 giga) adalah operasi matriks mampu mereduksi waktu komputasi hingga 5 kali lipat.


Mungkin ada yang menduga jangan-jangan ini dikarenakan waktu pertama running saja. Artinya andaikan operasi ini dilakukan berulang-ulang dalam waktu yang lama, maka hasilnya hampir mirip antara loop dengan operasi matriks. Anda bisa melakukan sedikit modifikasi pada script di atas yakni sebagai berikut:
clear all ;
clc; 
lama_loop = 0; 
lama_indeks = 0;
iter = 0; 
while iter < 23
    M = randi(10,5000,5000);
    N = M; 
    [a,b] = size(M);
    tic; 
    for i=1:a,
        for j=1:b,
            if M(i,j) > 5
                M(i,j) = 20000; 
            end
        end
    end
    lama_loop = lama_loop + toc; 
    tic; 
    N(N>5) = 20000;
    lama_indeks = lama_indeks + toc; 
    iter = iter + 1; 
end

disp(['lama operasi loop untuk 100 iterasi = ', num2str(lama_loop)]);
disp(['lama operasi matriks untuk 100 iterasi = ', num2str(lama_indeks)]);
Hasilnya kurang lebih sama.


Baru satu logika if dan satu loop sudah sekian waktu yang bisa dihemat dengan mengganti loop dengan operasi matriks. Sementara pada kenyataannya dalam suatu proyek komputasi ada banyak loop-loop yang sebenarnya tidak perlu dilakukan karena bisa diganti dengan operasi matriks. 

Intinya operasi matriks ini sangat berfungsi dalam kasus modifikasi elemen-elemen pada matriks. Bahkan hampir semua algoritma yang berhubungan  dengan operasi pada elemen-elemen matriks bisa dinotasikan ke dalam operasi matriks. 

Sunday, April 13, 2014

Menghitung luas area yang gelap pada image di MATLAB



Pada tulisan kali ini saya akan memberikan sedikit tutorial mengenai bagaimana menentukan persentase area pada gambar yang nilai pixel RGB-nya memilik karakter warna tertentu (misalnya gelap atau hitam). Tanpa perlu berbasa-basi saya langsung saja membagikan script disertai komentar seperlunya yang bisa saudara coba di MATLAB.
clc;
clear all ;
[a,b] = imread('polpot.jpg'); 
[m,n,o] = size(a);
% pertama akan ditentukan apakah nilai RGB pada koordinat seragam. Misalnya
% kita ambil sampel koordinat dari 10 baris pertama dan 10 kolom pertama. 
HJ = (a(1:10,1:10,1) == a(1:10,1:10,3) ) & ((a(1:10,1:10,1) == a(1:10,1:10,2) )) ;
HJ
%% 
% yang ternyata elemen matriks HJ  semua 1 yang menandakan semua nilai RGB piksel seragam.
% Untuk  membuktikan, coba kita Print 
% nilai elemen pada baris dan kolom tersebut
a(1:10,1:10,:)
%%
% sekarang karena outputnya seragam maka otomatis nilai sum terhadap
% seluruh matriks HJ akan sama dengan penjumlahan terhadap seluruh matrix
% 'ones' yang ukurannya sama dengan HJ. Jika logika ini kita perumum, akan bisa digunakan pada
% kasus matrik HH yang mencakup seluruh area dari image, yang  akan kita
% peroleh adalah
HH = (a(:,:,1) == a(:,:,2)) & (a(:,:,1) == a(:,:,3)); 
if sum(sum(HH)) == sum(sum(ones(m,n)))
    M  = a(:,:,1) <10;
    disp(['persen area yang nilai pixel-nya di bawah 10 (nilai 0 berarti hitam): ',... 
    num2str(sum(sum(M))/sum(sum(ones(m,n)))*100), ' persen']);
else 
    disp('nilai pixel tidak seragam')
    % karena nilai pixel tidak seragam, maka kita bisa gunakan asumsi bahwa
    % nilai pixel yang mendekati warna gelap itu yang nilai pixel-nya
    % kurang dari 5 untuk R, 5 untuk G, dan 5 untuk B. Memang nilai pixel
    % yang benar-benar hitam adalah R = 0, G = 0, dan B = 0. Akan tetapi
    % kita hanya mendapat sedikit porsi dari area image  untuk nilai tersebut. 
    % Jadi kita gunakan pendekatan tadi. 
    M = (a(:,:,1) < 5) & (a(:,:,2) < 5) & (a(:,:,3) < 5); 
    disp(['persen area yang nilai pixel untuk R < 5; G  < 5, dan B < 5: ',... 
    num2str(sum(sum(M))/sum(sum(ones(m,n)))*100), ' persen']);
end
%% 
% Kita juga bisa menentukan pada baris dan kolom mana saja yang nilai pixel
% R, G, B tidak seragam
logic = (a(:,:,1)== a(:,:, 2)) & (a(:,:,1)== a(:,:,3)); 
[x1,x2,x3] = ind2sub(size(a),find(~logic)); 
disp('10 baris pertama dan 10 kolom pertama yang pikselnya tidak seragam: '); 
x1(1:10)'
x2(1:10)'
disp('nilai RGB pada baris dan kolom tersebut: '); 
a(x1(1:10),x2(1:10),:)

Sunday, March 16, 2014

Clock follow Mouse di javaFX

Dalam demo ini saya sebenarnya ingin menunjukkan beberapa konsep pada javaFX yang sebenarnya kurang sempurna, atau mungkin saya saja yang belum tahu persis teknik yang paling bagus. Yakni bagaimana menjalankan beberapa animasi yang mengalami delay satu sama lain. Misalnya TranslateTransition di javaFx itu mau tidak mau harus dibikin secara bersamaan mulainya. Tidak bisa kita memulai secara terpisah. Ketika kita panggil perintah animation play, maka otomatis kesemuanya instance dari TranslateTransition itu jalan secara serta merta. Padahal saya menginginkan suatu kasus di mana antara masing-masing instance terdapat delay sehingga satu instance akan lebih dulu jalan ketimbang yang lain, dan pada masing-masing instance terdapat delay waktu. Saya mendapati suatu teknik yang bisa dipake, hanya saja teknik ini bisa dianggap kotor (dirty) dalam pemrograman javaFX, dan memang tidak sesuai dengan yang saya harapkan. Kotor dalam artian bukan pendekatan yang seharusnya diambil. Yakni dengan menggunakan java default Thread. Hasil yang diperoleh adalah Scenegraph dalam Thread berjalan dengan berat dan lambat. Demonya bisa dilihat di video berikut:



Makanya saya tidak bisa mewujudkan keinginan saya untuk merespon koordinat mouse saat ini bukan sekedar posisi klik dari mouse. Karena memang berat.

Concurrent programming untuk javaFX menggunakan Task dan Service class. Masalahnya adalah kedua kelas ini tidak bisa digunakan untuk memanipulasi Scenegraph. Hanya untuk keperluan-keperluan lain misalnya mendownload video, mengextract file atau lain-lain di luar manipulasi scenegraph. Sementara kita tidak mungkin menginterupsi main Thread dari program untuk memberi delay dari animasi.

Untuk source dari demo di atas Anda bisa lihat di sini:
https://github.com/gunungloli666/jam_follow_mouse/blob/master/src/fjr/mouse/follower/MouseFollowerPlugin.java

Saturday, February 8, 2014

Animasi Convex Hull

Convex hull didefenisikan sebagai sutau objek di mana garis yang menghubungkan titik-titik di dalam objek tersebut juga berada di dalam objek tersebut. Untuk menentukan convex hull dari kumpulan titik, terdapat beberapa algoritma yang digunakan, salah satunya adalah monotone chain. Animasinya dapat dilihat pada video berikut



Sementara kode untuk menggenerate-nya di javaFX dapat dilihat pada
https://gist.github.com/gunungloli666/8859178

Thursday, January 30, 2014

Trammel Archimedes di JavaFX

Berikut ini saya berikan contoh animasi javaFX yakni dalam membuat trammel Archimedes.



Adapun source code nya adalah
package fjr.ellipse;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;


// @author moh_fajar 
// @date: 31-1-2014

public class EllipseExp extends Application {

 public static void main(String[] args) {
  launch(args);
 }

 Timeline animation;
 GraphicsContext gc; 
 Canvas canvas; 
 Trammel tr ;
 
 double angle; 

 double canvasWidth = 450 , canvasHeight = 350 ; 
 @Override
 public void start(Stage primaryStage) throws Exception {
  Group root = new Group();
  Group child = new Group(); 
  primaryStage.setScene(new Scene(root,500, 500));
  angle = 45; 
  tr = new Trammel(200, 150 , 100, 60, angle);
  canvas = new Canvas(canvasWidth, canvasHeight); 
  gc = canvas.getGraphicsContext2D(); 
  root.getChildren().add(child); 
  child.getChildren().addAll(canvas);
  animation = new Timeline();
  animation.getKeyFrames().addAll(
    new KeyFrame(Duration.millis(10),
      new EventHandler() {
       @Override
       public void handle(ActionEvent arg0) {
        angle-= Math.PI/200 ; 
        tr.setAngle(angle);
        tr.calculatePosition();
        draw(gc);
       }
      }));

  primaryStage.show();
  
  animation.setCycleCount(Timeline.INDEFINITE);
  animation.setAutoReverse(false);
  animation.play(); 
 }
 
 void draw(GraphicsContext gc){
  gc.setFill(Color.WHITE);
  gc.fillRect(0, 0, canvasWidth, canvasHeight);
  gc.setStroke(Color.BLACK);
  gc.strokeRect(0, 0, canvasWidth, canvasHeight);
  gc.setFill(Color.RED);
  gc.fillRect(tr.x, tr.y, 5, 5);
  gc.setFill(Color.BLUE);
  gc.fillRect(tr.x1, tr.y1, 5, 5);
  
  gc.setFill(Color.GREENYELLOW);
  gc.fillRect(tr.x2, tr.y2, 5, 5);
  
  gc.setStroke(Color.RED);
  gc.strokeLine(tr.x, tr.y, tr.x2, tr.y2);
  
  
  gc.setStroke(Color.BLACK);
  gc.strokeLine(tr.x2, tr.y1 - 110 , tr.x2, tr.y1 + 110);
  gc.strokeLine(tr.x2- 200, tr.y1  , tr.x2 + 200, tr.y1 );
  
  gc.setStroke(Color.BLACK);
  gc.strokeOval(tr.getXCorrection() -   tr.getA(), tr.getYCorrection() -  
      tr.getB(), 2 * tr.getA() , 2 * tr.getB());
 }

 class Trammel {

  double angle;
  double p, q;

  public double x, y; // posisi dari rod
  public double x1, y1 ; // posisi dari pivot 1 
  public double x2, y2 ; // posisi dari pivot 2

  
  double xCorrection; 
  double yCorrection; 
  /*
   * Dalam Trammel Archimedes, terdapat dua titik pivot yakni A dan B.
   * Sementara yang men-trace elipse adalah titik C pada ujung trammel.
   * Jarak A ke B adalah p, sementara jarak B ke C adalah q, dengan
   * demikian lintasan titik C ditentukan oleh x = (p + q) cos \theta y =
   * q sin theta.  lihat: http://en.wikipedia.org/wiki/Trammel_of_Archimedes
   */

  public Trammel(double x, double y, double p, double q, double angle) {
   this.xCorrection = x; 
   this.yCorrection = y; 
   
   this.p = p;
   this.q = q;
   this.angle = angle; 
  }

  
  public void setAngle(double angle) {
   this.angle = angle;
  }

  public void calculatePosition() {
   x = xCorrection+  (p + q) * Math.cos(angle);
   y = yCorrection + (q) * Math.sin(angle);
   
   x1 = x - q * Math.cos(angle); 
   y1 = y -  q * Math.sin(angle);
   
   x2 = x1 -   p * Math.cos(angle); 
   y2 = y1 -   p * Math.sin(angle); 
  }
  
  public double getA(){return (p+q);}
  public double getB(){return q ;}
  public double getXCorrection() {return xCorrection;}
  public double getYCorrection() {return yCorrection; }
 }

}

Sunday, January 19, 2014

Mengganti access modifier (tipe variabel: private, protected, final menjadi public) di Java

Berikut ini saya berikan contoh kegunaan Reflection di java. Yakni untuk mengganti tipe access modifier. Misalnya kita mempunyai kelas yang field-nya dibuat private, maka kita dapat menggunakan reflection untuk membuatnya jadi public. Perhatikan contoh berikut

Kita dapat merubah nilai field name yang private sekaligus final dalam kelas tersebut dengan bantuan reflection di Java. Perhatikan contoh berikut

Hasil eksekusinya adalah:

Menggunakan generic untuk conversi list di Java

Salah satu dari kegunaan generic di java adalah untuk mengkonversi list objek tipe tertentu menjadi list objek tipe turunannya.  Jika kita ingin mengkonversi list yang berisi objek tipe A menjadi list objek tipe B (yang mengekstends A), itu tidak bisa dilakukan secara langsung, seperti contoh berikut

Pada contoh di atas method convert tersebut tidak bisa dituliskan seperti itu, walaupun kelas B mengekstends kelas A. Yang harus dilakukan adalah dengan menggunakan generic seperti potongan berikut

Dan itupun hanya berlaku jika kelas B mengekstends kelas A. Jika tidak akan terjadi error misalnya:

Yang akan menghasilkan output

Wednesday, January 8, 2014

Menyimpan gambar di canvas ke file di javaFX

Berikut ini saya berikan contoh bagaimana menyimpan hasil penggambaran free-form kita dalam javaFx canvas ke dalam file.
package fjr.test.testIO;
   import java.io.File;

import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.stage.*;

import javax.imageio.ImageIO;

import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.paint.Color;


public class PrintCanvas extends Application {

 GraphicsContext gc; 
 Canvas canvas; 
 double canvaswidth  = 300 , canvasheight = 350 ; 
 
    @Override
    public void start(Stage primaryStage) {
        Group root = new Group();
        Scene scene = new Scene(root, 400, 450);
        canvas = new Canvas(canvaswidth, canvasheight);
        canvas.setTranslateX(10);
        canvas.setTranslateY(50);
        gc = canvas.getGraphicsContext2D();
        gc.setLineWidth(23);
        gc.setFill(Color.GREEN);
        gc.setStroke(Color.BLUE);
        gc.setLineWidth(6);
        
        gc.strokeRect(0, 0, canvaswidth, canvasheight);
                
        gc.setStroke(Color.BLACK);
        
        final WritableImage wim = new WritableImage((int)canvaswidth, (int)canvasheight);
        root.getChildren().add(canvas); 
        primaryStage.setScene(scene);
        primaryStage.show();
        
        final  Button simpan = new Button();
        simpan.setTranslateX(10);
        simpan.setTranslateY(10);
        simpan.setPrefSize(100, 30);
        simpan.setText("SIMPAN");
  simpan.setOnAction(new EventHandler() {
   @Override
   public void handle(ActionEvent arg0) {
    canvas.snapshot(null, wim);
    File file = new File("E:/CanvasImage.png");
    try {
     ImageIO.write(SwingFXUtils.fromFXImage(wim, null), "png",
       file);
     System.out.println("File sudah disimpan"); 
    } catch (Exception s) {
    }
   }
  });

  root.getChildren().add(new HBox(){{
   getChildren().add(simpan);
  }}); 
  

  canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler() {
            @Override
            public void handle(MouseEvent e) {
             gc.lineTo(e.getX(), e.getY());
             gc.stroke(); 
            }
        });
  
  canvas.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler() {
            @Override
            public void handle(MouseEvent e) {
             gc.beginPath();
             gc.moveTo(e.getX(), e.getY());
             gc.stroke();
            }
        });

    }

    public static void main(String[] args) {
        launch(args);
    }

}
Hasilnya adalah


Tuesday, January 7, 2014

Cara menggabungkan javaFX dengan java Swing

Tanpa perlu berbasa-basi berikut saya berikan contoh menggabungkan teknologi javaFX dengan java Swing. Berdasarkan informasi yang saya peroleh javaFX mungkin kedepannya dipake untuk menggantikan Swing.
package fjr.test.javafxpanel;

import java.awt.Dimension;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.util.Duration;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class TestJavaFXPanel {

 private XYChart.Series hourDataSeries;
    private XYChart.Series minuteDataSeries;
    private NumberAxis xAxis;
    private Timeline animation;

    private double hours = 0;
    private double minutes = 0;
    private double timeInHours = 0;
    private double prevY = 10;
    private double y = 10;
    
 public TestJavaFXPanel() {
  initAndShowGUI();
 }

 private void initAndShowGUI() {
  JFrame frame = new JFrame("Test Swing");
  frame.setPreferredSize(new Dimension(600, 400));
  frame.pack();
  final JFXPanel fxPanel = new JFXPanel();
  frame.add(fxPanel);
  frame.setVisible(true);

  Platform.runLater(new Runnable() {
   @Override
   public void run() {
    initFX(fxPanel);
   }
  });
 }

 private void initFX(JFXPanel fxPanel) {
  Scene scene = createScene();
  fxPanel.setScene(scene);
  
   animation = new Timeline();
         animation.getKeyFrames().add(new KeyFrame(Duration.millis(1000/60), new EventHandler() {
             @Override public void handle(ActionEvent actionEvent) {
                 for(int count=0; count < 6; count++) {
                     nextTime();
                     plotTime();
                 }
             }
         }));

         animation.setCycleCount(Animation.INDEFINITE);
 }

 private Scene createScene() {
  StackPane root = new StackPane(){{
   setTranslateX(0);
   setTranslateY(0);
  }};
  
  Button button = new Button("PLAY"){{
   setMinSize(100, 30);
   setOnAction(new EventHandler() {
    
    @Override
    public void handle(ActionEvent arg0) {
      play();
    }
   });
  }};
  
  
  Button button2 = new Button("STOP"){{
   setMinSize(100, 30);
   setOnAction(new EventHandler() {
    
    @Override
    public void handle(ActionEvent arg0) {
     // TODO Auto-generated method stub
      stop();
    }
   } );
  }};
  
  HBox box = new HBox(){{
   setSpacing(10);
   setTranslateX(10);
   setTranslateY(10);
  }}; 
  
  box.getChildren().addAll(button, button2);
  
  root.getChildren().addAll( new BorderPane(){{
   setLeft(createChart());
  }} , box); 
  
  Scene sc = new Scene(root);
  return sc;
 }

 public static void main(String[] args) {
  SwingUtilities.invokeLater(new Runnable() {
   @Override
   public void run() {
    new TestJavaFXPanel();
   }
  });
 }
    protected LineChart createChart() {
         xAxis = new NumberAxis(0,24,3);
         final NumberAxis yAxis = new NumberAxis(0,100,10);
         final LineChart lc = new LineChart(xAxis,yAxis);
         lc.setCreateSymbols(false);
         lc.setAnimated(false);
         lc.setLegendVisible(false);
         lc.setTitle("PLOT GRAFIK");
         xAxis.setForceZeroInRange(false);
         yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis,"$",null));
         hourDataSeries = new XYChart.Series();
         minuteDataSeries = new XYChart.Series();
         hourDataSeries.getData().add(new XYChart.Data(timeInHours,prevY));
         minuteDataSeries.getData().add(new XYChart.Data(timeInHours,prevY));
         for (double m=0; m<(60); m++) {
             nextTime();
             plotTime();
         }
         lc.getData().add(minuteDataSeries);
         lc.getData().add(hourDataSeries);
         
         lc.setTranslateX(10);
         lc.setTranslateY(40);
         lc.setMaxWidth(400);
         lc.setMaxHeight(330);
         return lc;
     }

     private void nextTime() {
         if (minutes == 59) {
             hours ++;
             minutes = 0;
         } else {
             minutes ++;
         }
         timeInHours = hours + ((1d/60d)*minutes);
     }
     private void plotTime() {
         if ((timeInHours % 1) == 0) {
             double oldY = y;
             y = prevY - 10 + (Math.random()*20);
             prevY = oldY;
             while (y < 10 || y > 90) y = y - 10 + (Math.random()*20);
             hourDataSeries.getData().add(new XYChart.Data(timeInHours, prevY));
             if (timeInHours > 25) hourDataSeries.getData().remove(0);
             if (timeInHours > 24) {
                 xAxis.setLowerBound(xAxis.getLowerBound()+1);
                 xAxis.setUpperBound(xAxis.getUpperBound()+1);
             }
         }
         double min = (timeInHours % 1);
         double randomPickVariance = Math.random();
         if (randomPickVariance < 0.3) {
             double minY = prevY + ((y-prevY) * min) - 4 + (Math.random()*8);
             minuteDataSeries.getData().add(new XYChart.Data(timeInHours,minY));
         } else if (randomPickVariance < 0.7) {
             double minY = prevY + ((y-prevY) * min) - 6 + (Math.random()*12);
             minuteDataSeries.getData().add(new XYChart.Data(timeInHours,minY));
         } else if (randomPickVariance < 0.95) {
             double minY = prevY + ((y-prevY) * min) - 10 + (Math.random()*20);
             minuteDataSeries.getData().add(new XYChart.Data(timeInHours,minY));
         } else {
             double minY = prevY + ((y-prevY) * min) - 15 + (Math.random()*30);
             minuteDataSeries.getData().add(new XYChart.Data(timeInHours,minY));
         }
         if (timeInHours > 25) minuteDataSeries.getData().remove(0);
     }
    public void play() {
         animation.play();

     }
    
      public void stop() {
         animation.pause();
     }    

  
}
Potongan hasil eksekusinya adalah sebagai berikut

Saturday, January 4, 2014

Menggunakan radio button dengan benar di JavaFX

Berikut ini adalah contoh source cara menggunakan radio button yang benar di JavaFX:
package fjr.test.toggle;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class TestToggleA extends Application {
 
 static enum Cuaca{
  CERAH , BERAWAN
 }

 Cuaca cuaca_now = Cuaca.CERAH; 
 
 public static void main(String[] args){
  Application.launch(args);
 }

 ToggleGroup groupToggle ; 
 
 RadioButton radio1 , radio2 ; 

 @Override
 public void start(Stage stage) throws Exception {
  Group root = new Group(); 
  
  groupToggle = new ToggleGroup(); 

  HBox box = new HBox(){{
   setTranslateX(30);
   setTranslateY(30);
   setSpacing(10);
   
   getChildren().addAll(
     radio1 = new RadioButton("BERSINAR"){{
      setToggleGroup(groupToggle);
      setUserData(Cuaca.CERAH);
     }}, 
     
     radio2 = new RadioButton("GELAP"){{
      setToggleGroup(groupToggle);
      setUserData(Cuaca.BERAWAN);
     }}); 
  }}; 
  
  
  switch(cuaca_now){
  case CERAH:
   radio1.setSelected(true);
   break; 
  case BERAWAN:
   radio2.setSelected(true);
   break; 
  }
  
  root.getChildren().add(box); 
  stage.setScene(new Scene(root, 200,200));
  stage.show(); 
  
  
  System.out.println("Type cuaca saat inisialisasi: "+ cuaca_now); 
  
  groupToggle.selectedToggleProperty().addListener(new ChangeListener() {
   @Override
   public void changed(ObservableValue arg0,
     Toggle arg1, Toggle toggle) {
    // TODO Auto-generated method stub
    RadioButton radio = (RadioButton) toggle; 
    Cuaca c = (Cuaca) radio.getUserData(); 
    cuaca_now = c; 
    System.out.println("Type cuaca saat pergantian button: "+cuaca_now);
   }
  });
  
 }
}