/*
* @(#)BezierDemo.java
*
* Copyright (c) 2008 The authors and contributors of JHotDraw.
* You may not use, copy or modify this file, except in compliance with the
* accompanying license terms.
*/
package org.jhotdraw.samples.mini;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.*;
import org.jhotdraw.geom.*;
/**
* Demonstration of the curve fitting algorithm of class {@link Bezier}.
*
* @author Werner Randelshofer
* @version $Id$
*/
public class BezierDemo extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private static class Example {
double error;
BezierPath digitized = new BezierPath();
@Nullable BezierPath bezier;
@Nullable ArrayList<ArrayList<Point2D.Double>> segments;
public void invalidate() {
bezier = null;
segments = null;
}
}
private ArrayList<Example> examples = new ArrayList<Example>();
private JDialog dumpDialog;
private JTextArea dumpArea;
private class MouseHandler implements MouseMotionListener, MouseListener {
private Example example;
@Override
public void mouseDragged(MouseEvent e) {
double zoomFactor = getZoomFactor();
example.digitized.lineTo(e.getX() / zoomFactor, e.getY() / zoomFactor);
example.invalidate();
canvas.repaint();
}
@Override
public void mouseMoved(MouseEvent e) {
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
example = new Example();
examples.add(example);
example.error = getError();
double zoomFactor = getZoomFactor();
example.digitized.moveTo(e.getX() / zoomFactor, e.getY() / zoomFactor);
canvas.repaint();
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
private MouseHandler handler = new MouseHandler();
private class Canvas extends JPanel {
private static final long serialVersionUID = 1L;
@Override
public void paintComponent(Graphics gr) {
long start = System.currentTimeMillis();
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
// Update examples
for (Example ex : examples) {
if (ex.bezier == null) {
ex.bezier = Bezier.fitBezierPath(ex.digitized, ex.error);
}
if (ex.segments == null) {
ArrayList<Point2D.Double> digitizedPoints = new ArrayList<Point2D.Double>();
for (BezierPath.Node node : ex.digitized) {
digitizedPoints.add(new Point2D.Double(node.x[0], node.y[0]));
}
// Split into segments at corners
ex.segments = new ArrayList<ArrayList<Point2D.Double>>();
ex.segments = Bezier.splitAtCorners(digitizedPoints, 77 / 180d * Math.PI, getError() * 2);
// Clean up the data in the segments
for (int i = 0, n = ex.segments.size(); i < n; i++) {
ArrayList<Point2D.Double> seg = ex.segments.get(i);
seg = Bezier.removeClosePoints(seg, getError());
seg = Bezier.reduceNoise(seg, 0.8);
ex.segments.set(i, seg);
}
}
}
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform identityTransform = g.getTransform();
AffineTransform tx = g.getTransform();
double zoomFactor = getZoomFactor();
tx.scale(zoomFactor, zoomFactor);
g.setTransform(tx);
if (showPolylineCheck.isSelected()) {
g.setColor(Color.black);
for (Example ex : examples) {
g.draw(ex.digitized);
}
}
if (showBezierCheck.isSelected()) {
g.setColor(Color.blue);
for (Example ex : examples) {
g.draw(ex.bezier);
}
}
g.setTransform(identityTransform);
if (showDigitizedCheck.isSelected()) {
for (Example ex : examples) {
g.setColor(Color.white);
for (BezierPath.Node node : ex.digitized) {
g.fillRect((int) (node.x[0] * zoomFactor - 2), (int) (node.y[0] * zoomFactor - 2), 5, 5);
}
}
for (Example ex : examples) {
g.setColor(Color.black);
for (BezierPath.Node node : ex.digitized) {
g.fillRect((int) (node.x[0] * zoomFactor - 1), (int) (node.y[0] * zoomFactor - 1), 3, 3);
}
}
}
if (showPreprocessedCheck.isSelected()) {
for (Example ex : examples) {
g.setColor(Color.WHITE);
for (ArrayList<Point2D.Double> seg : ex.segments) {
for (int i = 0, n = seg.size(); i < n; i++) {
Point2D.Double node = seg.get(i);
g.fillRect((int) (node.x * zoomFactor - 2), (int) (node.y * zoomFactor - 2), 5, 5);
}
}
for (ArrayList<Point2D.Double> seg : ex.segments) {
for (int i = 0, n = seg.size(); i < n; i++) {
Point2D.Double node = seg.get(i);
g.setColor((i == 0 || i == n - 1) ? Color.RED : Color.CYAN);
g.fillRect((int) (node.x * zoomFactor - 1), (int) (node.y * zoomFactor - 1), 3, 3);
}
}
}
}
if (showControlsCheck.isSelected()) {
for (Example ex : examples) {
for (BezierPath.Node node : ex.bezier) {
if (node.mask == BezierPath.C0_MASK) {
} else if (node.mask == BezierPath.C1C2_MASK && node.keepColinear) {
g.setColor(Color.WHITE);
g.fillRect((int) (node.x[1] * zoomFactor - 2), (int) (node.y[1] * zoomFactor - 2), 5, 5);
g.fillRect((int) (node.x[2] * zoomFactor - 2), (int) (node.y[2] * zoomFactor - 2), 5, 5);
g.setColor(Color.CYAN);
g.fillRect((int) (node.x[1] * zoomFactor - 1), (int) (node.y[1] * zoomFactor - 1), 3, 3);
g.draw(new Line2D.Double(node.x[1] * zoomFactor, node.y[1] * zoomFactor, node.x[0] * zoomFactor, node.y[0] * zoomFactor));
g.fillRect((int) (node.x[2] * zoomFactor - 1), (int) (node.y[2] * zoomFactor - 1), 3, 3);
g.draw(new Line2D.Double(node.x[2] * zoomFactor, node.y[2] * zoomFactor, node.x[0] * zoomFactor, node.y[0] * zoomFactor));
} else {
if ((node.mask & BezierPath.C1_MASK) == BezierPath.C1_MASK) {
g.setColor(Color.WHITE);
g.fillRect((int) (node.x[1] * zoomFactor - 2), (int) (node.y[1] * zoomFactor - 2), 5, 5);
g.setColor(Color.MAGENTA);
g.fillRect((int) (node.x[1] * zoomFactor - 1), (int) (node.y[1] * zoomFactor - 1), 3, 3);
g.draw(new Line2D.Double(node.x[1] * zoomFactor, node.y[1] * zoomFactor, node.x[0] * zoomFactor, node.y[0] * zoomFactor));
}
if ((node.mask & BezierPath.C2_MASK) == BezierPath.C2_MASK) {
g.setColor(Color.WHITE);
g.fillRect((int) (node.x[2] * zoomFactor - 2), (int) (node.y[2] * zoomFactor - 2), 5, 5);
g.setColor(Color.MAGENTA);
g.fillRect((int) (node.x[2] * zoomFactor - 1), (int) (node.y[2] * zoomFactor - 1), 3, 3);
g.draw(new Line2D.Double(node.x[2] * zoomFactor, node.y[2] * zoomFactor, node.x[0] * zoomFactor, node.y[0] * zoomFactor));
}
}
}
for (BezierPath.Node node : ex.bezier) {
g.setColor(Color.WHITE);
g.fillRect((int) (node.x[0] * zoomFactor - 2), (int) (node.y[0] * zoomFactor - 2), 5, 5);
}
for (BezierPath.Node node : ex.bezier) {
g.setColor((node.keepColinear && node.mask != BezierPath.C0_MASK) ? Color.BLUE : Color.RED);
g.fillRect((int) (node.x[0] * zoomFactor - 1), (int) (node.y[0] * zoomFactor - 1), 3, 3);
}
}
}
long end = System.currentTimeMillis();
g.setColor(Color.BLACK);
g.drawString((end - start) + " ms", 5, g.getFontMetrics().getHeight());
}
}
private Canvas canvas;
/** Creates new form BezierDemo */
public BezierDemo() {
initComponents();
canvas = new Canvas();
canvas.setOpaque(true);
canvas.setBackground(Color.WHITE);
canvas.addMouseListener(handler);
canvas.addMouseMotionListener(handler);
add(canvas, BorderLayout.CENTER);
Point2D.Double[] d = { // Digitized points
};
BezierPath digi = new BezierPath();
digi.addPolyline(Arrays.asList(d));
Example ex = new Example();
examples.add(ex);
ex.digitized = digi;
ex.error = 2d;
}
public static void main(String[] arg) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Bezier Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new BezierDemo());
f.setPreferredSize(new Dimension(400, 300));
f.pack();
f.setVisible(true);
}
});
}
private double getSquaredError() {
double error = getError();
return error * error;
}
private double getError() {
double error = 2d / getZoomFactor();
return error;
}
private double getZoomFactor() {
return zoomSlider.getValue() / 100d;
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
jPanel1 = new javax.swing.JPanel();
toleranceLabel = new javax.swing.JLabel();
zoomSlider = new javax.swing.JSlider();
showDigitizedCheck = new javax.swing.JCheckBox();
showPreprocessedCheck = new javax.swing.JCheckBox();
showPolylineCheck = new javax.swing.JCheckBox();
showBezierCheck = new javax.swing.JCheckBox();
showControlsCheck = new javax.swing.JCheckBox();
eraseButton = new javax.swing.JButton();
dumpButton = new javax.swing.JButton();
setLayout(new java.awt.BorderLayout());
jPanel1.setBorder(javax.swing.BorderFactory.createEmptyBorder(12, 20, 20, 20));
jPanel1.setLayout(new java.awt.GridBagLayout());
toleranceLabel.setText("Zoom:");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
jPanel1.add(toleranceLabel, gridBagConstraints);
zoomSlider.setMaximum(800);
zoomSlider.setMinimum(100);
zoomSlider.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
zoomChanged(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 1.0;
jPanel1.add(zoomSlider, gridBagConstraints);
showDigitizedCheck.setText("Show Source Points");
showDigitizedCheck.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
checkboxPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
jPanel1.add(showDigitizedCheck, gridBagConstraints);
showPreprocessedCheck.setText("Show Preprocessed Points");
showPreprocessedCheck.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
checkboxPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
jPanel1.add(showPreprocessedCheck, gridBagConstraints);
showPolylineCheck.setSelected(true);
showPolylineCheck.setText("Show Polyline");
showPolylineCheck.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
checkboxPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(0, 20, 0, 0);
jPanel1.add(showPolylineCheck, gridBagConstraints);
showBezierCheck.setSelected(true);
showBezierCheck.setText("Show Bezier Path");
showBezierCheck.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
checkboxPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(0, 20, 0, 0);
jPanel1.add(showBezierCheck, gridBagConstraints);
showControlsCheck.setText("Show Bezier Controls");
showControlsCheck.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
checkboxPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
jPanel1.add(showControlsCheck, gridBagConstraints);
eraseButton.setText("Erase");
eraseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
eraseButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 4;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
jPanel1.add(eraseButton, gridBagConstraints);
dumpButton.setText("Dump");
dumpButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
dumpButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 3;
gridBagConstraints.gridy = 4;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
jPanel1.add(dumpButton, gridBagConstraints);
add(jPanel1, java.awt.BorderLayout.NORTH);
}// </editor-fold>//GEN-END:initComponents
private void eraseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_eraseButtonActionPerformed
examples.clear();
canvas.repaint();
}//GEN-LAST:event_eraseButtonActionPerformed
private void zoomChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_zoomChanged
canvas.repaint();
}//GEN-LAST:event_zoomChanged
private void dumpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dumpButtonActionPerformed
if (dumpDialog == null) {
dumpDialog = new JDialog();
dumpDialog.setTitle("Dump");
dumpArea = new JTextArea();
dumpDialog.add(new JScrollPane(dumpArea));
dumpDialog.setSize(400, 400);
}
StringBuilder buf = new StringBuilder();
buf.append(" Point2D.Double[] d = { // Digitized points \n");
for (int i = 0; i < examples.size(); i++) {
for (BezierPath.Node node : examples.get(i).digitized) {
buf.append(" new Point2D.Double(");
buf.append(node.x[0]);
buf.append(",");
buf.append(node.y[0]);
buf.append("),\n");
}
}
buf.append(" };\n");
dumpArea.setText(buf.toString());
dumpDialog.setVisible(true);
}//GEN-LAST:event_dumpButtonActionPerformed
private void checkboxPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkboxPerformed
canvas.repaint();
}//GEN-LAST:event_checkboxPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton dumpButton;
private javax.swing.JButton eraseButton;
private javax.swing.JPanel jPanel1;
private javax.swing.JCheckBox showBezierCheck;
private javax.swing.JCheckBox showControlsCheck;
private javax.swing.JCheckBox showDigitizedCheck;
private javax.swing.JCheckBox showPolylineCheck;
private javax.swing.JCheckBox showPreprocessedCheck;
private javax.swing.JLabel toleranceLabel;
private javax.swing.JSlider zoomSlider;
// End of variables declaration//GEN-END:variables
}