package com.baselet.element.relation.helper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.baselet.control.SharedUtils;
import com.baselet.control.basics.geom.GeometricFunctions;
import com.baselet.control.basics.geom.Line;
import com.baselet.control.basics.geom.PointDouble;
import com.baselet.control.basics.geom.Rectangle;
import com.baselet.element.sticking.PointChange;
import com.baselet.element.sticking.PointDoubleIndexed;
public class RelationPointList {
List<RelationPoint> points = new ArrayList<RelationPoint>();
Map<Integer, Rectangle> textBoxSpaces = new HashMap<Integer, Rectangle>();
public void add(double x, double y) {
points.add(new RelationPoint(points.size(), x, y));
}
public List<RelationPoint> getPointHolders() {
return Collections.unmodifiableList(points);
}
public PointDoubleIndexed addPointOnLine(Line line, double x, double y) {
RelationPoint newPoint = null;
PointDouble endOfLine = line.getStart();
for (ListIterator<RelationPoint> iter = points.listIterator(); iter.hasNext();) {
PointDoubleIndexed point = iter.next().getPoint();
if (point.equals(endOfLine)) {
newPoint = new RelationPoint(iter.nextIndex(), x, y);
iter.add(newPoint);
}
}
rebuildpointIndexes();
if (newPoint == null) {
throw new RuntimeException("Point " + endOfLine + " not found in list " + points);
}
return newPoint.getPoint();
}
private void rebuildpointIndexes() {
List<RelationPoint> rebuiltList = new ArrayList<RelationPoint>();
for (int i = 0; i < points.size(); i++) {
rebuiltList.add(new RelationPoint(i, points.get(i).getPoint().getX(), points.get(i).getPoint().getY()));
}
points.clear();
points.addAll(rebuiltList);
}
public void applyChangesToPoints(List<PointChange> changes) {
applyPointChange(changes);
// if there are only 2 points and they would overlap now (therefore the relation would have a size of 0x0px), revert the move
// revertChangesIfOnly2PointsOverlap(changes); // disabled because of issue #382 (see details there)
}
// private void revertChangesIfOnly2PointsOverlap(List<PointChange> changes) {
// if (points.size() == 2 && points.get(0).getPoint().getX().equals(points.get(1).getPoint().getX()) && points.get(0).getPoint().getY().equals(points.get(1).getPoint().getY())) {
// List<PointChange> inverse = new ArrayList<PointChange>();
// for (PointChange change : changes) {
// inverse.add(new PointChange(change.getIndex(), -change.getDiffX(), -change.getDiffY()));
// }
// applyPointChange(inverse);
// }
// }
private void applyPointChange(List<PointChange> changes) {
for (ListIterator<RelationPoint> iter = points.listIterator(); iter.hasNext();) {
RelationPoint p = iter.next();
PointDoubleIndexed pt = p.getPoint();
for (PointChange change : changes) {
if (pt.getIndex().equals(change.getIndex())) {
iter.set(new RelationPoint(pt.getIndex(), pt.getX() + change.getDiffX(), pt.getY() + change.getDiffY(), p.getSize()));
}
}
}
}
void moveRelationPointsAndTextSpacesByToUpperLeftCorner() {
Rectangle rect = createRectangleContainingAllPointsAndTextSpace();
int displacementX = SharedUtils.realignToGrid(false, rect.getX(), false);
int displacementY = SharedUtils.realignToGrid(false, rect.getY(), false);
moveRelationPointsAndTextSpacesBy(-displacementX, -displacementY);
}
void moveRelationPointsAndTextSpacesBy(int displacementX, int displacementY) {
for (ListIterator<RelationPoint> iter = points.listIterator(); iter.hasNext();) {
RelationPoint p = iter.next();
iter.set(new RelationPoint(p.getPoint().getIndex(), p.getPoint().getX() + displacementX, p.getPoint().getY() + displacementY, p.getSize()));
// If points are off the grid they can be realigned here (use the following 2 lines instead of move())
// p.setX(SharedUtils.realignTo(true, p.getX()-displacementX, false, SharedConstants.DEFAULT_GRID_SIZE));
// p.setY(SharedUtils.realignTo(true, p.getY()-displacementY, false, SharedConstants.DEFAULT_GRID_SIZE));
}
for (Entry<Integer, Rectangle> textSpace : textBoxSpaces.entrySet()) {
Rectangle old = textSpace.getValue();
textSpace.setValue(new Rectangle(old.getX() + displacementX, old.getY() + displacementY, old.getWidth(), old.getHeight()));
}
}
public boolean removeRelationPointIfOnLineBetweenNeighbourPoints() {
boolean updateNecessary = false;
if (points.size() > 2) {
ListIterator<RelationPoint> iter = points.listIterator();
PointDoubleIndexed leftNeighbour = iter.next().getPoint();
PointDoubleIndexed pointToCheck = iter.next().getPoint();
while (iter.hasNext()) {
PointDoubleIndexed rightNeighbour = iter.next().getPoint();
// if a point lies on the line between its 2 neighbourpoints, it will be removed
if (GeometricFunctions.getDistanceBetweenLineAndPoint(leftNeighbour, rightNeighbour, pointToCheck) < 5) {
updateNecessary = true;
iter.previous();
iter.previous();
iter.remove();
pointToCheck = iter.next().getPoint();
}
else {
leftNeighbour = pointToCheck;
pointToCheck = rightNeighbour;
}
}
}
if (updateNecessary) {
rebuildpointIndexes();
}
return updateNecessary;
}
public List<Line> getRelationPointLines() {
List<Line> lines = new ArrayList<Line>();
for (int i = 1; i < points.size(); i++) {
lines.add(new Line(points.get(i - 1).getPoint(), points.get(i).getPoint()));
}
return lines;
}
public Line getFirstLine() {
return new Line(points.get(0).getPoint(), points.get(1).getPoint());
}
public Line getMiddleLine() {
PointDoubleIndexed begin = points.get(points.size() / 2).getPoint();
PointDoubleIndexed end = points.get(points.size() / 2 - 1).getPoint();
return new Line(begin, end);
}
public Line getLastLine() {
return new Line(points.get(points.size() - 2).getPoint(), points.get(points.size() - 1).getPoint());
}
public Collection<PointDoubleIndexed> getStickablePoints() {
return Arrays.asList(points.get(0).getPoint(), points.get(points.size() - 1).getPoint());
}
public Rectangle getDragBox() {
PointDouble center = getMiddleLine().getCenter();
double size = RelationPointConstants.DRAG_BOX_SIZE / 2;
Rectangle rectangle = new Rectangle(center.x - size, center.y - size, size * 2, size * 2);
return rectangle;
}
public String toAdditionalAttributesString() {
StringBuilder sb = new StringBuilder("");
for (RelationPoint p : points) {
sb.append(p.getPoint().getX()).append(";").append(p.getPoint().getY()).append(";");
}
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
}
return sb.toString();
}
@Override
public String toString() {
return "Relationpoints: " + SharedUtils.listToString(",", points);
}
public PointDoubleIndexed get(int index) {
return points.get(index).getPoint();
}
public void setSize(int index, Rectangle size) {
for (RelationPoint p : points) {
if (p.getPoint().getIndex() == index) {
p.setSize(size);
return;
}
}
throw new RuntimeException("Unknown Point Index " + index);
}
public void setTextBox(int index, Rectangle rect) {
Rectangle realignedRect = SharedUtils.realignToGrid(rect, true);
textBoxSpaces.put(index, realignedRect);
}
public void removeTextBox(int index) {
textBoxSpaces.remove(index);
}
public Set<Integer> getTextBoxIndexes() {
return textBoxSpaces.keySet();
}
public Rectangle createRectangleContainingAllPointsAndTextSpace() {
Rectangle rectangleContainingAllPointsAndTextSpace = null;
for (RelationPoint p : points) {
rectangleContainingAllPointsAndTextSpace = Rectangle.mergeToLeft(rectangleContainingAllPointsAndTextSpace, p.getSizeAbsolute());
}
for (Rectangle textSpace : textBoxSpaces.values()) {
rectangleContainingAllPointsAndTextSpace = Rectangle.mergeToLeft(rectangleContainingAllPointsAndTextSpace, textSpace);
}
return rectangleContainingAllPointsAndTextSpace;
}
}