freerouting/src/main/java/eu/mihosoft/freerouting/boardgraphics/CoordinateTransform.java

299 lines
10 KiB
Java

/*
* Copyright (C) 2014 Alfons Wirtz
* website www.freerouting.net
*
* 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 at <http://www.gnu.org/licenses/>
* for more details.
*/
package eu.mihosoft.freerouting.boardgraphics;
import eu.mihosoft.freerouting.geometry.planar.FloatPoint;
import eu.mihosoft.freerouting.geometry.planar.IntBox;
import eu.mihosoft.freerouting.geometry.planar.Limits;
import java.awt.Dimension;
import java.awt.geom.Point2D;
/**
* Transformation function between the eu.mihosoft.freerouting.board and the screen coordinate systems.
*
* @author Alfons Wirtz
*/
public class CoordinateTransform implements java.io.Serializable
{
public CoordinateTransform(IntBox p_design_box, Dimension p_panel_bounds )
{
this.screen_bounds = p_panel_bounds;
this.design_box = p_design_box;
this.rotation_pole = p_design_box.centre_of_gravity();
int min_ll = Math.min(p_design_box.ll.x, p_design_box.ll.y);
int max_ur = Math.max(p_design_box.ur.x, p_design_box.ur.y);
if (Math.max(Math.abs(min_ll), Math.abs(max_ur)) <= 0.3 * Limits.CRIT_INT)
{
// create an offset to p_design_box to enable deep zoom out
double design_offset = Math.max(p_design_box.width(), p_design_box.height());
design_box_with_offset = p_design_box.offset(design_offset);
}
else
{
// no offset because of danger of integer overflow
design_box_with_offset = p_design_box;
}
double x_scale_factor = screen_bounds.getWidth()/design_box_with_offset.width();
double y_scale_factor = screen_bounds.getHeight()/design_box_with_offset.height();
scale_factor = Math.min(x_scale_factor, y_scale_factor) ;
display_x_offset = scale_factor * design_box_with_offset.ll.x;
display_y_offset = scale_factor * design_box_with_offset.ll.y ;
}
/** Copy constructor */
public CoordinateTransform(CoordinateTransform p_coordinate_transform)
{
this.screen_bounds = new Dimension(p_coordinate_transform.screen_bounds);
this.design_box = new IntBox(p_coordinate_transform.design_box.ll, p_coordinate_transform.design_box.ur);
this.rotation_pole = new FloatPoint(p_coordinate_transform.rotation_pole.x, p_coordinate_transform.rotation_pole.y);
this.design_box_with_offset = new IntBox(p_coordinate_transform.design_box_with_offset.ll, p_coordinate_transform.design_box_with_offset.ur);
this.scale_factor = p_coordinate_transform.scale_factor;
this.display_x_offset = p_coordinate_transform.display_x_offset;
this.display_y_offset = p_coordinate_transform.display_y_offset;
this.mirror_left_right = p_coordinate_transform.mirror_left_right;
this.mirror_top_bottom = p_coordinate_transform.mirror_top_bottom;
this.rotation = p_coordinate_transform.rotation;
}
/**
* scale a value from the eu.mihosoft.freerouting.board to the screen coordinate system
*/
public double board_to_screen(double p_val)
{
return p_val * scale_factor ;
}
/**
* scale a value the screen to the eu.mihosoft.freerouting.board coordinate system
*/
public double screen_to_board(double p_val)
{
return p_val / scale_factor;
}
/**
* transform a eu.mihosoft.freerouting.geometry.planar.FloatPoint to a java.awt.geom.Point2D
*/
public Point2D board_to_screen(FloatPoint p_point)
{
FloatPoint rotated_point = p_point.rotate(this.rotation, this.rotation_pole);
double x, y;
if (this.mirror_left_right)
{
x = (design_box_with_offset.width() - rotated_point.x - 1) * scale_factor + display_x_offset;
}
else
{
x = rotated_point.x * scale_factor - display_x_offset;
}
if (this.mirror_top_bottom)
{
y = (design_box_with_offset.height() - rotated_point.y - 1) * scale_factor + display_y_offset;
}
else
{
y = rotated_point.y * scale_factor - display_y_offset;
}
return new Point2D.Double(x, y);
}
/**
* Transform a java.awt.geom.Point2D to a eu.mihosoft.freerouting.geometry.planar.FloatPoint
*/
public FloatPoint screen_to_board(Point2D p_point)
{
double x, y;
if (this.mirror_left_right)
{
x = design_box_with_offset.width() -(p_point.getX() - display_x_offset) / scale_factor - 1;
}
else
{
x = (p_point.getX() + display_x_offset)/ scale_factor;
}
if (this.mirror_top_bottom)
{
y = design_box_with_offset.height() -(p_point.getY() - display_y_offset) / scale_factor - 1;
}
else
{
y = (p_point.getY() + display_y_offset)/ scale_factor;
}
FloatPoint result = new FloatPoint(x, y);
return result.rotate(-this.rotation, this.rotation_pole);
}
/**
* Transforms an angle in radian on the eu.mihosoft.freerouting.board to an angle on the screen.
*/
public double board_to_screen_angle(double p_angle)
{
double result = p_angle + this.rotation;
if (this.mirror_left_right)
{
result = Math.PI - result;
}
if (this.mirror_top_bottom)
{
result = -result;
}
while (result >= 2 * Math.PI)
{
result -= 2 * Math.PI;
}
while (result < 0)
{
result += 2 * Math.PI;
}
return result;
}
/**
* Transform a eu.mihosoft.freerouting.geometry.planar.IntBox to a java.awt.Rectangle
* If the internal rotation is not a multiple of Pi/2, a bounding rectangle of the
* rotated rectangular shape is returned.
*/
public java.awt.Rectangle board_to_screen(IntBox p_box)
{
Point2D corner_1 = board_to_screen(p_box.ll.to_float());
Point2D corner_2 = board_to_screen(p_box.ur.to_float());
double ll_x = Math.min(corner_1.getX(), corner_2.getX());
double ll_y = Math.min(corner_1.getY(), corner_2.getY());
double dx = Math.abs(corner_2.getX() - corner_1.getX());
double dy = Math.abs(corner_2.getY() - corner_1.getY());
java.awt.Rectangle result =
new java.awt. Rectangle((int) Math.floor(ll_x), (int) Math.floor(ll_y),
(int) Math.ceil(dx), (int) Math.ceil(dy));
return result;
}
/**
* Transform a java.awt.Rectangle to a eu.mihosoft.freerouting.geometry.planar.IntBox
* If the internal rotation is not a multiple of Pi/2, a bounding box of the
* rotated rectangular shape is returned.
*/
public IntBox screen_to_board(java.awt.Rectangle p_rect)
{
FloatPoint corner_1 = screen_to_board(new Point2D.Double(p_rect.getX(), p_rect.getY()));
FloatPoint corner_2 = screen_to_board(new Point2D.Double(p_rect.getX() + p_rect.getWidth(),
p_rect.getY() + p_rect.getHeight()));
int llx = (int) Math.floor(Math.min(corner_1.x, corner_2.x));
int lly = (int) Math.floor(Math.min(corner_1.y, corner_2.y));
int urx = (int) Math.ceil(Math.max(corner_1.x, corner_2.x));
int ury = (int) Math.ceil(Math.max(corner_1.y, corner_2.y));
return new IntBox(llx, lly, urx, ury);
}
/**
* If p_value is true, the left side and the right side of the eu.mihosoft.freerouting.board will be swapped.
*/
public void set_mirror_left_right(boolean p_value)
{
mirror_left_right = p_value;
}
/**
* Returns, if the left side and the right side of the eu.mihosoft.freerouting.board are swapped.
*/
public boolean is_mirror_left_right()
{
return mirror_left_right;
}
/**
* If p_value is true, the top side and the botton side of the eu.mihosoft.freerouting.board will be swapped.
*/
public void set_mirror_top_bottom(boolean p_value)
{
// Because the origin of display is the upper left corner, the internal value
// will be opposite to the input value of this function.
mirror_top_bottom = !p_value;
}
/**
* Returns, if the top side and the botton side of the eu.mihosoft.freerouting.board are swapped.
*/
public boolean is_mirror_top_bottom()
{
// Because the origin of display is the upper left corner, the internal value
// is opposite to the result of this function.
return !mirror_top_bottom;
}
/**
* Sets the rotation of the displayed eu.mihosoft.freerouting.board to p_value.
*/
public void set_rotation(double p_value)
{
rotation = p_value;
}
/**
* Returns the rotation of the displayed eu.mihosoft.freerouting.board.
*/
public double get_rotation()
{
return rotation;
}
/**
* Returns the internal rotation snapped to the nearest multiple of 90 degree.
* The result will be 0, 1, 2 or 3.
*/
public int get_90_degree_rotation()
{
int multiple = (int) Math.round(Math.toDegrees(rotation) / 90.0);
while (multiple < 0)
{
multiple += 4;
}
while (multiple >= 4)
{
multiple -= 4;
}
return multiple;
}
final IntBox design_box;
final IntBox design_box_with_offset ;
final Dimension screen_bounds ;
private final double scale_factor ;
private final double display_x_offset;
private final double display_y_offset;
/**
* Left side and right side of the eu.mihosoft.freerouting.board are swapped.
*/
private boolean mirror_left_right = false;
/**
* Top side and bottom side of the eu.mihosoft.freerouting.board are swapped.
*/
private boolean mirror_top_bottom = true;
private double rotation = 0;
private FloatPoint rotation_pole;
}