|
| 1 | +package convex_dt; |
| 2 | + |
| 3 | +import dcel.HalfEdge; |
| 4 | +import kds.KDSPoint; |
| 5 | +import static convex_dt.Utils.*; // not very nice, but it's to avoid having to write Utils.<func> everywhere |
| 6 | +import static convex_dt.ConvexShape.*; |
| 7 | +import sun.reflect.generics.reflectiveObjects.NotImplementedException; |
| 8 | + |
| 9 | +import java.util.ArrayList; |
| 10 | + |
| 11 | +/** |
| 12 | + * Created by cvium on 29-11-2016. |
| 13 | + */ |
| 14 | +public class ConvexDT { |
| 15 | + private HalfEdge base; |
| 16 | + private HalfEdge lcand; |
| 17 | + private HalfEdge rcand; |
| 18 | + private ConvexShape shape; |
| 19 | + private ArrayList<KDSPoint> points; |
| 20 | + |
| 21 | + public ConvexDT() { |
| 22 | + } |
| 23 | + |
| 24 | + public ConvexDT(ArrayList<KDSPoint> points, ConvexShape shape) { |
| 25 | + throw new NotImplementedException(); |
| 26 | + } |
| 27 | + |
| 28 | + public HalfEdge computeSmallDelaunay(ArrayList<KDSPoint> points) { |
| 29 | + throw new NotImplementedException(); |
| 30 | + } |
| 31 | + |
| 32 | + public HalfEdge findLowerSupport(HalfEdge left, HalfEdge right) { |
| 33 | + if (lowerThan(left.getOrigin(), right.getOrigin())) { |
| 34 | + while (shape.inInfCircle(rNext(right).getOrigin(), left.getOrigin(), right.getOrigin()) == |
| 35 | + infCircleEnum.INSIDE && lessThan(rNext(right).getOrigin(), right.getOrigin())) { |
| 36 | + right = rNext(right); |
| 37 | + } |
| 38 | + if (shape.inInfCircle(right.getDestination(), left.getOrigin(), right.getOrigin()) == infCircleEnum.INSIDE) { |
| 39 | + right = rPrev(right); |
| 40 | + } else if (shape.inInfCircle(left.getDestination(), left.getOrigin(), right.getOrigin()) == infCircleEnum.INSIDE) { |
| 41 | + left = rPrev(left); |
| 42 | + } else { |
| 43 | + return connect(right.getTwin(), oPrev(left)); |
| 44 | + } |
| 45 | + } else { |
| 46 | + // TODO ???? |
| 47 | + throw new NotImplementedException(); |
| 48 | + } |
| 49 | + |
| 50 | + // TODO ?????? |
| 51 | + return connect(right, left); |
| 52 | + } |
| 53 | + |
| 54 | + public void makeBundle(HalfEdge edge) { |
| 55 | + if (edge != lNext(lNext(lNext(edge)))) { |
| 56 | + if (lNext(edge).isBridge()) delete(lNext(edge)); |
| 57 | + if (dNext(edge).isBridge()) delete(dNext(edge)); |
| 58 | + connect(edge, lPrev(edge)).markBridge(); |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + public HalfEdge unBundle(HalfEdge edge) { |
| 63 | + HalfEdge fixed, moving, returnEdge, junk; |
| 64 | + if (onLine(oNext(edge).getDestination(), edge.getOrigin(), edge.getDestination())) { |
| 65 | + fixed = oPrev(edge); |
| 66 | + moving = lNext(lNext(edge)); |
| 67 | + returnEdge = oNext(edge); |
| 68 | + } else { |
| 69 | + fixed = lNext(edge); |
| 70 | + moving = lNext(oPrev(edge)); |
| 71 | + returnEdge = oPrev(edge); |
| 72 | + } |
| 73 | + |
| 74 | + while (fixed != moving) { |
| 75 | + junk = connect(fixed, moving); |
| 76 | + moving = lNext(moving); |
| 77 | + } |
| 78 | + |
| 79 | + return returnEdge; |
| 80 | + } |
| 81 | + |
| 82 | + public void unBundleAll(HalfEdge edge) { |
| 83 | + HalfEdge current; |
| 84 | + for (int direction = 1; direction <= 2; ++direction) { |
| 85 | + current = oNext(edge); |
| 86 | + while (current != edge) { |
| 87 | + if (current.isBridge()) current = unBundle(current); |
| 88 | + else current = oNext(current); |
| 89 | + } |
| 90 | + edge = edge.getTwin(); |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + public HalfEdge produceONext(HalfEdge edge) { |
| 95 | + if (lNext(edge).isBridge()) { |
| 96 | + delete(lNext(edge)); |
| 97 | + makeBundle(connect(lNext(edge), edge).getTwin()); |
| 98 | + } else if (oNext(edge).isBridge()) { |
| 99 | + delete(oNext(edge)); |
| 100 | + makeBundle(oPrev(connect(edge, lPrev(edge)))); |
| 101 | + } |
| 102 | + return oNext(edge); |
| 103 | + } |
| 104 | + |
| 105 | + public HalfEdge produceOPrev(HalfEdge edge) { |
| 106 | + if (lPrev(edge).isBridge()) { |
| 107 | + delete(lPrev(edge)); |
| 108 | + makeBundle(connect(lPrev(edge), edge).getTwin()); |
| 109 | + } else if (oPrev(edge).isBridge()) { |
| 110 | + delete(oPrev(edge)); |
| 111 | + makeBundle(oNext(connect(edge, lNext(edge)))); |
| 112 | + } |
| 113 | + return oPrev(edge); |
| 114 | + } |
| 115 | + |
| 116 | + public HalfEdge connect(HalfEdge a, HalfEdge b) { |
| 117 | + // TODO: does this work? |
| 118 | + HalfEdge newEdge = new HalfEdge(a.getDestination(), b.getOrigin()); |
| 119 | + a.setNext(newEdge); |
| 120 | + b.setPrev(newEdge); |
| 121 | + // hackish way to create the twin |
| 122 | + newEdge.getTwin(); |
| 123 | + return newEdge; |
| 124 | + } |
| 125 | + |
| 126 | + public void delete(HalfEdge e) { |
| 127 | + // remove e from its prev and next edges, TODO: IS THAT ENOUGH? |
| 128 | + e.getPrev().setNext(null); |
| 129 | + e.getNext().setPrev(null); |
| 130 | + // have to remove the twin as well |
| 131 | + e.getTwin().getPrev().setNext(null); |
| 132 | + e.getTwin().getNext().setPrev(null); |
| 133 | + } |
| 134 | + |
| 135 | + public HalfEdge connectLeft() { |
| 136 | + unBundleAll(lcand); |
| 137 | + // \__ Base is directed from right to left, so base dest must be connected to lcand's origin |
| 138 | + return connect(base.getTwin(), lcand.getTwin()); |
| 139 | + } |
| 140 | + |
| 141 | + public HalfEdge connectRight() { |
| 142 | + unBundleAll(rcand); |
| 143 | + // TODO: should this be reversed? THINK NOT |
| 144 | + // __/ Base is directed from left to right, so base dest must be connected to rcand's origin |
| 145 | + return connect(base.getTwin(), rcand.getTwin()); |
| 146 | + } |
| 147 | + |
| 148 | + public HalfEdge computeLcand() { |
| 149 | + HalfEdge current = null, top = null, t = null; |
| 150 | + lcand = rPrev(base); |
| 151 | + |
| 152 | + if (shape.inInfCircle(lcand.getDestination(), base.getOrigin(), base.getDestination()) == infCircleEnum.BEFORE) { |
| 153 | + lcand = produceONext(lcand); |
| 154 | + while (shape.inInfCircle(lcand.getDestination(), base.getOrigin(), base.getDestination()) == infCircleEnum.BEFORE) { |
| 155 | + delete(oPrev(lcand)); |
| 156 | + lcand = produceONext(lcand); |
| 157 | + } |
| 158 | + if (isValid(lcand)) delete(oPrev(lcand)); |
| 159 | + else lcand = oPrev(lcand); |
| 160 | + } |
| 161 | + |
| 162 | + if (isValid(lcand)) { |
| 163 | + current = oNext(lcand); |
| 164 | + top = lcand; |
| 165 | + |
| 166 | + switch(shape.inCircle(lcand.getOrigin(), lcand.getDestination(), base.getOrigin(), current.getDestination())) { |
| 167 | + case INSIDE: |
| 168 | + if (current.isBridge() || rPrev(current).isBridge()) { |
| 169 | + current = produceONext(oPrev(current)); |
| 170 | + } |
| 171 | + if (leftOf(current.getDestination(), lcand.getOrigin(), lcand.getDestination())) { |
| 172 | + if (rPrev(current) != dPrev(lcand)) |
| 173 | + makeBundle(connect(lcand, current.getTwin()).getTwin()); |
| 174 | + if (current != oNext(lcand)) |
| 175 | + makeBundle(lNext(connect(lPrev(lcand), current.getTwin()))); |
| 176 | + t = produceONext(lcand); |
| 177 | + delete(lcand); |
| 178 | + lcand = t; |
| 179 | + current = oNext(lcand); |
| 180 | + top = lcand; |
| 181 | + } else { |
| 182 | + // We have lcand |
| 183 | + break; |
| 184 | + //return lcand; |
| 185 | + } |
| 186 | + break; |
| 187 | + case ONBEFORE: |
| 188 | + if (leftOf(current.getDestination(), current.getOrigin(), lcand.getDestination())) { |
| 189 | + current = rPrev(current); |
| 190 | + } else{ |
| 191 | + return lcand; |
| 192 | + } |
| 193 | + break; |
| 194 | + case ONAFTER: |
| 195 | + top = dNext(top); |
| 196 | + t = current; |
| 197 | + current = oNext(current); |
| 198 | + delete(current); |
| 199 | + break; |
| 200 | + default: |
| 201 | + //return lcand; |
| 202 | + break; |
| 203 | + } |
| 204 | + } else { |
| 205 | + System.out.println("lcand not valid???"); |
| 206 | + } |
| 207 | + |
| 208 | + // TODO top shouldn't be null I think |
| 209 | + //assert top != null; |
| 210 | + //assert current != null; |
| 211 | + |
| 212 | + if (top != lcand) { |
| 213 | + makeBundle(lNext(connect(top, lcand))); |
| 214 | + top = oNext(lcand); |
| 215 | + } |
| 216 | + if (oPrev(current) != top) { |
| 217 | + makeBundle(oPrev(connect(top, oPrev(current)))); |
| 218 | + } |
| 219 | + return lcand; |
| 220 | + } |
| 221 | + |
| 222 | + public HalfEdge computeRcand() { |
| 223 | + HalfEdge current = null, top = null, t = null; |
| 224 | + // TODO ? |
| 225 | + HalfEdge base_twin = base.getTwin(); |
| 226 | + rcand = lNext(base_twin); |
| 227 | + |
| 228 | + if (shape.inInfCircle(rcand.getDestination(), base_twin.getOrigin(), base_twin.getDestination()) == infCircleEnum.BEFORE) { |
| 229 | + rcand = produceOPrev(rcand); |
| 230 | + while (shape.inInfCircle(rcand.getDestination(), base_twin.getOrigin(), base_twin.getDestination()) == infCircleEnum.BEFORE) { |
| 231 | + delete(oNext(rcand)); |
| 232 | + rcand = produceOPrev(rcand); |
| 233 | + } |
| 234 | + if (isValid(rcand)) delete(oNext(rcand)); |
| 235 | + else rcand = oNext(rcand); |
| 236 | + } |
| 237 | + |
| 238 | + if (isValid(rcand)) { |
| 239 | + current = oPrev(rcand); |
| 240 | + top = rcand; |
| 241 | + switch(shape.inCircle(rcand.getOrigin(), rcand.getDestination(), base_twin.getOrigin(), current.getDestination())) { |
| 242 | + case INSIDE: |
| 243 | + if (current.isBridge() || lNext(current).isBridge()) { |
| 244 | + current = produceOPrev(lNext(current)); |
| 245 | + } |
| 246 | + if (leftOf(current.getDestination(), rcand.getOrigin(), rcand.getDestination())) { |
| 247 | + if (lNext(current) != rcand) { |
| 248 | + |
| 249 | + } |
| 250 | + } |
| 251 | + break; |
| 252 | + case ONBEFORE: |
| 253 | + break; |
| 254 | + case ONAFTER: |
| 255 | + break; |
| 256 | + default: |
| 257 | + // we have rcand |
| 258 | + break; |
| 259 | + |
| 260 | + } |
| 261 | + |
| 262 | + } |
| 263 | + |
| 264 | + |
| 265 | + throw new NotImplementedException(); |
| 266 | + } |
| 267 | + |
| 268 | + public HalfEdge delaunay(ArrayList<KDSPoint> points) { |
| 269 | + if (points.size() < 4) return computeSmallDelaunay(points); |
| 270 | + else { |
| 271 | + int split = (int) Math.floor(points.size() / 2); |
| 272 | + |
| 273 | + ArrayList<KDSPoint>left = (ArrayList<KDSPoint>) points.subList(0, split); |
| 274 | + ArrayList<KDSPoint> right = (ArrayList<KDSPoint>) points.subList(split + 1, points.size()); |
| 275 | + |
| 276 | + HalfEdge lleft = delaunay(left); |
| 277 | + HalfEdge lright = delaunay(right); |
| 278 | + |
| 279 | + base = findLowerSupport(lleft, lright); |
| 280 | + boolean leftLower = lowerThan(lleft.getOrigin(), lright.getOrigin()); |
| 281 | + HalfEdge lower; |
| 282 | + if (leftLower) { |
| 283 | + if (lleft.getOrigin() == base.getDestination()) lower = base.getTwin(); |
| 284 | + else lower = lleft; |
| 285 | + } else { |
| 286 | + lower = rNext(lright); |
| 287 | + } |
| 288 | + |
| 289 | + lcand = computeLcand(); |
| 290 | + rcand = computeRcand(); |
| 291 | + |
| 292 | + if (isValid(lcand) && isValid(rcand)) { |
| 293 | + switch(shape.inCircle(base.getOrigin(), base.getDestination(), lcand.getDestination(), rcand.getDestination())) { |
| 294 | + case INSIDE: |
| 295 | + base = connectRight(); |
| 296 | + break; |
| 297 | + case ON: |
| 298 | + case ONBEFORE: |
| 299 | + case ONAFTER: |
| 300 | + if (rightOf(rcand.getDestination(), lcand.getOrigin(), lcand.getDestination())) { |
| 301 | + base = connectRight(); |
| 302 | + } else { |
| 303 | + base = connectLeft(); |
| 304 | + } |
| 305 | + break; |
| 306 | + default: |
| 307 | + base = connectLeft(); |
| 308 | + } |
| 309 | + } |
| 310 | + else if (isValid(lcand)) base = connectLeft(); |
| 311 | + else if (isValid(rcand)) base = connectRight(); |
| 312 | + else if (shape.inInfCircle(rcand.getDestination(), base.getOrigin(), base.getDestination()) == infCircleEnum.AFTER) { |
| 313 | + while (shape.inInfCircle(lcand.getDestination(), lcand.getOrigin(), rcand.getDestination()) == infCircleEnum.INSIDE) { |
| 314 | + lcand = rPrev(lcand); |
| 315 | + } |
| 316 | + base = connect(rcand, oPrev(lcand)); |
| 317 | + delete(rcand); |
| 318 | + } |
| 319 | + else if (shape.inInfCircle(rcand.getDestination(), base.getOrigin(), base.getDestination()) == infCircleEnum.AFTER) { |
| 320 | + while (shape.inInfCircle(rcand.getDestination(), lcand.getDestination(), rcand.getOrigin()) == infCircleEnum.INSIDE) { |
| 321 | + rcand = lNext(rcand); |
| 322 | + } |
| 323 | + base = connect(lPrev(rcand), lcand.getTwin()); |
| 324 | + delete(lcand); |
| 325 | + } |
| 326 | + |
| 327 | + if (!leftLower) lower = rPrev(lower); |
| 328 | + |
| 329 | + return lower; |
| 330 | + } |
| 331 | + } |
| 332 | + |
| 333 | + public boolean isValid(HalfEdge e) { |
| 334 | + return shape.inInfCircle(e.getDestination(), base.getOrigin(), base.getDestination()) == infCircleEnum.INSIDE; |
| 335 | + } |
| 336 | +} |
0 commit comments