/android/Android(LGame-0.3.2&LAE-1.1)/LAE-1.1(Canvas)/src/core/org/loon/framework/android/game/core/geom/AreaOp.java

http://loon-simple.googlecode.com/ · Java · 521 lines · 396 code · 38 blank · 87 comment · 116 complexity · 16ea09b3fc86039a2cf2d9a72b847e04 MD5 · raw file

  1. /*
  2. * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
  3. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4. *
  5. * This code is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License version 2 only, as
  7. * published by the Free Software Foundation. Sun designates this
  8. * particular file as subject to the "Classpath" exception as provided
  9. * by Sun in the LICENSE file that accompanied this code.
  10. *
  11. * This code is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14. * version 2 for more details (a copy is included in the LICENSE file that
  15. * accompanied this code).
  16. *
  17. * You should have received a copy of the GNU General Public License version
  18. * 2 along with this work; if not, write to the Free Software Foundation,
  19. * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20. *
  21. * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22. * CA 95054 USA or visit www.sun.com if you need additional information or
  23. * have any questions.
  24. */
  25. package org.loon.framework.android.game.core.geom;
  26. import java.util.Arrays;
  27. import java.util.Comparator;
  28. import java.util.Enumeration;
  29. import java.util.Vector;
  30. @SuppressWarnings("unchecked")
  31. public abstract class AreaOp {
  32. public static abstract class CAGOp extends AreaOp {
  33. boolean inLeft;
  34. boolean inRight;
  35. boolean inResult;
  36. public void newRow() {
  37. inLeft = false;
  38. inRight = false;
  39. inResult = false;
  40. }
  41. public int classify(Edge e) {
  42. if (e.getCurveTag() == CTAG_LEFT) {
  43. inLeft = !inLeft;
  44. } else {
  45. inRight = !inRight;
  46. }
  47. boolean newClass = newClassification(inLeft, inRight);
  48. if (inResult == newClass) {
  49. return ETAG_IGNORE;
  50. }
  51. inResult = newClass;
  52. return (newClass ? ETAG_ENTER : ETAG_EXIT);
  53. }
  54. public int getState() {
  55. return (inResult ? RSTAG_INSIDE : RSTAG_OUTSIDE);
  56. }
  57. public abstract boolean newClassification(boolean inLeft,
  58. boolean inRight);
  59. }
  60. public static class AddOp extends CAGOp {
  61. public boolean newClassification(boolean inLeft, boolean inRight) {
  62. return (inLeft || inRight);
  63. }
  64. }
  65. public static class SubOp extends CAGOp {
  66. public boolean newClassification(boolean inLeft, boolean inRight) {
  67. return (inLeft && !inRight);
  68. }
  69. }
  70. public static class IntOp extends CAGOp {
  71. public boolean newClassification(boolean inLeft, boolean inRight) {
  72. return (inLeft && inRight);
  73. }
  74. }
  75. public static class XorOp extends CAGOp {
  76. public boolean newClassification(boolean inLeft, boolean inRight) {
  77. return (inLeft != inRight);
  78. }
  79. }
  80. public static class NZWindOp extends AreaOp {
  81. private int count;
  82. public void newRow() {
  83. count = 0;
  84. }
  85. public int classify(Edge e) {
  86. // Note: the right curves should be an empty set with this op...
  87. // assert(e.getCurveTag() == CTAG_LEFT);
  88. int newCount = count;
  89. int type = (newCount == 0 ? ETAG_ENTER : ETAG_IGNORE);
  90. newCount += e.getCurve().getDirection();
  91. count = newCount;
  92. return (newCount == 0 ? ETAG_EXIT : type);
  93. }
  94. public int getState() {
  95. return ((count == 0) ? RSTAG_OUTSIDE : RSTAG_INSIDE);
  96. }
  97. }
  98. public static class EOWindOp extends AreaOp {
  99. private boolean inside;
  100. public void newRow() {
  101. inside = false;
  102. }
  103. public int classify(Edge e) {
  104. // Note: the right curves should be an empty set with this op...
  105. // assert(e.getCurveTag() == CTAG_LEFT);
  106. boolean newInside = !inside;
  107. inside = newInside;
  108. return (newInside ? ETAG_ENTER : ETAG_EXIT);
  109. }
  110. public int getState() {
  111. return (inside ? RSTAG_INSIDE : RSTAG_OUTSIDE);
  112. }
  113. }
  114. private AreaOp() {
  115. }
  116. /* Constants to tag the left and right curves in the edge list */
  117. public static final int CTAG_LEFT = 0;
  118. public static final int CTAG_RIGHT = 1;
  119. /* Constants to classify edges */
  120. public static final int ETAG_IGNORE = 0;
  121. public static final int ETAG_ENTER = 1;
  122. public static final int ETAG_EXIT = -1;
  123. /* Constants used to classify result state */
  124. public static final int RSTAG_INSIDE = 1;
  125. public static final int RSTAG_OUTSIDE = -1;
  126. public abstract void newRow();
  127. public abstract int classify(Edge e);
  128. public abstract int getState();
  129. public Vector calculate(Vector left, Vector right) {
  130. Vector edges = new Vector();
  131. addEdges(edges, left, AreaOp.CTAG_LEFT);
  132. addEdges(edges, right, AreaOp.CTAG_RIGHT);
  133. edges = pruneEdges(edges);
  134. return edges;
  135. }
  136. private static void addEdges(Vector edges, Vector curves, int curvetag) {
  137. Enumeration enum_ = curves.elements();
  138. while (enum_.hasMoreElements()) {
  139. Curve c = (Curve) enum_.nextElement();
  140. if (c.getOrder() > 0) {
  141. edges.add(new Edge(c, curvetag));
  142. }
  143. }
  144. }
  145. private static Comparator YXTopComparator = new Comparator() {
  146. public int compare(Object o1, Object o2) {
  147. Curve c1 = ((Edge) o1).getCurve();
  148. Curve c2 = ((Edge) o2).getCurve();
  149. double v1, v2;
  150. if ((v1 = c1.getYTop()) == (v2 = c2.getYTop())) {
  151. if ((v1 = c1.getXTop()) == (v2 = c2.getXTop())) {
  152. return 0;
  153. }
  154. }
  155. if (v1 < v2) {
  156. return -1;
  157. }
  158. return 1;
  159. }
  160. };
  161. private Vector pruneEdges(Vector edges) {
  162. int numedges = edges.size();
  163. if (numedges < 2) {
  164. return edges;
  165. }
  166. Edge[] edgelist = (Edge[]) edges.toArray(new Edge[numedges]);
  167. Arrays.sort(edgelist, YXTopComparator);
  168. Edge e;
  169. int left = 0;
  170. int right = 0;
  171. int cur = 0;
  172. int next = 0;
  173. double yrange[] = new double[2];
  174. Vector subcurves = new Vector();
  175. Vector chains = new Vector();
  176. Vector links = new Vector();
  177. // Active edges are between left (inclusive) and right (exclusive)
  178. while (left < numedges) {
  179. double y = yrange[0];
  180. // Prune active edges that fall off the top of the active y range
  181. for (cur = next = right - 1; cur >= left; cur--) {
  182. e = edgelist[cur];
  183. if (e.getCurve().getYBot() > y) {
  184. if (next > cur) {
  185. edgelist[next] = e;
  186. }
  187. next--;
  188. }
  189. }
  190. left = next + 1;
  191. // Grab a new "top of Y range" if the active edges are empty
  192. if (left >= right) {
  193. if (right >= numedges) {
  194. break;
  195. }
  196. y = edgelist[right].getCurve().getYTop();
  197. if (y > yrange[0]) {
  198. finalizeSubCurves(subcurves, chains);
  199. }
  200. yrange[0] = y;
  201. }
  202. // Incorporate new active edges that enter the active y range
  203. while (right < numedges) {
  204. e = edgelist[right];
  205. if (e.getCurve().getYTop() > y) {
  206. break;
  207. }
  208. right++;
  209. }
  210. // Sort the current active edges by their X values and
  211. // determine the maximum valid Y range where the X ordering
  212. // is correct
  213. yrange[1] = edgelist[left].getCurve().getYBot();
  214. if (right < numedges) {
  215. y = edgelist[right].getCurve().getYTop();
  216. if (yrange[1] > y) {
  217. yrange[1] = y;
  218. }
  219. }
  220. // Note: We could start at left+1, but we need to make
  221. // sure that edgelist[left] has its equivalence set to 0.
  222. int nexteq = 1;
  223. for (cur = left; cur < right; cur++) {
  224. e = edgelist[cur];
  225. e.setEquivalence(0);
  226. for (next = cur; next > left; next--) {
  227. Edge prevedge = edgelist[next - 1];
  228. int ordering = e.compareTo(prevedge, yrange);
  229. if (yrange[1] <= yrange[0]) {
  230. throw new InternalError("backstepping to " + yrange[1]
  231. + " from " + yrange[0]);
  232. }
  233. if (ordering >= 0) {
  234. if (ordering == 0) {
  235. // If the curves are equal, mark them to be
  236. // deleted later if they cancel each other
  237. // out so that we avoid having extraneous
  238. // curve segments.
  239. int eq = prevedge.getEquivalence();
  240. if (eq == 0) {
  241. eq = nexteq++;
  242. prevedge.setEquivalence(eq);
  243. }
  244. e.setEquivalence(eq);
  245. }
  246. break;
  247. }
  248. edgelist[next] = prevedge;
  249. }
  250. edgelist[next] = e;
  251. }
  252. // Now prune the active edge list.
  253. // For each edge in the list, determine its classification
  254. // (entering shape, exiting shape, ignore - no change) and
  255. // record the current Y range and its classification in the
  256. // Edge object for use later in constructing the new outline.
  257. newRow();
  258. double ystart = yrange[0];
  259. double yend = yrange[1];
  260. for (cur = left; cur < right; cur++) {
  261. e = edgelist[cur];
  262. int etag;
  263. int eq = e.getEquivalence();
  264. if (eq != 0) {
  265. // Find one of the segments in the "equal" range
  266. // with the right transition state and prefer an
  267. // edge that was either active up until ystart
  268. // or the edge that extends the furthest downward
  269. // (i.e. has the most potential for continuation)
  270. int origstate = getState();
  271. etag = (origstate == AreaOp.RSTAG_INSIDE ? AreaOp.ETAG_EXIT
  272. : AreaOp.ETAG_ENTER);
  273. Edge activematch = null;
  274. Edge longestmatch = e;
  275. double furthesty = yend;
  276. do {
  277. // Note: classify() must be called
  278. // on every edge we consume here.
  279. classify(e);
  280. if (activematch == null && e.isActiveFor(ystart, etag)) {
  281. activematch = e;
  282. }
  283. y = e.getCurve().getYBot();
  284. if (y > furthesty) {
  285. longestmatch = e;
  286. furthesty = y;
  287. }
  288. } while (++cur < right
  289. && (e = edgelist[cur]).getEquivalence() == eq);
  290. --cur;
  291. if (getState() == origstate) {
  292. etag = AreaOp.ETAG_IGNORE;
  293. } else {
  294. e = (activematch != null ? activematch : longestmatch);
  295. }
  296. } else {
  297. etag = classify(e);
  298. }
  299. if (etag != AreaOp.ETAG_IGNORE) {
  300. e.record(yend, etag);
  301. links.add(new CurveLink(e.getCurve(), ystart, yend, etag));
  302. }
  303. }
  304. // assert(getState() == AreaOp.RSTAG_OUTSIDE);
  305. if (getState() != AreaOp.RSTAG_OUTSIDE) {
  306. System.out.println("Still inside at end of active edge list!");
  307. System.out.println("num curves = " + (right - left));
  308. System.out.println("num links = " + links.size());
  309. System.out.println("y top = " + yrange[0]);
  310. if (right < numedges) {
  311. System.out.println("y top of next curve = "
  312. + edgelist[right].getCurve().getYTop());
  313. } else {
  314. System.out.println("no more curves");
  315. }
  316. for (cur = left; cur < right; cur++) {
  317. e = edgelist[cur];
  318. System.out.println(e);
  319. int eq = e.getEquivalence();
  320. if (eq != 0) {
  321. System.out.println(" was equal to " + eq + "...");
  322. }
  323. }
  324. }
  325. resolveLinks(subcurves, chains, links);
  326. links.clear();
  327. // Finally capture the bottom of the valid Y range as the top
  328. // of the next Y range.
  329. yrange[0] = yend;
  330. }
  331. finalizeSubCurves(subcurves, chains);
  332. Vector ret = new Vector();
  333. Enumeration enum_ = subcurves.elements();
  334. while (enum_.hasMoreElements()) {
  335. CurveLink link = (CurveLink) enum_.nextElement();
  336. ret.add(link.getMoveto());
  337. CurveLink nextlink = link;
  338. while ((nextlink = nextlink.getNext()) != null) {
  339. if (!link.absorb(nextlink)) {
  340. ret.add(link.getSubCurve());
  341. link = nextlink;
  342. }
  343. }
  344. ret.add(link.getSubCurve());
  345. }
  346. return ret;
  347. }
  348. public static void finalizeSubCurves(Vector subcurves, Vector chains) {
  349. int numchains = chains.size();
  350. if (numchains == 0) {
  351. return;
  352. }
  353. if ((numchains & 1) != 0) {
  354. throw new InternalError("Odd number of chains!");
  355. }
  356. ChainEnd[] endlist = new ChainEnd[numchains];
  357. chains.toArray(endlist);
  358. for (int i = 1; i < numchains; i += 2) {
  359. ChainEnd open = endlist[i - 1];
  360. ChainEnd close = endlist[i];
  361. CurveLink subcurve = open.linkTo(close);
  362. if (subcurve != null) {
  363. subcurves.add(subcurve);
  364. }
  365. }
  366. chains.clear();
  367. }
  368. private static CurveLink[] EmptyLinkList = new CurveLink[2];
  369. private static ChainEnd[] EmptyChainList = new ChainEnd[2];
  370. public static void resolveLinks(Vector subcurves, Vector chains,
  371. Vector links) {
  372. int numlinks = links.size();
  373. CurveLink[] linklist;
  374. if (numlinks == 0) {
  375. linklist = EmptyLinkList;
  376. } else {
  377. if ((numlinks & 1) != 0) {
  378. throw new InternalError("Odd number of new curves!");
  379. }
  380. linklist = new CurveLink[numlinks + 2];
  381. links.toArray(linklist);
  382. }
  383. int numchains = chains.size();
  384. ChainEnd[] endlist;
  385. if (numchains == 0) {
  386. endlist = EmptyChainList;
  387. } else {
  388. if ((numchains & 1) != 0) {
  389. throw new InternalError("Odd number of chains!");
  390. }
  391. endlist = new ChainEnd[numchains + 2];
  392. chains.toArray(endlist);
  393. }
  394. int curchain = 0;
  395. int curlink = 0;
  396. chains.clear();
  397. ChainEnd chain = endlist[0];
  398. ChainEnd nextchain = endlist[1];
  399. CurveLink link = linklist[0];
  400. CurveLink nextlink = linklist[1];
  401. while (chain != null || link != null) {
  402. /*
  403. * Strategy 1: Connect chains or links if they are the only things
  404. * left...
  405. */
  406. boolean connectchains = (link == null);
  407. boolean connectlinks = (chain == null);
  408. if (!connectchains && !connectlinks) {
  409. // assert(link != null && chain != null);
  410. /*
  411. * Strategy 2: Connect chains or links if they close off an open
  412. * area...
  413. */
  414. connectchains = ((curchain & 1) == 0 && chain.getX() == nextchain
  415. .getX());
  416. connectlinks = ((curlink & 1) == 0 && link.getX() == nextlink
  417. .getX());
  418. if (!connectchains && !connectlinks) {
  419. /*
  420. * Strategy 3: Connect chains or links if their successor is
  421. * between them and their potential connectee...
  422. */
  423. double cx = chain.getX();
  424. double lx = link.getX();
  425. connectchains = (nextchain != null && cx < lx && obstructs(
  426. nextchain.getX(), lx, curchain));
  427. connectlinks = (nextlink != null && lx < cx && obstructs(
  428. nextlink.getX(), cx, curlink));
  429. }
  430. }
  431. if (connectchains) {
  432. CurveLink subcurve = chain.linkTo(nextchain);
  433. if (subcurve != null) {
  434. subcurves.add(subcurve);
  435. }
  436. curchain += 2;
  437. chain = endlist[curchain];
  438. nextchain = endlist[curchain + 1];
  439. }
  440. if (connectlinks) {
  441. ChainEnd openend = new ChainEnd(link, null);
  442. ChainEnd closeend = new ChainEnd(nextlink, openend);
  443. openend.setOtherEnd(closeend);
  444. chains.add(openend);
  445. chains.add(closeend);
  446. curlink += 2;
  447. link = linklist[curlink];
  448. nextlink = linklist[curlink + 1];
  449. }
  450. if (!connectchains && !connectlinks) {
  451. // assert(link != null);
  452. // assert(chain != null);
  453. // assert(chain.getEtag() == link.getEtag());
  454. chain.addLink(link);
  455. chains.add(chain);
  456. curchain++;
  457. chain = nextchain;
  458. nextchain = endlist[curchain + 1];
  459. curlink++;
  460. link = nextlink;
  461. nextlink = linklist[curlink + 1];
  462. }
  463. }
  464. if ((chains.size() & 1) != 0) {
  465. System.out.println("Odd number of chains!");
  466. }
  467. }
  468. /*
  469. * Does the position of the next edge at v1 "obstruct" the connectivity
  470. * between current edge and the potential partner edge which is positioned
  471. * at v2?
  472. *
  473. * Phase tells us whether we are testing for a transition into or out of the
  474. * interior part of the resulting area.
  475. *
  476. * Require 4-connected continuity if this edge and the partner edge are both
  477. * "entering into" type edges Allow 8-connected continuity for
  478. * "exiting from" type edges
  479. */
  480. public static boolean obstructs(double v1, double v2, int phase) {
  481. return (((phase & 1) == 0) ? (v1 <= v2) : (v1 < v2));
  482. }
  483. }