Skip to content

Commit

Permalink
prmr#401 Shape intersection fix
Browse files Browse the repository at this point in the history
  • Loading branch information
yingjie-xu authored and louib committed Aug 15, 2021
1 parent ac5c545 commit 9649676
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 0 deletions.
89 changes: 89 additions & 0 deletions src/ca/mcgill/cs/jetuml/geom/GeomUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,95 @@ public static Point intersectCircle(Rectangle pBounds, Direction pDirection)
return new Point( pBounds.getCenter().getX() + offsetX, pBounds.getCenter().getY() + offsetY);
}

/**
* Returns the point that intersects a ellipse on the line that originates
* at the center of pBounds going in direction pDirection.
*
* @param pBounds The bounds of the rectangle in which the ellipse to intersect in inscribed.
* @param pDirection The direction to follow to intersect the sides of the ellipse.
* @return The point that intersects the ellipse inscribed in pBounds if we draw a line from its
* center going in direction pDirection.
* @pre pBounds != null && pDirection != null
*/
public static Point intersectEllipse(Rectangle pBounds, Direction pDirection)
{
assert pBounds != null && pDirection != null;

if( pDirection.isCardinal() )
{
return intersectionForCardinalDirection(pBounds, pDirection);
}

final int a = pBounds.getWidth()/2;
final int b = pBounds.getHeight()/2;

int offsetX = (int) round(cos(toRadians(pDirection.asAngle() - Direction.EAST.asAngle())) * a);
int offsetY = (int) round(sin(toRadians(pDirection.asAngle() - Direction.EAST.asAngle())) * b);
return new Point( pBounds.getCenter().getX() + offsetX, pBounds.getCenter().getY() + offsetY);
}

/**
*
* @param pBounds The bounds of the rectangle in which the rounded rectangle to intersect in inscribed.
* @param pDirection The direction to follow to intersect the sides of the rounded rectangle.
* @return The point that intersects the rounded rectangle inscribed in pBounds if we draw a line from
* its center going in direction pDirection.
* @pre pBounds != null && pDirection != null
*/
public static Point intersectRoundedRectangle(Rectangle pBounds, Direction pDirection)
{
assert pBounds != null && pDirection != null;

if( pDirection.isCardinal() )
{
return intersectionForCardinalDirection(pBounds, pDirection);
}

final int arcSize = 20; // same as the value in viewUtils
int radius = arcSize/2;
int widthOffset = pBounds.getWidth()/2 - radius;
int heightOffset = pBounds.getHeight()/2 - radius;

// calculate bounds of rounded corner
Direction topNE = Direction.fromLine(pBounds.getCenter(), new Point(pBounds.getMaxX() - radius, pBounds.getY()));
Direction bottomNE = Direction.fromLine(pBounds.getCenter(), new Point(pBounds.getMaxX(), pBounds.getY() + radius));
Direction topSE = Direction.fromLine(pBounds.getCenter(), new Point(pBounds.getMaxX(), pBounds.getMaxY() - radius));
Direction bottomSE = Direction.fromLine(pBounds.getCenter(), new Point(pBounds.getMaxX() - radius, pBounds.getMaxY()));
Direction topSW = topNE.mirrored();
Direction bottomSW = bottomNE.mirrored();
Direction topNW = topSE.mirrored();
Direction bottomNW = bottomSE.mirrored();

if( pDirection.isBetween(topNE, bottomNE))
{
int offsetX = (int) round(cos(toRadians(pDirection.asAngle() - Direction.EAST.asAngle())) * radius);
int offsetY = (int) round(sin(toRadians(pDirection.asAngle() - Direction.EAST.asAngle())) * radius);
return new Point( pBounds.getCenter().getX() + offsetX + widthOffset, pBounds.getCenter().getY() + offsetY - heightOffset);
}
else if( pDirection.isBetween(topSE, bottomSE))
{
int offsetX = (int) round(cos(toRadians(pDirection.asAngle() - Direction.EAST.asAngle())) * radius);
int offsetY = (int) round(sin(toRadians(pDirection.asAngle() - Direction.EAST.asAngle())) * radius);
return new Point( pBounds.getCenter().getX() + offsetX + widthOffset, pBounds.getCenter().getY() + offsetY + heightOffset);
}
else if( pDirection.isBetween(topSW, bottomSW))
{
int offsetX = (int) round(cos(toRadians(pDirection.asAngle() - Direction.EAST.asAngle())) * radius);
int offsetY = (int) round(sin(toRadians(pDirection.asAngle() - Direction.EAST.asAngle())) * radius);
return new Point( pBounds.getCenter().getX() + offsetX - widthOffset, pBounds.getCenter().getY() + offsetY + heightOffset);
}
else if( pDirection.isBetween(topNW, bottomNW))
{
int offsetX = (int) round(cos(toRadians(pDirection.asAngle() - Direction.EAST.asAngle())) * radius);
int offsetY = (int) round(sin(toRadians(pDirection.asAngle() - Direction.EAST.asAngle())) * radius);
return new Point( pBounds.getCenter().getX() + offsetX - widthOffset, pBounds.getCenter().getY() + offsetY - heightOffset);
}
else
{
return intersectRectangle(pBounds, pDirection);
}
}

