/* * Copyright (C) 2022 JPEXS * * 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 3 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.helpers.Reference; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.JToggleButton; import javax.swing.SwingUtilities; import javax.swing.border.BevelBorder; /** * * @author JPEXS */ public class TransformPanel extends JPanel { private JTextField moveHorizontalTextField = new JTextField(8); private JTextField moveVerticalTextField = new JTextField(8); private JComboBox moveUnitComboBox = new JComboBox<>(); private JCheckBox moveRelativeCheckBox = new JCheckBox("Relative move"); private JTextField scaleWidthTextField = new JTextField(formatDouble(100), 8); private JTextField scaleHeightTextField = new JTextField(formatDouble(100), 8); private JComboBox scaleUnitComboBox = new JComboBox<>(); private JCheckBox scaleProportionallyCheckBox = new JCheckBox("Scale proportionally"); private JTextField rotateTextField = new JTextField(formatDouble(0), 8); private JComboBox rotateUnitComboBox = new JComboBox<>(); private JToggleButton rotateAntiClockwiseToggleButton = new JToggleButton(View.getIcon("rotateanticlockwise16")); private JToggleButton rotateClockwiseToggleButton = new JToggleButton(View.getIcon("rotateclockwise16")); private JTextField skewHorizontalTextField = new JTextField(formatDouble(0), 8); private JTextField skewVerticalTextField = new JTextField(formatDouble(0), 8); private JComboBox skewUnitComboBox = new JComboBox<>(); private JTextField matrixATextField = new JTextField(formatDouble(1), 8); private JTextField matrixBTextField = new JTextField(formatDouble(0), 8); private JTextField matrixCTextField = new JTextField(formatDouble(0), 8); private JTextField matrixDTextField = new JTextField(formatDouble(1), 8); private JTextField matrixETextField = new JTextField(formatDouble(0), 8); private JTextField matrixFTextField = new JTextField(formatDouble(0), 8); private JCheckBox matrixEditCurrentCheckBox = new JCheckBox("Edit current matrix"); private ImagePanel imagePanel; private Rectangle2D bounds = new Rectangle2D.Double(0, 0, 1, 1); private Point2D registrationPoint = new Point2D.Double(0, 0); private RegistrationPointPanel registrationPointPanel; public static enum UnitKind { LENGTH, ANGLE } public static enum Unit { PX("px", 1 / 20.0, UnitKind.LENGTH), TWIP("twip", 1.0, UnitKind.LENGTH), PERCENT("%", 0.0, UnitKind.LENGTH), TURN("turn", 1 / 360.0, UnitKind.ANGLE), DEG("°", 1, UnitKind.ANGLE), RAD("rad", Math.PI / 180, UnitKind.ANGLE), GRAD("grad", 1 / 0.9, UnitKind.ANGLE); private Unit(String name, double value, UnitKind kind) { this.name = name; this.value = value; this.kind = kind; } private final String name; private final double value; private final UnitKind kind; @Override public String toString() { return name; } public double getValue() { return value; } public UnitKind getKind() { return kind; } } private class UnitComboItem { Unit unit; String title; } public TransformPanel(ImagePanel imagePanel) { imagePanel.addBoundsChangeListener(new BoundsChangeListener() { @Override public void boundsChanged(Rectangle2D newBounds, Point2D registraionPoint) { update(newBounds, registraionPoint); } }); setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); this.imagePanel = imagePanel; setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; add(makeHeader("Registration point", "transformregpoint16")); JPanel registrationPointPanel = new JPanel(new FlowLayout()); this.registrationPointPanel = new RegistrationPointPanel(this::registrationPointChangedActionPerformed); registrationPointPanel.add(this.registrationPointPanel); add(registrationPointPanel); add(makeHeader("Move", "transformmove16")); JPanel movePanel = new JPanel(new GridBagLayout()); addRow(movePanel, 0, new JLabel("Horizontal:"), moveHorizontalTextField, moveUnitComboBox); addRow(movePanel, 1, new JLabel("Vertical:"), moveVerticalTextField); addJoinedRow(movePanel, 2, moveRelativeCheckBox, 3); addJoinedRow(movePanel, 3, makeClearApplyPanel(this::applyMoveActionPerformed, this::clearMoveActionPerformed), 3); add(movePanel); moveUnitComboBox.addItem(Unit.PX); moveUnitComboBox.addItem(Unit.TWIP); moveUnitComboBox.setSelectedItem(Unit.PX); addUnitChangeListener(moveUnitComboBox, new UnitChangedListener() { @Override public void unitChanged(Unit prevUnit, Unit newUnit) { try { double moveHorizontal = Double.parseDouble(moveHorizontalTextField.getText()); double moveVertical = Double.parseDouble(moveVerticalTextField.getText()); moveHorizontalTextField.setText(formatDouble(convertUnit(moveHorizontal, prevUnit, newUnit))); moveVerticalTextField.setText(formatDouble(convertUnit(moveVertical, prevUnit, newUnit))); } catch (NumberFormatException nfe) { return; } } }); add(makeHeader("Scale", "transformscale16")); JPanel scalePanel = new JPanel(new GridBagLayout()); addRow(scalePanel, 0, new JLabel("Width:"), scaleWidthTextField, scaleUnitComboBox); addRow(scalePanel, 1, new JLabel("Height:"), scaleHeightTextField); addJoinedRow(scalePanel, 2, scaleProportionallyCheckBox, 3); addJoinedRow(scalePanel, 3, makeClearApplyPanel(this::applyScaleActionPerformed, this::clearScaleActionPerformed), 3); add(scalePanel); scaleUnitComboBox.addItem(Unit.PERCENT); scaleUnitComboBox.addItem(Unit.PX); scaleUnitComboBox.addItem(Unit.TWIP); scaleUnitComboBox.setSelectedItem(Unit.PERCENT); addUnitChangeListener(scaleUnitComboBox, new UnitChangedListener() { @Override public void unitChanged(Unit prevUnit, Unit newUnit) { try { double scaleWidth = Double.parseDouble(scaleWidthTextField.getText()); double scaleHeight = Double.parseDouble(scaleHeightTextField.getText()); if (prevUnit == Unit.PERCENT) { scaleWidthTextField.setText(formatDouble(convertUnit(bounds.getWidth() * scaleWidth / 100, Unit.TWIP, newUnit))); scaleHeightTextField.setText(formatDouble(convertUnit(bounds.getHeight() * scaleHeight / 100, Unit.TWIP, newUnit))); return; } if (newUnit == Unit.PERCENT) { double scaleWidthTwip = convertUnit(scaleWidth, prevUnit, Unit.TWIP); double scaleHeightTwip = convertUnit(scaleHeight, prevUnit, Unit.TWIP); scaleWidthTextField.setText(formatDouble((scaleWidthTwip * 100 / bounds.getWidth()))); scaleHeightTextField.setText(formatDouble((scaleHeightTwip * 100 / bounds.getHeight()))); return; } scaleWidthTextField.setText(formatDouble(convertUnit(scaleWidth, prevUnit, newUnit))); scaleHeightTextField.setText(formatDouble(convertUnit(scaleHeight, prevUnit, newUnit))); } catch (NumberFormatException nfe) { return; } } }); scaleWidthTextField.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { if (scaleProportionallyCheckBox.isSelected()) { try { double scaleWidth = Double.parseDouble(scaleWidthTextField.getText()); double scaleHeight; if (scaleUnitComboBox.getSelectedItem() == Unit.PERCENT) { scaleHeight = scaleWidth; } else { double ratio = bounds.getHeight() / bounds.getWidth(); scaleHeight = ratio * scaleWidth; } scaleWidthTextField.setText(formatDouble(scaleWidth)); scaleHeightTextField.setText(formatDouble(scaleHeight)); } catch (NumberFormatException nfe) { } } } }); scaleHeightTextField.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { if (scaleProportionallyCheckBox.isSelected()) { try { double scaleHeight = Double.parseDouble(scaleHeightTextField.getText()); double scaleWidth; if (scaleUnitComboBox.getSelectedItem() == Unit.PERCENT) { scaleWidth = scaleHeight; } else { double ratio = bounds.getWidth() / bounds.getHeight(); scaleWidth = ratio * scaleHeight; } scaleWidthTextField.setText(formatDouble(scaleWidth)); scaleHeightTextField.setText(formatDouble(scaleHeight)); } catch (NumberFormatException nfe) { } } } }); add(makeHeader("Rotate", "transformrotate16")); ButtonGroup clockGroup = new ButtonGroup(); clockGroup.add(rotateClockwiseToggleButton); clockGroup.add(rotateAntiClockwiseToggleButton); JPanel rotatePanel = new JPanel(new GridBagLayout()); addRow(rotatePanel, 0, new JLabel("Angle:"), rotateTextField, rotateUnitComboBox, rotateAntiClockwiseToggleButton, rotateClockwiseToggleButton); addJoinedRow(rotatePanel, 1, makeClearApplyPanel(this::applyRotateActionPerformed, this::clearRotateActionPerformed), 5); add(rotatePanel); rotateUnitComboBox.addItem(Unit.TURN); rotateUnitComboBox.addItem(Unit.DEG); rotateUnitComboBox.addItem(Unit.RAD); rotateUnitComboBox.addItem(Unit.GRAD); rotateUnitComboBox.setSelectedItem(Unit.TURN); rotateClockwiseToggleButton.setSelected(true); addUnitChangeListener(rotateUnitComboBox, new UnitChangedListener() { @Override public void unitChanged(Unit prevUnit, Unit newUnit) { try { double rotate = Double.parseDouble(rotateTextField.getText()); rotateTextField.setText(formatDouble(convertUnit(rotate, prevUnit, newUnit))); } catch (NumberFormatException nfe) { return; } } }); add(makeHeader("Skew", "transformskew16")); JPanel skewPanel = new JPanel(new GridBagLayout()); addRow(skewPanel, 0, new JLabel("Horizontal:"), skewHorizontalTextField, skewUnitComboBox); addRow(skewPanel, 1, new JLabel("Vertical:"), skewVerticalTextField); addJoinedRow(skewPanel, 2, makeClearApplyPanel(this::applySkewActionPerformed, this::clearSkewActionPerformed), 3); add(skewPanel); skewUnitComboBox.addItem(Unit.PX); skewUnitComboBox.addItem(Unit.TWIP); skewUnitComboBox.addItem(Unit.TURN); skewUnitComboBox.addItem(Unit.DEG); skewUnitComboBox.addItem(Unit.RAD); skewUnitComboBox.addItem(Unit.GRAD); skewUnitComboBox.setSelectedItem(Unit.DEG); addUnitChangeListener(skewUnitComboBox, new UnitChangedListener() { @Override public void unitChanged(Unit prevUnit, Unit newUnit) { try { double skewHorizontal = Double.parseDouble(skewHorizontalTextField.getText()); double skewVertical = Double.parseDouble(skewVerticalTextField.getText()); skewHorizontalTextField.setText(formatDouble(convertUnit(skewHorizontal, prevUnit, newUnit))); skewVerticalTextField.setText(formatDouble(convertUnit(skewVertical, prevUnit, newUnit))); } catch (NumberFormatException nfe) { return; } } }); add(makeHeader("Matrix", "transformmatrix16")); JPanel matrixPanel = new JPanel(new GridBagLayout()); addRow(matrixPanel, 0, new JLabel("A"), matrixATextField, new JLabel("C"), matrixCTextField, new JLabel("E"), matrixETextField); addRow(matrixPanel, 1, new JLabel("B"), matrixBTextField, new JLabel("D"), matrixDTextField, new JLabel("F"), matrixFTextField); addJoinedRow(matrixPanel, 2, matrixEditCurrentCheckBox, 6); addJoinedRow(matrixPanel, 3, makeClearApplyPanel(this::applyMatrixActionPerformed, this::clearMatrixActionPerformed), 6); add(matrixPanel); matrixEditCurrentCheckBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (matrixEditCurrentCheckBox.isSelected()) { Matrix matrix = imagePanel.getNewMatrix(); matrixATextField.setText(formatDouble(matrix.scaleX)); matrixBTextField.setText(formatDouble(matrix.rotateSkew0)); matrixCTextField.setText(formatDouble(matrix.rotateSkew1)); matrixDTextField.setText(formatDouble(matrix.scaleY)); matrixETextField.setText(formatDouble(matrix.translateX)); matrixFTextField.setText(formatDouble(matrix.translateY)); } else { matrixATextField.setText(formatDouble(1)); matrixBTextField.setText(formatDouble(0)); matrixCTextField.setText(formatDouble(0)); matrixDTextField.setText(formatDouble(1)); matrixETextField.setText(formatDouble(0)); matrixFTextField.setText(formatDouble(0)); } } }); } public void load() { clearMoveActionPerformed(null); clearScaleActionPerformed(null); clearRotateActionPerformed(null); clearSkewActionPerformed(null); clearMatrixActionPerformed(null); } private void update(Rectangle2D bounds, Point2D registraionPoint) { this.bounds = bounds; this.registrationPoint = registraionPoint; if (!moveRelativeCheckBox.isSelected()) { moveHorizontalTextField.setText(formatDouble(convertUnit(bounds.getX(), Unit.TWIP, (Unit) moveUnitComboBox.getSelectedItem()))); moveVerticalTextField.setText(formatDouble(convertUnit(bounds.getY(), Unit.TWIP, (Unit) moveUnitComboBox.getSelectedItem()))); } if (scaleProportionallyCheckBox.isSelected() && scaleUnitComboBox.getSelectedItem() != Unit.PERCENT) { try { double ratio = bounds.getHeight() / bounds.getWidth(); double scaleWidth = Double.parseDouble(scaleWidthTextField.getText()); double scaleHeight = ratio * scaleWidth; scaleHeightTextField.setText(formatDouble(scaleHeight)); } catch (NumberFormatException nfe) { } } if (matrixEditCurrentCheckBox.isSelected()) { Matrix matrix = imagePanel.getNewMatrix(); matrixATextField.setText(formatDouble(matrix.scaleX)); matrixBTextField.setText(formatDouble(matrix.rotateSkew0)); matrixCTextField.setText(formatDouble(matrix.rotateSkew1)); matrixDTextField.setText(formatDouble(matrix.scaleY)); matrixETextField.setText(formatDouble(matrix.translateX)); matrixFTextField.setText(formatDouble(matrix.translateY)); } } private void clearMoveActionPerformed(ActionEvent e) { moveHorizontalTextField.setText(formatDouble(convertUnit(bounds.getX(), Unit.TWIP, Unit.PX))); moveVerticalTextField.setText(formatDouble(convertUnit(bounds.getY(), Unit.TWIP, Unit.PX))); moveUnitComboBox.setSelectedItem(Unit.PX); moveRelativeCheckBox.setSelected(false); } private void applyMoveActionPerformed(ActionEvent e) { Matrix matrix = new Matrix(); try { double moveHorizontal = convertUnit(Double.parseDouble(moveHorizontalTextField.getText()), (Unit) moveUnitComboBox.getSelectedItem(), Unit.TWIP); double moveVertical = convertUnit(Double.parseDouble(moveVerticalTextField.getText()), (Unit) moveUnitComboBox.getSelectedItem(), Unit.TWIP); if (!moveRelativeCheckBox.isSelected()) { matrix.translate(-bounds.getX(), -bounds.getY()); } matrix.translate(moveHorizontal, moveVertical); imagePanel.applyTransformMatrix(matrix); } catch (NumberFormatException nfe) { } } private void clearScaleActionPerformed(ActionEvent e) { scaleUnitComboBox.setSelectedItem(Unit.PERCENT); scaleWidthTextField.setText(formatDouble(100)); scaleHeightTextField.setText(formatDouble(100)); scaleProportionallyCheckBox.setSelected(true); } private void applyScaleActionPerformed(ActionEvent e) { try { double scaleWidth = Double.parseDouble(scaleWidthTextField.getText()); double scaleHeight = Double.parseDouble(scaleHeightTextField.getText()); Unit scaleUnit = (Unit) scaleUnitComboBox.getSelectedItem(); double scaleWidthFactor; double scaleHeightFactor; if (scaleUnit == Unit.PERCENT) { scaleWidthFactor = scaleWidth / 100.0; scaleHeightFactor = scaleHeight / 100.0; } else { scaleWidthFactor = convertUnit(scaleWidth, scaleUnit, Unit.TWIP) / bounds.getWidth(); scaleHeightFactor = convertUnit(scaleHeight, scaleUnit, Unit.TWIP) / bounds.getHeight(); } Matrix matrix = new Matrix(); matrix.translate(registrationPoint.getX(), registrationPoint.getY()); matrix.scale(scaleWidthFactor, scaleHeightFactor); matrix.translate(-registrationPoint.getX(), -registrationPoint.getY()); imagePanel.applyTransformMatrix(matrix); } catch (NumberFormatException nfe) { } } private void clearRotateActionPerformed(ActionEvent e) { rotateTextField.setText(formatDouble(0)); rotateUnitComboBox.setSelectedItem(Unit.TURN); rotateClockwiseToggleButton.setSelected(true); } private void applyRotateActionPerformed(ActionEvent e) { try { double rotate = Double.parseDouble(rotateTextField.getText()); double rotateRad = (rotateAntiClockwiseToggleButton.isSelected() ? -1.0 : 1.0) * convertUnit(rotate, (Unit) rotateUnitComboBox.getSelectedItem(), Unit.RAD); Matrix matrix = new Matrix(AffineTransform.getRotateInstance(rotateRad, registrationPoint.getX(), registrationPoint.getY())); imagePanel.applyTransformMatrix(matrix); } catch (NumberFormatException nfe) { } } private void clearSkewActionPerformed(ActionEvent e) { skewHorizontalTextField.setText(formatDouble(0)); skewVerticalTextField.setText(formatDouble(0)); skewUnitComboBox.setSelectedItem(Unit.DEG); } private void applySkewActionPerformed(ActionEvent e) { try { Unit skewUnit = (Unit) skewUnitComboBox.getSelectedItem(); double skewHorizontal = Double.parseDouble(skewHorizontalTextField.getText()); double skewVertical = Double.parseDouble(skewVerticalTextField.getText()); double skewHorizontalTwip; double skewVerticalTwip; if (skewUnit.getKind() == UnitKind.ANGLE) { double skewHorizontalRad = convertUnit(skewHorizontal, skewUnit, Unit.RAD); skewHorizontalTwip = bounds.getHeight() * Math.tan(skewHorizontalRad); double skewVerticalRad = convertUnit(skewVertical, skewUnit, Unit.RAD); skewVerticalTwip = bounds.getWidth() * Math.tan(skewVerticalRad); } else { skewHorizontalTwip = convertUnit(skewHorizontal, skewUnit, Unit.TWIP); skewVerticalTwip = convertUnit(skewVertical, skewUnit, Unit.TWIP); } AffineTransform trans = new AffineTransform(); trans.translate(registrationPoint.getX(), registrationPoint.getY()); trans.shear(skewHorizontalTwip / bounds.getWidth(), skewVerticalTwip / bounds.getHeight()); trans.translate(-registrationPoint.getX(), -registrationPoint.getY()); Matrix matrix = new Matrix(trans); imagePanel.applyTransformMatrix(matrix); } catch (NumberFormatException nfe) { } } private void clearMatrixActionPerformed(ActionEvent e) { matrixATextField.setText(formatDouble(1)); matrixBTextField.setText(formatDouble(0)); matrixCTextField.setText(formatDouble(0)); matrixDTextField.setText(formatDouble(1)); matrixETextField.setText(formatDouble(0)); matrixFTextField.setText(formatDouble(0)); matrixEditCurrentCheckBox.setSelected(false); } private void applyMatrixActionPerformed(ActionEvent e) { try { Matrix matrix = new Matrix(); matrix.scaleX = Double.parseDouble(matrixATextField.getText()); matrix.rotateSkew0 = Double.parseDouble(matrixBTextField.getText()); matrix.rotateSkew1 = Double.parseDouble(matrixCTextField.getText()); matrix.scaleY = Double.parseDouble(matrixDTextField.getText()); matrix.translateX = Double.parseDouble(matrixETextField.getText()); matrix.translateY = Double.parseDouble(matrixFTextField.getText()); if (matrixEditCurrentCheckBox.isSelected()) { matrix = imagePanel.getNewMatrix().inverse().concatenate(matrix); } imagePanel.applyTransformMatrix(matrix); } catch (NumberFormatException nfe) { } } private void registrationPointChangedActionPerformed(ActionEvent e) { RegistrationPointPosition position = registrationPointPanel.getSelectedPosition(); Point2D newRegistrationPoint = new Point2D.Double( bounds.getX() + bounds.getWidth() * position.getPositionX(), bounds.getY() + bounds.getHeight() * position.getPositionY() ); imagePanel.setRegistrationPoint(newRegistrationPoint); } private void addJoinedRow(JPanel panel, int rownum, Component comp, int numCols) { GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.insets = new Insets(2, 2, 2, 2); c.gridx = 0; c.gridy = rownum; c.gridwidth = numCols; panel.add(comp, c); } private void addRow(JPanel panel, int rownum, Component... comp) { GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.insets = new Insets(2, 2, 2, 2); for (int i = 0; i < comp.length; i++) { c.gridx = i; c.gridy = rownum; panel.add(comp[i], c); } } private JPanel makeHeader(String title, String icon) { JPanel headerPanel = new JPanel(new FlowLayout()); JLabel label = new JLabel(title); if (icon != null) { label.setIcon(View.getIcon(icon)); } headerPanel.add(label); headerPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); return headerPanel; } private JPanel makeClearApplyPanel(ActionListener onApply, ActionListener onClear) { JPanel buttonsPanel = new JPanel(new FlowLayout()); JButton clearButton = new JButton("Clear"); clearButton.addActionListener(onClear); JButton applyButton = new JButton("Apply"); applyButton.addActionListener(onApply); buttonsPanel.add(clearButton); buttonsPanel.add(applyButton); return buttonsPanel; } private void addUnitChangeListener(JComboBox unitComboBox, UnitChangedListener listener) { final Reference previousValue = new Reference<>((Unit) unitComboBox.getSelectedItem()); unitComboBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { Unit newValue = (Unit) e.getItem(); listener.unitChanged(previousValue.getVal(), newValue); previousValue.setVal(newValue); } } }); } interface UnitChangedListener { public void unitChanged(Unit prevUnit, Unit newUnit); } private static double convertUnit(double value, Unit sourceUnit, Unit targetUnit) { if (sourceUnit == targetUnit) { return value; } if (sourceUnit.kind != targetUnit.kind) { return value; //no conversion } //to px return (value / sourceUnit.value) * targetUnit.value; } private static String formatDouble(double value) { DecimalFormat df = new DecimalFormat("0.000"); return df.format(value); } public static void main(String[] args) { System.out.println("20 twip to px =" + convertUnit(20, Unit.TWIP, Unit.PX)); System.out.println("1 turn to deg =" + convertUnit(1, Unit.TURN, Unit.DEG)); System.out.println("1 deg to rad =" + convertUnit(1, Unit.DEG, Unit.RAD)); System.out.println("1 deg to grad =" + convertUnit(1, Unit.DEG, Unit.GRAD)); } } class RegistrationPointPanel extends JPanel { private Rectangle[][] rects = new Rectangle[3][3]; private RegistrationPointPosition[][] positions = new RegistrationPointPosition[][] { /*x = LEFT */ {RegistrationPointPosition.TOP_LEFT, RegistrationPointPosition.LEFT, RegistrationPointPosition.BOTTOM_LEFT}, /*x = CENTER*/ {RegistrationPointPosition.TOP, RegistrationPointPosition.CENTER, RegistrationPointPosition.BOTTOM}, /*x = RIGHT*/ {RegistrationPointPosition.TOP_RIGHT, RegistrationPointPosition.RIGHT, RegistrationPointPosition.BOTTOM_RIGHT}, }; private RegistrationPointPosition selectedPosition = RegistrationPointPosition.CENTER; final int RECT_SIZE = 10; final int SPACE = 4; private final Cursor DEFAULT_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR); private final Cursor HAND_CURSOR = new Cursor(Cursor.HAND_CURSOR); private ActionListener listener; public RegistrationPointPosition getSelectedPosition() { return selectedPosition; } public RegistrationPointPanel(ActionListener listener) { this.listener = listener; for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { rects[x][y] = new Rectangle(); rects[x][y].x = x * (RECT_SIZE + SPACE); rects[x][y].y = y * (RECT_SIZE + SPACE); rects[x][y].width = RECT_SIZE; rects[x][y].height = RECT_SIZE; } } MouseAdapter adapter = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { if (rects[x][y].contains(e.getPoint())) { selectedPosition = positions[x][y]; repaint(); listener.actionPerformed(new ActionEvent(RegistrationPointPanel.this,0,"")); return; } } } } } @Override public void mouseMoved(MouseEvent e) { for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { if (rects[x][y].contains(e.getPoint())) { if (getCursor() != HAND_CURSOR) { setCursor(HAND_CURSOR); } return; } } } if (getCursor() != DEFAULT_CURSOR) { setCursor(DEFAULT_CURSOR); } } }; addMouseListener(adapter); addMouseMotionListener(adapter); } @Override public Dimension getPreferredSize() { return new Dimension(3 * RECT_SIZE + 2 * SPACE + 1, 3 * RECT_SIZE + 2 * SPACE + 1); } @Override public Dimension getMinimumSize() { return getPreferredSize(); } @Override protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D)g; g2d.setPaint(getBackground()); g2d.fillRect(0, 0, getWidth(), getHeight()); g2d.setStroke(new BasicStroke(1)); g2d.setPaint(getForeground()); GeneralPath path = new GeneralPath(); for (int y = 0; y < 3; y++) { path.moveTo(rects[0][y].getCenterX(), rects[0][y].getCenterY()); path.lineTo(rects[2][y].getCenterX(), rects[2][y].getCenterY()); } for (int x = 0; x < 3; x++) { path.moveTo(rects[x][0].getCenterX(), rects[x][0].getCenterY()); path.lineTo(rects[x][2].getCenterX(), rects[x][2].getCenterY()); } g2d.draw(path); for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { if (positions[x][y] == selectedPosition) { g2d.setPaint(getForeground()); } else { g2d.setPaint(getBackground()); } g2d.fill(rects[x][y]); g2d.setPaint(getForeground()); g2d.draw(rects[x][y]); } } } }