package com.baselet.element.relation.helper; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import com.baselet.control.SharedUtils; import com.baselet.control.basics.geom.Line; import com.baselet.control.basics.geom.Point; import com.baselet.control.basics.geom.Rectangle; import com.baselet.diagram.draw.DrawHandler; import com.baselet.element.sticking.PointChange; import com.baselet.element.sticking.PointDoubleIndexed; public class RelationPointHandler implements ResizableObject { /** * Points of this relation (point of origin is the upper left corner of the relation element (not the drawpanel!)) */ private RelationPointList points = new RelationPointList(); private final RelationPointHolder relation; public RelationPointHandler(RelationPointHolder relation, RelationPointList points) { super(); this.relation = relation; this.points = points; } public RelationSelection getSelection(Point point) { if (isPointOverDragBox(point)) { return RelationSelection.DRAG_BOX; } else if (RelationPointHandlerUtils.getRelationPointContaining(point, points) != null) { return RelationSelection.RELATION_POINT; } else if (getLineContaining(point) != null) { return RelationSelection.LINE; } else { return RelationSelection.NOTHING; } } private PointDoubleIndexed relationPointOfCurrentDrag = null; /** * this method is basically the same as {@link #getSelection(Point)}, but also applies changes to the relationpoints * (the order of checks is the same, but they do different things, therefore they are separated) */ public RelationSelection getSelectionAndMovePointsIfNecessary(Point point, Integer diffX, Integer diffY, boolean firstDrag) { // Special case: if this is not the first drag and a relation-point is currently dragged, it has preference // Necessary to avoid changing the currently moved point if moving over another point and to avoid losing the current point if it's a new line point and the mouse is dragged very fast if (!firstDrag && relationPointOfCurrentDrag != null) { relationPointOfCurrentDrag = movePointAndResizeRectangle(relationPointOfCurrentDrag, diffX, diffY); return RelationSelection.RELATION_POINT; } // If the special case doesn't apply, forget the relationPointOfFirstDrag, because its a new first drag relationPointOfCurrentDrag = null; if (isPointOverDragBox(point)) { return RelationSelection.DRAG_BOX; } PointDoubleIndexed pointOverRelationPoint = RelationPointHandlerUtils.getRelationPointContaining(point, points); if (pointOverRelationPoint != null) { relationPointOfCurrentDrag = movePointAndResizeRectangle(pointOverRelationPoint, diffX, diffY); return RelationSelection.RELATION_POINT; } Line lineOnPoint = getLineContaining(point); if (lineOnPoint != null) { relationPointOfCurrentDrag = points.addPointOnLine(lineOnPoint, SharedUtils.realignToGridRoundToNearest(false, point.x), SharedUtils.realignToGridRoundToNearest(false, point.y)); relationPointOfCurrentDrag = movePointAndResizeRectangle(relationPointOfCurrentDrag, diffX, diffY); return RelationSelection.LINE; } return RelationSelection.NOTHING; } private boolean isPointOverDragBox(Point point) { return getDragBox().contains(point); } private Line getLineContaining(Point point) { for (Line line : points.getRelationPointLines()) { double distanceToPoint = line.getDistanceToPoint(point.toPointDouble()); if (distanceToPoint < RelationPointConstants.NEW_POINT_DISTANCE) { return line; } } return null; } public List<PointDoubleIndexed> movePointAndResizeRectangle(List<PointChange> changedPoints) { points.applyChangesToPoints(changedPoints); resizeRectAndReposPoints(); List<PointDoubleIndexed> updatedChangedPoint = new ArrayList<PointDoubleIndexed>(); for (PointChange c : changedPoints) { updatedChangedPoint.add(points.get(c.getIndex())); } return updatedChangedPoint; } private PointDoubleIndexed movePointAndResizeRectangle(PointDoubleIndexed point, Integer diffX, Integer diffY) { return movePointAndResizeRectangle(Arrays.asList(new PointChange(point.getIndex(), diffX, diffY))).get(0); } public void resizeRectAndReposPoints() { // now rebuild width and height of the relation, based on the new positions of the relation-points Rectangle newRect = RelationPointHandlerUtils.calculateRelationRectangleBasedOnPoints(relation.getRectangle().getUpperLeftCorner(), relation.getGridSize(), points); relation.setRectangle(newRect); // move relation points to their new position (their position is relative to the relation-position) points.moveRelationPointsAndTextSpacesByToUpperLeftCorner(); } public boolean removeRelationPointIfOnLineBetweenNeighbourPoints() { return points.removeRelationPointIfOnLineBetweenNeighbourPoints(); } // HELPER METHODS public Line getFirstLine() { return points.getFirstLine(); } public Line getMiddleLine() { return points.getMiddleLine(); } public Line getLastLine() { return points.getLastLine(); } public Collection<PointDoubleIndexed> getStickablePoints() { return points.getStickablePoints(); } public Rectangle getDragBox() { return points.getDragBox(); } // DRAW METHODS public void drawLinesBetweenPoints(DrawHandler drawer, boolean shortFirstLine, boolean shortLastLine) { List<Line> lines = points.getRelationPointLines(); for (int i = 0; i < lines.size(); i++) { Line lineToDraw = lines.get(i); if (i == 0 && shortFirstLine) { lineToDraw = lineToDraw.getShorterVersion(true, RelationDrawer.SHORTEN_IF_ARROW); } else if (i == lines.size() - 1 && shortLastLine) { lineToDraw = lineToDraw.getShorterVersion(false, RelationDrawer.SHORTEN_IF_ARROW); } drawer.drawLine(lineToDraw); } } public void drawCirclesAndDragBox(DrawHandler drawer) { for (RelationPoint p : points.getPointHolders()) { drawer.drawCircle(p.getPoint().getX(), p.getPoint().getY(), RelationPointConstants.POINT_SELECTION_RADIUS); } drawer.drawRectangle(getDragBox()); } public String toAdditionalAttributesString() { return points.toAdditionalAttributesString(); } public void drawSelectionSpace(DrawHandler drawer) { for (RelationPoint rp : points.getPointHolders()) { drawer.drawRectangle(rp.getSizeAbsolute()); } } public void setTextBox(int index, Rectangle size) { points.setTextBox(index, size); } /** * resets all textbox indexes except those which are contained in the excludedList */ public void resetTextBoxIndexesExcept(Set<Integer> excludedList) { Set<Integer> unusedTextBoxIndexes = new HashSet<Integer>(points.getTextBoxIndexes()); unusedTextBoxIndexes.removeAll(excludedList); for (Integer index : unusedTextBoxIndexes) { points.removeTextBox(index); } } @Override public void setPointMinSize(int index, Rectangle size) { size = SharedUtils.realignToGrid(size, true); points.setSize(index, size); } @Override public void resetPointMinSize(int index) { points.setSize(index, RelationPoint.DEFAULT_SIZE); } @Override public String toString() { return points.toString(); } }