/*
* returns the length, in pixel, of the opposing side to the angle pAngleInDegrees
* of a right triangle for when the length (in pixels) of the adjacent side is
Expand Down
9 changes: 9 additions & 0 deletions src/ca/mcgill/cs/jetuml/viewers/nodes/StateNodeViewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
import ca.mcgill.cs.jetuml.diagram.Node;
import ca.mcgill.cs.jetuml.diagram.nodes.StateNode;
import ca.mcgill.cs.jetuml.geom.Dimension;
import ca.mcgill.cs.jetuml.geom.Direction;
import ca.mcgill.cs.jetuml.geom.GeomUtils;
import ca.mcgill.cs.jetuml.geom.Point;
import ca.mcgill.cs.jetuml.geom.Rectangle;
import ca.mcgill.cs.jetuml.views.StringViewer;
import ca.mcgill.cs.jetuml.views.ViewUtils;
Expand Down Expand Up @@ -52,4 +55,10 @@ public Rectangle getBounds(Node pNode)
return new Rectangle(pNode.position().getX(), pNode.position().getY(),
Math.max(bounds.width(), DEFAULT_WIDTH), Math.max(bounds.height(), DEFAULT_HEIGHT));
}

@Override
public Point getConnectionPoint(Node pNode, Direction pDirection)
{
return GeomUtils.intersectRoundedRectangle(getBounds(pNode), pDirection);
}
}
9 changes: 9 additions & 0 deletions src/ca/mcgill/cs/jetuml/viewers/nodes/UseCaseNodeViewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@

import ca.mcgill.cs.jetuml.diagram.Node;
import ca.mcgill.cs.jetuml.diagram.nodes.UseCaseNode;
import ca.mcgill.cs.jetuml.geom.Direction;
import ca.mcgill.cs.jetuml.geom.GeomUtils;
import ca.mcgill.cs.jetuml.geom.Point;
import ca.mcgill.cs.jetuml.geom.Rectangle;
import ca.mcgill.cs.jetuml.views.StringViewer;
import ca.mcgill.cs.jetuml.views.ViewUtils;
Expand Down Expand Up @@ -54,4 +57,10 @@ public Rectangle getBounds(Node pNode)
HORIZONTAL_NAME_PADDING),
Math.max(DEFAULT_HEIGHT, NAME_VIEWER.getDimension(((UseCaseNode)pNode).getName()).height()));
}

@Override
public Point getConnectionPoint(Node pNode, Direction pDirection)
{
return GeomUtils.intersectEllipse(getBounds(pNode), pDirection);
}
}
96 changes: 96 additions & 0 deletions test/ca/mcgill/cs/jetuml/geom/TestGeomUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,100 @@ void testIntersectCircle_NW()
{
assertEquals(new Point(2,4), GeomUtils.intersectCircle(aSquare, Direction.fromAngle(305)));
}

@Test
void testIntersectEllipse_North()
{
assertEquals(new Point(30,0), GeomUtils.intersectEllipse(aRectangle, Direction.NORTH));
}

@Test
void testIntersectEllipse_East()
{
assertEquals(new Point(60,20), GeomUtils.intersectEllipse(aRectangle, Direction.EAST));
}

@Test
void tesIntersectEllipse_South()
{
assertEquals(new Point(30,40), GeomUtils.intersectEllipse(aRectangle, Direction.SOUTH));
}

@Test
void testIntersectEllipse_West()
{
assertEquals(new Point(0,20), GeomUtils.intersectEllipse(aRectangle, Direction.WEST));
}

@Test
void testIntersectEllipse_NE()
{
assertEquals(new Point(58,13), GeomUtils.intersectEllipse(aRectangle, Direction.fromAngle(70)));
}

@Test
void testIntersectEllipse_SE()
{
assertEquals(new Point(58,27), GeomUtils.intersectEllipse(aRectangle, Direction.fromAngle(110)));
}

@Test
void testIntersectEllipse_SW()
{
assertEquals(new Point(9,34), GeomUtils.intersectEllipse(aRectangle, Direction.fromAngle(225)));
}

@Test
void testIntersectEllipse_NW()
{
assertEquals(new Point(5,9), GeomUtils.intersectEllipse(aRectangle, Direction.fromAngle(305)));
}

@Test
void testIntersectRoundedRectangle_North()
{
assertEquals(new Point(30,0), GeomUtils.intersectRoundedRectangle(aRectangle, Direction.NORTH));
}

@Test
void testIntersectRoundedRectangle_East()
{
assertEquals(new Point(60,20), GeomUtils.intersectRoundedRectangle(aRectangle, Direction.EAST));
}

@Test
void testIntersectRoundedRectangle_South()
{
assertEquals(new Point(30,40), GeomUtils.intersectRoundedRectangle(aRectangle, Direction.SOUTH));
}

@Test
void testIntersectRoundedRectangle_West()
{
assertEquals(new Point(0,20), GeomUtils.intersectRoundedRectangle(aRectangle, Direction.WEST));
}

@Test
void testIntersectRoundedRectangle_NE()
{
assertEquals(new Point(59,7), GeomUtils.intersectRoundedRectangle(aRectangle, Direction.fromAngle(70)));
}

@Test
void testIntersectRoundedRectangle_SE()
{
assertEquals(new Point(59,33), GeomUtils.intersectRoundedRectangle(aRectangle, Direction.fromAngle(110)));
}

@Test
void testIntersectRoundedRectangle_SW()
{
assertEquals(new Point(3,37), GeomUtils.intersectRoundedRectangle(aRectangle, Direction.fromAngle(225)));
}

@Test
void testIntersectRoundedRectangle_NW()
{
assertEquals(new Point(2,4), GeomUtils.intersectRoundedRectangle(aRectangle, Direction.fromAngle(305)));
}
}

0 comments on commit 9649676

Please sign in to comment.