reorg area calc code

This commit is contained in:
bancika
2020-04-11 17:20:06 +02:00
parent a0611fa727
commit 64920a2c07
4 changed files with 151 additions and 119 deletions
@@ -0,0 +1,125 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC 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.
DIYLC 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 for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.presenter;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class AreaUtils {
/**
* Merges all areas that either overlap or are joined by connections.
*
* @param areas
* @param connections
* @return
*/
public static boolean crunchAreas(List<Area> areas, Set<Connection> connections) {
boolean isChanged = false;
List<Area> newAreas = new ArrayList<Area>();
List<Boolean> consumed = new ArrayList<Boolean>();
for (int i = 0; i < areas.size(); i++) {
consumed.add(false);
}
for (int i = 0; i < areas.size(); i++) {
for (int j = i + 1; j < areas.size(); j++) {
if (consumed.get(j))
continue;
Area a1 = areas.get(i);
Area a2 = areas.get(j);
Area intersection = null;
if (a1.getBounds2D().intersects(a2.getBounds())) {
intersection = new Area(a1);
intersection.intersect(a2);
}
// if the two areas intersect, make a union and consume the second area
if (intersection != null && !intersection.isEmpty()) {
a1.add(a2);
consumed.set(j, true);
} else { // maybe there's a connection between them
for (Connection p : connections) { // use getBounds to optimize the computation, don't get into complex math if not needed
if ((a1.getBounds().contains(p.getP1()) && a2.getBounds().contains(p.getP2()) && a1.contains(p.getP1()) && a2.contains(p.getP2())) ||
(a1.getBounds().contains(p.getP2()) && a2.getBounds().contains(p.getP1())) && a1.contains(p.getP2()) && a2.contains(p.getP1())) {
a1.add(a2);
consumed.set(j, true);
break;
}
}
}
}
}
for (int i = 0; i < areas.size(); i++)
if (!consumed.get(i))
newAreas.add(areas.get(i));
else
isChanged = true;
if (isChanged) {
areas.clear();
areas.addAll(newAreas);
crunchAreas(areas, connections);
}
return isChanged;
}
public static List<Area> tryAreaBreakout(Area a) {
List<Area> toReturn = new ArrayList<Area>();
Path2D p = null;
PathIterator pathIterator = a.getPathIterator(null);
while (!pathIterator.isDone()) {
double[] coord = new double[6];
int type = pathIterator.currentSegment(coord);
switch (type) {
case PathIterator.SEG_MOVETO:
if (p != null) {
Area partArea = new Area(p);
toReturn.add(partArea);
}
p = new Path2D.Double();
p.moveTo(coord[0], coord[1]);
break;
case PathIterator.SEG_LINETO:
p.lineTo(coord[0], coord[1]);
break;
case PathIterator.SEG_CUBICTO:
p.curveTo(coord[0], coord[1], coord[2], coord[3], coord[4], coord[5]);
break;
case PathIterator.SEG_QUADTO:
p.quadTo(coord[0], coord[1], coord[2], coord[3]);
break;
}
pathIterator.next();
}
if (p != null) {
Area partArea = new Area(p);
toReturn.add(partArea);
}
return toReturn;
}
}
@@ -22,7 +22,9 @@
package org.diylc.presenter;
import java.awt.Point;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.diylc.core.IDIYComponent;
import org.diylc.core.measures.Size;
@@ -56,5 +58,24 @@ public class CalcUtils {
return;
}
}
}
public static void expandConnections(Set<Connection> connections) {
Set<Connection> toAdd = new HashSet<Connection>();
for (Connection c1 : connections)
for (Connection c2 : connections) {
if (c1 == c2)
continue;
if (c1.getP1().distance(c2.getP1()) < DrawingManager.CONTROL_POINT_SIZE)
toAdd.add(new Connection(c1.getP2(), c2.getP2()));
if (c1.getP1().distance(c2.getP2()) < DrawingManager.CONTROL_POINT_SIZE)
toAdd.add(new Connection(c1.getP2(), c2.getP1()));
if (c1.getP2().distance(c2.getP1()) < DrawingManager.CONTROL_POINT_SIZE)
toAdd.add(new Connection(c1.getP1(), c2.getP2()));
if (c1.getP2().distance(c2.getP2()) < DrawingManager.CONTROL_POINT_SIZE)
toAdd.add(new Connection(c1.getP1(), c2.getP1()));
}
if (connections.addAll(toAdd))
expandConnections(connections);
}
}
@@ -29,8 +29,6 @@ import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
@@ -631,127 +629,15 @@ public class DrawingManager {
for (int i = 0; i < preliminaryAreas.size(); i++) {
Area a = preliminaryAreas.get(i);
if (checkBreakout.get(i))
areas.addAll(tryBreakout(a));
areas.addAll(AreaUtils.tryAreaBreakout(a));
else
areas.add(a);
}
expandConnections(connections);
CalcUtils.expandConnections(connections);
crunchAreas(areas, connections);
AreaUtils.crunchAreas(areas, connections);
return areas;
}
public void expandConnections(Set<Connection> connections) {
Set<Connection> toAdd = new HashSet<Connection>();
for (Connection c1 : connections)
for (Connection c2 : connections) {
if (c1 == c2)
continue;
if (c1.getP1().distance(c2.getP1()) < CONTROL_POINT_SIZE)
toAdd.add(new Connection(c1.getP2(), c2.getP2()));
if (c1.getP1().distance(c2.getP2()) < CONTROL_POINT_SIZE)
toAdd.add(new Connection(c1.getP2(), c2.getP1()));
if (c1.getP2().distance(c2.getP1()) < CONTROL_POINT_SIZE)
toAdd.add(new Connection(c1.getP1(), c2.getP2()));
if (c1.getP2().distance(c2.getP2()) < CONTROL_POINT_SIZE)
toAdd.add(new Connection(c1.getP1(), c2.getP1()));
}
if (connections.addAll(toAdd))
expandConnections(connections);
}
/**
* Merges all areas that either overlap or are joined by connections.
*
* @param areas
* @param connections
* @return
*/
private boolean crunchAreas(List<Area> areas, Set<Connection> connections) {
boolean isChanged = false;
List<Area> newAreas = new ArrayList<Area>();
List<Boolean> consumed = new ArrayList<Boolean>();
for (int i = 0; i < areas.size(); i++) {
consumed.add(false);
}
for (int i = 0; i < areas.size(); i++) {
for (int j = i + 1; j < areas.size(); j++) {
if (consumed.get(j))
continue;
Area a1 = areas.get(i);
Area a2 = areas.get(j);
Area intersection = null;
if (a1.getBounds2D().intersects(a2.getBounds())) {
intersection = new Area(a1);
intersection.intersect(a2);
}
// if the two areas intersect, make a union and consume the second area
if (intersection != null && !intersection.isEmpty()) {
a1.add(a2);
consumed.set(j, true);
} else { // maybe there's a connection between them
for (Connection p : connections) { // use getBounds to optimize the computation, don't get into complex math if not needed
if ((a1.getBounds().contains(p.getP1()) && a2.getBounds().contains(p.getP2()) && a1.contains(p.getP1()) && a2.contains(p.getP2())) ||
(a1.getBounds().contains(p.getP2()) && a2.getBounds().contains(p.getP1())) && a1.contains(p.getP2()) && a2.contains(p.getP1())) {
a1.add(a2);
consumed.set(j, true);
break;
}
}
}
}
}
for (int i = 0; i < areas.size(); i++)
if (!consumed.get(i))
newAreas.add(areas.get(i));
else
isChanged = true;
if (isChanged) {
areas.clear();
areas.addAll(newAreas);
crunchAreas(areas, connections);
}
return isChanged;
}
private List<Area> tryBreakout(Area a) {
List<Area> toReturn = new ArrayList<Area>();
Path2D p = null;
PathIterator pathIterator = a.getPathIterator(null);
while (!pathIterator.isDone()) {
double[] coord = new double[6];
int type = pathIterator.currentSegment(coord);
switch (type) {
case PathIterator.SEG_MOVETO:
if (p != null) {
Area partArea = new Area(p);
toReturn.add(partArea);
}
p = new Path2D.Double();
p.moveTo(coord[0], coord[1]);
break;
case PathIterator.SEG_LINETO:
p.lineTo(coord[0], coord[1]);
break;
case PathIterator.SEG_CUBICTO:
p.curveTo(coord[0], coord[1], coord[2], coord[3], coord[4], coord[5]);
break;
case PathIterator.SEG_QUADTO:
p.quadTo(coord[0], coord[1], coord[2], coord[3]);
break;
}
pathIterator.next();
}
if (p != null) {
Area partArea = new Area(p);
toReturn.add(partArea);
}
return toReturn;
}
}
}
@@ -3141,7 +3141,7 @@ public class Presenter implements IPlugInPort {
}
}
drawingManager.expandConnections(connections);
CalcUtils.expandConnections(connections);
return new ArrayList<Connection>(connections);
}