Commit 345b351b authored by Piotr Gawron's avatar Piotr Gawron
Browse files

when transforming lines create defensive copy of lines

parent affb89d3
......@@ -4,13 +4,14 @@ import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.common.geometry.LineTransformation;
import lcsb.mapviewer.common.geometry.PointTransformation;
import lcsb.mapviewer.model.graphics.PolylineData;
import org.apache.logging.log4j.*;
/**
* Class with basic operations on lines with special functions for CellDesigner
* format.
......@@ -20,116 +21,118 @@ import org.apache.logging.log4j.*;
*/
public class CellDesignerLineTransformation extends LineTransformation {
/**
* Default class logger.
*/
@SuppressWarnings("unused")
private static Logger logger = LogManager.getLogger(CellDesignerLineTransformation.class.getName());
/**
* Class used for transformations on {@link Point2D} class.
*/
private static PointTransformation pt = new PointTransformation();
/**
* Transform data in CellDesigner format into typical x,y coordinates for all
* points.
*
* CellDesigner format stores coordinates of a line in a different base. In
* this base we have two points that define a base: vector between these
* points is 1 unit in first coordinate, and orthogonal vector to the one
* mentioned before is 1 unit in the second coordinate. Moreover, these two
* points defining base belong to this line.
*
*
* @param startPoint
* first point defining CellDesigner base
* @param endPoint
* second point defining CellDesigner base
* @param midPoints
* points defining line
* @return line in standard x,y format
*/
public List<Point2D> getLinePointsFromPoints(final Point2D startPoint, final Point2D endPoint, final List<Point2D> midPoints) {
if (!pt.isValidPoint(startPoint)) {
throw new InvalidArgumentException("Invalid start point: " + startPoint);
}
if (!pt.isValidPoint(endPoint)) {
throw new InvalidArgumentException("Invalid end point: " + endPoint);
}
List<Point2D> result = new ArrayList<Point2D>();
double dx1 = endPoint.getX() - startPoint.getX();
double dy1 = endPoint.getY() - startPoint.getY();
double dx2 = -dy1;
double dy2 = dx1;
result.add(startPoint);
if (midPoints != null) {
for (Point2D p : midPoints) {
if (!pt.isValidPoint(p)) {
throw new InvalidArgumentException("Invalid point: " + p);
}
double x = startPoint.getX() + dx1 * p.getX() + dx2 * p.getY();
double y = startPoint.getY() + dy1 * p.getX() + dy2 * p.getY();
result.add(new Point2D.Double(x, y));
}
}
result.add(endPoint);
return result;
}
/**
* Transform line of typical xy coordinates into coordinates used by
* celldesigner.
*
* CellDesigner format stores coordinates of a line in a different base. In
* this base we have two points that define a base: vector between these
* points is 1 unit in first coordinate, and orthogonal vector to the one
* mentioned before is 1 unit in the second coordinate. These two points are
* the first and the last.
*
* @param line
* line to be transformed into CellDesigner format
* @return points of the line in CellDesigner format (without the first and
* the last)
*/
public List<Point2D> getPointsFromLine(final PolylineData line) {
if (line == null) {
throw new InvalidArgumentException("Line cannot be null.");
}
if (line.getPoints().size() < 2) {
throw new InvalidArgumentException("Invalid line passed as an argument.");
}
double ax = line.getBeginPoint().getX();
double ay = line.getBeginPoint().getY();
double bx = line.getEndPoint().getX();
double by = line.getEndPoint().getY();
double dx = bx - ax;
double dy = by - ay;
List<Point2D> result = new ArrayList<Point2D>();
if (dy * dy + dx * dx == 0) {
throw new InvalidArgumentException("First and last point in the line must differ but found: " + line.getBeginPoint() + ", " + line.getEndPoint());
}
for (int i = 1; i < line.getPoints().size() - 1; i++) {
double ox = line.getPoints().get(i).getX();
double oy = line.getPoints().get(i).getY();
double py = (dy * (ax - ox) + dx * (oy - ay)) / (dy * dy + dx * dx);
double px = (dx * (ox - ax) + dy * (oy - ay)) / (dx * dx + dy * dy);
result.add(new Point2D.Double(px, py));
}
return result;
}
/**
* Default class logger.
*/
@SuppressWarnings("unused")
private static Logger logger = LogManager.getLogger(CellDesignerLineTransformation.class.getName());
/**
* Class used for transformations on {@link Point2D} class.
*/
private static PointTransformation pt = new PointTransformation();
/**
* Transform data in CellDesigner format into typical x,y coordinates for all
* points.
*
* CellDesigner format stores coordinates of a line in a different base. In this
* base we have two points that define a base: vector between these points is 1
* unit in first coordinate, and orthogonal vector to the one mentioned before
* is 1 unit in the second coordinate. Moreover, these two points defining base
* belong to this line.
*
*
* @param startPoint
* first point defining CellDesigner base
* @param endPoint
* second point defining CellDesigner base
* @param midPoints
* points defining line
* @return line in standard x,y format
*/
public List<Point2D> getLinePointsFromPoints(final Point2D startPoint, final Point2D endPoint,
final List<Point2D> midPoints) {
if (!pt.isValidPoint(startPoint)) {
throw new InvalidArgumentException("Invalid start point: " + startPoint);
}
if (!pt.isValidPoint(endPoint)) {
throw new InvalidArgumentException("Invalid end point: " + endPoint);
}
List<Point2D> result = new ArrayList<>();
double dx1 = endPoint.getX() - startPoint.getX();
double dy1 = endPoint.getY() - startPoint.getY();
double dx2 = -dy1;
double dy2 = dx1;
result.add(new Point2D.Double(startPoint.getX(), startPoint.getY()));
if (midPoints != null) {
for (Point2D p : midPoints) {
if (!pt.isValidPoint(p)) {
throw new InvalidArgumentException("Invalid point: " + p);
}
double x = startPoint.getX() + dx1 * p.getX() + dx2 * p.getY();
double y = startPoint.getY() + dy1 * p.getX() + dy2 * p.getY();
result.add(new Point2D.Double(x, y));
}
}
result.add(new Point2D.Double(endPoint.getX(), endPoint.getY()));
return result;
}
/**
* Transform line of typical xy coordinates into coordinates used by
* celldesigner.
*
* CellDesigner format stores coordinates of a line in a different base. In this
* base we have two points that define a base: vector between these points is 1
* unit in first coordinate, and orthogonal vector to the one mentioned before
* is 1 unit in the second coordinate. These two points are the first and the
* last.
*
* @param line
* line to be transformed into CellDesigner format
* @return points of the line in CellDesigner format (without the first and the
* last)
*/
public List<Point2D> getPointsFromLine(final PolylineData line) {
if (line == null) {
throw new InvalidArgumentException("Line cannot be null.");
}
if (line.getPoints().size() < 2) {
throw new InvalidArgumentException("Invalid line passed as an argument.");
}
double ax = line.getBeginPoint().getX();
double ay = line.getBeginPoint().getY();
double bx = line.getEndPoint().getX();
double by = line.getEndPoint().getY();
double dx = bx - ax;
double dy = by - ay;
List<Point2D> result = new ArrayList<Point2D>();
if (dy * dy + dx * dx == 0) {
throw new InvalidArgumentException("First and last point in the line must differ but found: "
+ line.getBeginPoint() + ", " + line.getEndPoint());
}
for (int i = 1; i < line.getPoints().size() - 1; i++) {
double ox = line.getPoints().get(i).getX();
double oy = line.getPoints().get(i).getY();
double py = (dy * (ax - ox) + dx * (oy - ay)) / (dy * dy + dx * dx);
double px = (dx * (ox - ax) + dy * (oy - ay)) / (dx * dx + dy * dy);
result.add(new Point2D.Double(px, py));
}
return result;
}
}
package lcsb.mapviewer.converter.model.celldesigner.geometry.helper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
......@@ -9,7 +10,8 @@ import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
......@@ -19,200 +21,256 @@ import lcsb.mapviewer.common.geometry.PointTransformation;
import lcsb.mapviewer.model.graphics.PolylineData;
public class CellDesignerLineTransformationTest {
Logger logger = LogManager.getLogger(CellDesignerLineTransformationTest.class);
CellDesignerLineTransformation lineTransformation = new CellDesignerLineTransformation();
PointTransformation pointTransformation = new PointTransformation();
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void testGetPointsFromLine() {
try {
Point2D startPoint = new Point2D.Double(1, 7);
Point2D endPoint = new Point2D.Double(3, 5);
List<Point2D> points = new ArrayList<Point2D>();
points.add(new Point2D.Double(1.5, 2.5));
points.add(new Point2D.Double(12, 13));
points.add(new Point2D.Double(30, 40));
PolylineData line = new PolylineData(lineTransformation.getLinePointsFromPoints(startPoint, endPoint, points));
List<Point2D> newPoints = lineTransformation.getPointsFromLine(line);
assertNotNull(newPoints);
assertEquals(points.size(), newPoints.size());
for (int i = 0; i < points.size(); i++) {
double dist = points.get(i).distance(newPoints.get(i));
assertTrue("Distance to big: " + dist + " for points " + points.get(i) + " and " + newPoints.get(i), dist < 1e-6);
assertTrue(pointTransformation.isValidPoint(newPoints.get(i)));
}
} catch (Exception e) {
e.printStackTrace();
fail("Exception occured: " + e.getMessage());
}
}
@Test
public void testGetProblematicPointsFromLine() {
try {
Point2D startPoint = new Point2D.Double(1, 7);
Point2D endPoint = new Point2D.Double(1, 5);
List<Point2D> points = new ArrayList<Point2D>();
points.add(new Point2D.Double(1.5, 2.5));
points.add(new Point2D.Double(12, 13));
points.add(new Point2D.Double(30, 40));
PolylineData line = new PolylineData(lineTransformation.getLinePointsFromPoints(startPoint, endPoint, points));
List<Point2D> newPoints = lineTransformation.getPointsFromLine(line);
assertNotNull(newPoints);
assertEquals(points.size(), newPoints.size());
for (int i = 0; i < points.size(); i++) {
assertTrue("Invalid point after transformation: " + newPoints.get(i), pointTransformation.isValidPoint(newPoints.get(i)));
double dist = points.get(i).distance(newPoints.get(i));
assertTrue("Distance to big: " + dist + " for points " + points.get(i) + " and " + newPoints.get(i), dist < 1e-6);
assertTrue(pointTransformation.isValidPoint(newPoints.get(i)));
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetProblematicPointsFromLine2() {
try {
Point2D startPoint = new Point2D.Double(5, 1);
Point2D endPoint = new Point2D.Double(70, 1);
List<Point2D> points = new ArrayList<Point2D>();
points.add(new Point2D.Double(1.5, 2.5));
points.add(new Point2D.Double(12, 13));
points.add(new Point2D.Double(-12, -13));
points.add(new Point2D.Double(30, 40));
PolylineData line = new PolylineData(lineTransformation.getLinePointsFromPoints(startPoint, endPoint, points));
List<Point2D> newPoints = lineTransformation.getPointsFromLine(line);
assertNotNull(newPoints);
assertEquals(points.size(), newPoints.size());
for (int i = 0; i < points.size(); i++) {
assertTrue("Invalid point after transformation: " + newPoints.get(i), pointTransformation.isValidPoint(newPoints.get(i)));
double dist = points.get(i).distance(newPoints.get(i));
assertTrue("Distance to big: " + dist + " for points " + points.get(i) + " and " + newPoints.get(i), dist < 1e-6);
assertTrue(pointTransformation.isValidPoint(newPoints.get(i)));
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetLinePointsFromInvalidPoints() {
try {
Point2D startPoint = new Point2D.Double(Double.POSITIVE_INFINITY, 1);
Point2D endPoint = new Point2D.Double(70, 1);
lineTransformation.getLinePointsFromPoints(startPoint, endPoint, new ArrayList<>());
fail("Excepiton expceted");
} catch (InvalidArgumentException e) {
assertTrue(e.getMessage().contains("Invalid start point"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetLinePointsFromInvalidPoints2() {
try {
Point2D endPoint = new Point2D.Double(Double.POSITIVE_INFINITY, 1);
Point2D startPoint = new Point2D.Double(70, 1);
lineTransformation.getLinePointsFromPoints(startPoint, endPoint, new ArrayList<>());
fail("Excepiton expceted");
} catch (InvalidArgumentException e) {
assertTrue(e.getMessage().contains("Invalid end point"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetLinePointsFromInvalidPoints3() {
try {
Point2D endPoint = new Point2D.Double(70, 2);
Point2D startPoint = new Point2D.Double(70, 1);
List<Point2D> list = new ArrayList<>();
list.add(new Point2D.Double(Double.POSITIVE_INFINITY, 1));
lineTransformation.getLinePointsFromPoints(startPoint, endPoint, list);
fail("Excepiton expceted");
} catch (InvalidArgumentException e) {
assertTrue(e.getMessage().contains("Invalid point"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetPointsFromInvalidLine() {
try {
lineTransformation.getPointsFromLine(null);
fail("Excepiton expceted");
} catch (InvalidArgumentException e) {
assertTrue(e.getMessage().contains("Line cannot be null"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetPointsFromInvalidLine2() {
try {
lineTransformation.getPointsFromLine(new PolylineData());
fail("Excepiton expceted");
} catch (InvalidArgumentException e) {
assertTrue(e.getMessage().contains("Invalid line passed as an argument"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetPointsFromInvalidLine3() {
try {
lineTransformation.getPointsFromLine(new PolylineData(new Point2D.Double(), new Point2D.Double()));
fail("Excepiton expceted");
} catch (InvalidArgumentException e) {
assertTrue(e.getMessage().contains("First and last point in the line must differ but found"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
Logger logger = LogManager.getLogger(CellDesignerLineTransformationTest.class);
CellDesignerLineTransformation lineTransformation = new CellDesignerLineTransformation();
PointTransformation pointTransformation = new PointTransformation();
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void testGetPointsFromLine() {
try {
Point2D startPoint = new Point2D.Double(1, 7);
Point2D endPoint = new Point2D.Double(3, 5);
List<Point2D> points = new ArrayList<Point2D>();
points.add(new Point2D.Double(1.5, 2.5));
points.add(new Point2D.Double(12, 13));
points.add(new Point2D.Double(30, 40));
PolylineData line = new PolylineData(lineTransformation.getLinePointsFromPoints(startPoint, endPoint, points));
List<Point2D> newPoints = lineTransformation.getPointsFromLine(line);
assertNotNull(newPoints);
assertEquals(points.size(), newPoints.size());
for (int i = 0; i < points.size(); i++) {
double dist = points.get(i).distance(newPoints.get(i));
assertTrue("Distance to big: " + dist + " for points " + points.get(i) + " and " + newPoints.get(i),
dist < 1e-6);
assertTrue(pointTransformation.isValidPoint(newPoints.get(i)));
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetPointsFromLineShouldCreateDefensiveCopy() {
try {
Point2D startPoint = new Point2D.Double(1, 7);
Point2D endPoint = new Point2D.Double(3, 5);
List<Point2D> points = new ArrayList<>();
points.add(new Point2D.Double(1.5, 2.5));
points.add(new Point2D.Double(12, 13));
points.add(new Point2D.Double(30, 40));
PolylineData line = new PolylineData(lineTransformation.getLinePointsFromPoints(startPoint, endPoint, points));
List<Point2D> newPoints = lineTransformation.getPointsFromLine(line);
List<Point2D> newPoints2 = lineTransformation.getPointsFromLine(line);
for (Point2D point : newPoints2) {
for (Point2D point2 : newPoints) {
assertFalse(point == point2);
}
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}