Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/**
* This file is part of LibLaserCut.
* Copyright (C) 2011 - 2013 Thomas Oster <thomas.oster@rwth-aachen.de>
* RWTH Aachen University - 52062 Aachen, Germany
*
* LibLaserCut is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LibLaserCut 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with LibLaserCut. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.t_oster.liblasercut.vectoroptimizers;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author Thomas Oster <thomas.oster@rwth-aachen.de>
*/
public class InnerFirstVectorOptimizer extends VectorOptimizer
{
// helper classes:
private abstract class ElementValueComparator implements Comparator<Element>
{
/**
* get one integer from the element
* order ascending by this integer
* inside objects should have the lowest values
*/
abstract int getValue(Element e);
/**
* compare by getValue()
*/
@Override
public int compare(Element a, Element b)
{
Integer av = new Integer(getValue(a));
Integer bv = new Integer(getValue(b));
return av.compareTo(bv);
}
}
private class XMinComparator extends ElementValueComparator
{
// compare by XMin a>b
@Override
int getValue(Element e)
{
return -e.boundingBox().getXMin();
}
}
private class YMinComparator extends ElementValueComparator
{
// compare by YMin a>b
@Override
int getValue(Element e)
{
return -e.boundingBox().getYMin();
}
}
private class XMaxComparator extends ElementValueComparator
{
// compare by XMax a<b
@Override
int getValue(Element e)
{
return e.boundingBox().getXMax();
}
}
private class YMaxComparator extends ElementValueComparator
{
// compare by YMax a<b
@Override
int getValue(Element e)
{
return e.boundingBox().getYMax();
}
}
@Override
protected List<Element> sort(List<Element> e)
{
List<Element> result = new LinkedList<Element>();
if (e.isEmpty())
{
return result;
}
/**
* cut inside parts first, outside parts later
* this algorithm is very robust, it works even for unconnected paths that
* are split into individual lines (e.g. from some DXF imports)
* it is not completely perfect, as it only considers the bounding-box and
* not the individual path
*
* see below for documentation of the inner workings
*/
result.addAll(e);
/**
* HEURISTIC:
* this algorithm is based on the following observation:
* let I and O be rectangles, I inside O
* for explanations, assume that:
* - the X-axis goes from left to right
* - the Y-axis goes from bottom to top
*
* ---------------- O: outside rectangle
* | |
* | ---- |
* y axis | |in| I |
* ^ | ---- |
* | | |
* | ----------------
* |
* ------> x axis
*
* look at each border:
* right border: I.getXMax() < O.getXMax()
* left border: I.getXMin() > O.getXMin()
* top border: I.getYMax() < O.getYMax()
* bottom border: I.getYMin() > O.getYMin()
*
* If we now SORT BY ymax ASCENDING, ymin DESCENDING, xmax ASCENDING, xmin
* DESCENDING
* (higher sorting priority listed first)
* we get the rectangles sorted inside-out:
* 1. I
* 2. O
*
* Because we sort by four values, this still works if
* the two rectangles start at the same corner and have the same width,
* but only differ in height.
*
* If each rectangle is split into four separate lines
* (e.g. because of a bad DXF import),
* this still mostly works:
* 1. O: bottom line
* 2. I: bottom
* 3. I: top, left, right (both have same YMax, but top has a higher YMin)
* 4: O: top, left, right (both have same YMax, but top has a higher YMin)
*
* TRADEOFFS AND LIMITATIONS:
* This algorithm does not work for paths that have the same bounding-box
* (e.g. a circle inscribed to a square)
*
* For concave polygons with the same bounding-box,
* many simple Polygon-inside-Polygon algorithms also fail
* (or have a useless definition of "inside" that matches the misbehaviour):
* Draw a concave polygon, remove one point at a concave edge.
* The resulting polygon is clearly outside the original, although every
* edge of it is inside the original!
*
* FUTURE WORK:
* It would also be nice to sort intersecting polygons, where one polygon
* is "90% inside" and "10% outside" the other.
* Real-world example:_A circular hole at the border of a rectangle.
* Due to rounding errors, it may appear slightly outside the rectangle.
* Mathematically, it is neither fully inside nor fully outside, but the
* user clearly wants it to be counted as "inside".
*
* POSSIBLE LIBRARIES:
* http://sourceforge.net/projects/geom-java/
* http://sourceforge.net/projects/jts-topo-suite
*
* USEFUL METHODS:
* Element.isClosedPath()
*/
// do the work:
Collections.sort(result, new XMinComparator());
Collections.sort(result, new YMinComparator());
Collections.sort(result, new XMaxComparator());
Collections.sort(result, new YMaxComparator());
return result;
}
}