/** * Methode analysiert die Grundrissebenen und verwendet deren Normalenvektoren, um zu bestimmen, * welcher Koordinatenebene deren Ausrichtungen am naechsten kommen, indem die Komponente mit dem * groessten absoluten Wert des Normalenvektors ermittelt wird. Diese wird zurueckgereicht * * @param bucket Bucket mit Grundrissen, die innerhalb der Methode gemerged werden * @return Achse, die fuer eine Projektion ignoriert werden koennte */ private Axis findRelevantComponent(FootprintBucket bucket) { Footprint currentFootprint = null; MyVector3f planeNormal = null; List<Footprint> footprints = bucket.getFootprints(); Iterator<Footprint> footprintIter = footprints.iterator(); Axis relevantComponent = Axis.UNKNOWN; Axis resultComponent = Axis.UNKNOWN; while (footprintIter.hasNext()) { currentFootprint = footprintIter.next(); // hole die Normale und suche eine Komponente mit Wert 0 planeNormal = currentFootprint.getFootprintPoly().getNormal(); // bestimme die Koordinatenkomponente mit dem groessten absoluten // Wert relevantComponent = mMathHelper.getIgnorableAxis(planeNormal, false); // validiere, dass alle Grundrisse die gleiche Ausrichtung besitzen if (resultComponent != Axis.UNKNOWN && relevantComponent != resultComponent) { assert false : "Planes mit unterschiedlichen Ausrichtungen der Normalenvektoren im gleichen Bucket"; } else resultComponent = relevantComponent; } return resultComponent; }
/** * 2. Votingverfahren fuer Strahlauswahl: Methode testet, ob der getroffene Strahl und der * Ausgangsstrahl Teil des gleichen Ausgangspolygons sind * * @param currentHit Treffer-Instanz, fuer deren Strahl getestet wird, ob dieser zum gleichen * Polygon gehoert, wie der Ausgangsstrahl * @param currentRay Strahlen-Instanz, fuer die ein Nachfolger gesucht wird * @param bucket Eimer, fuer den aktuell ein gemergter Grundriss errechnet wird * @return True, falls beide Strahlen Kanten des gleichen Polygons sind, False sonst */ private boolean isPartOfSameObject( final Hit currentHit, final Ray currentRay, final FootprintBucket bucket) { // bestimme die Quellpolygone fuer den Trefferstrahl und den Teststrahl List<Footprint> footprints = bucket.getFootprints(); MyPolygon currentPolygon = null; Ray hitRay = currentHit.getHitRay(); LOGGER.debug("CURRENT RAY: " + currentRay + " HITRAY: " + hitRay); for (int i = 0; i < footprints.size(); i++) { currentPolygon = footprints.get(i).getFootprintPoly(); // wenn beide Strahlen im Polygon enthalten sind, gebe True zurueck if (currentPolygon.isRayInPolygon(hitRay) && currentPolygon.isRayInPolygon(currentRay)) { LOGGER.debug("SAME POLY"); LOGGER.debug("CUR POLY: " + currentPolygon); return true; } } return false; }
/** * Methode sucht innerhalb der Footprintstrukturen nach einem Strahl, von dem aus der * Schnittalgorithmus starten kann. Der Startpunkt des Verfahrens muss ausserhalb aller anderen * Grundrisse liegen. Ein solcher Punkt muss innerhalb der Ebene, in der der Grundriss liegt, * einen Extremwert aufweisen. * * @param bucket Eimer mit einer Menge von Grundrissen, fuer die ein gemergter Grundriss erstellt * werden soll * @return Strahl, von dem ausgehend die Schnittberechnung erfolgt */ private Ray findStartRay(FootprintBucket bucket) { Axis ignorableAxis = findRelevantComponent(bucket); // sammele alle Rays in einer grossen Liste List<Ray> allRays = new ArrayList<Ray>(); List<Footprint> allFootprints = bucket.getFootprints(); Iterator<Footprint> footprintIter = allFootprints.iterator(); while (footprintIter.hasNext()) { allRays.addAll(footprintIter.next().getRays()); } List<Ray> rayBuffer = new ArrayList<Ray>(); // initialisiere auf maximal moeglichen Float-Wert float minValueForRelevantComponent = Float.MAX_VALUE; Ray currentStart = null, currentRay = null; MyVector3f currentPos = null; // suche jetzt den Strahl, der bezueglich der relevanten Komponente den // kleinsten Wert besitzt, dies ist der Start-Strahl Iterator<Ray> rayIter = allRays.iterator(); while (rayIter.hasNext()) { currentRay = rayIter.next(); currentPos = currentRay.getStartPtr(); // durchlaufe alle Rays und suche denjenigen, der zunaechst in einer // Komponente den kleinsten Wert besitzt // die Entscheidung, welche Komponente getestet wird, haengt von der // Achse ab, die vorab als "ignorierbar" bestimmt wurde switch (ignorableAxis) { // x case X: if (currentPos.z <= minValueForRelevantComponent) { minValueForRelevantComponent = currentPos.z; // adde den Ray am Start des Buffers => dadurch befinden // sich die Rays mit den kleinsten Werten am Ende vorne rayBuffer.add(0, currentRay); } break; // y case Y: if (currentPos.x <= minValueForRelevantComponent) { minValueForRelevantComponent = currentPos.x; rayBuffer.add(0, currentRay); } break; // z case Z: if (currentPos.y <= minValueForRelevantComponent) { minValueForRelevantComponent = currentPos.y; rayBuffer.add(0, currentRay); } break; // Fehler default: assert false : "Die berechnete relevante Komponente ist ungueltig: " + ignorableAxis; break; } } // durchlaufe den Buffer und pruefe, ob mehrere Rays mit gleicher // Komponente vorkommen rayIter = rayBuffer.iterator(); minValueForRelevantComponent = Float.MAX_VALUE; // entferne nun alle Strahlen, deren Wert ueber dem aktuellen Minimum // liegt while (rayIter.hasNext()) { currentRay = rayIter.next(); currentPos = currentRay.getStartPtr(); switch (ignorableAxis) { // x case X: if (currentPos.z <= minValueForRelevantComponent) { minValueForRelevantComponent = currentPos.z; } // Komponentenwert liegt ueber dem aktuellen Minimum => // entfernen else rayIter.remove(); break; // y case Y: if (currentPos.x <= minValueForRelevantComponent) { minValueForRelevantComponent = currentPos.x; } else rayIter.remove(); break; // z case Z: if (currentPos.y <= minValueForRelevantComponent) { minValueForRelevantComponent = currentPos.y; } else rayIter.remove(); break; } } // wenn es nur noch einen Strahl gibt, hat man sein Ergebnis if (rayBuffer.size() == 1) return rayBuffer.get(0); // Kontrollwert zuruecksetzen minValueForRelevantComponent = Float.MAX_VALUE; // sonst muss noch eine zweite Komponente geprueft werden // erneuter Switch mit Test auf die verbleibende Komponente: rayIter = rayBuffer.iterator(); while (rayIter.hasNext()) { currentRay = rayIter.next(); currentPos = currentRay.getStartPtr(); switch (ignorableAxis) { // x case X: if (currentPos.y < minValueForRelevantComponent) { minValueForRelevantComponent = currentPos.y; currentStart = currentRay; } break; // y case Y: if (currentPos.z < minValueForRelevantComponent) { minValueForRelevantComponent = currentPos.z; currentStart = currentRay; } break; // z case Z: if (currentPos.x < minValueForRelevantComponent) { minValueForRelevantComponent = currentPos.x; currentStart = currentRay; } break; } } // jetzt hat man einen Strahl, der in beiden Komponenten minimal ist return currentStart; }
/** * Methode berechnet fuer den uebergebenen Grundriss-Bucket einen gemergeten Grundriss und gibt * diesen als Vektor von Vertex3d-Instanzen zurueck. * * @param bucket Bucket mit Grundrissen, die innerhalb der Methode gemerged werden * @return Vektor mit Vertex3d-Instanzen, die den gemergten Grundriss beschreiben */ public List<Vertex3d> computeMergedFootprint(FootprintBucket bucket) { LOGGER.info("Footprintbucket enthaelt: " + bucket.getFootprints().size() + " Eingabepolygone!"); Ray startRay = findStartRay(bucket); LOGGER.trace("Startray fuer Schnittberechnungen: " + startRay); assert startRay != null : "FEHLER: Es konnte kein Anfangsstrahl ermittelt werden!"; List<Vertex3d> resultVertices = new ArrayList<Vertex3d>(); // sammele erneut alle Strahlen aller Grundrisse ein List<Ray> allRays = new ArrayList<Ray>(); List<Footprint> allFootprints = bucket.getFootprints(); Iterator<Footprint> footprintIter = allFootprints.iterator(); while (footprintIter.hasNext()) { allRays.addAll(footprintIter.next().getRays()); } Iterator<Ray> rayIter = allRays.iterator(); LOGGER.trace("Insgesamt befinden sich " + allRays.size() + " Rays im Eimer"); while (rayIter.hasNext()) { LOGGER.trace(rayIter.next()); } Ray currentRay = null, lastRay = null, intersectionRay = null; MyVector3f intersectionPoint = null; Vertex3d resultVertex = null; // fuege Startpunkt des Startstrahls als erstes Vertex hinzu resultVertex = new Vertex3d(startRay.getStart()); resultVertices.add(resultVertex); // Steuervariable fuer Iterationen Boolean doContinue = true; // erster Treffer des Startstrahls, wird fuer Abbruchkriterium benoetigt MyVector3f firstHit = null; // Iteration laeuft so lange, bis Abbruchkriterium erfuellt wurde while (doContinue) { // 1. Iteration if (currentRay == null) { currentRay = startRay; } Hit resultHit = findIntersectingRay(currentRay, bucket, lastRay, intersectionPoint); assert resultHit != null : "Es konnte kein Schnittstrahl ermittelt werden"; // System.out.println("Gefundener Strahl: " + intersectionRay); intersectionRay = resultHit.getHitRay(); intersectionPoint = resultHit.getIntersection(); // speichere den ersten gefundenen Treffer if (firstHit == null) firstHit = intersectionPoint; // erzeuge eine Vertex3d-Instanz fuer den Schnittpunkt und fuege sie // zum Result-Vektor hinzu resultVertex = new Vertex3d(intersectionPoint); // breche ab, wenn ein Vertex geadded werden soll, das bereits // vorhanden ist LOGGER.trace("Adde Vertex: " + resultVertex); if (resultVertices.contains(resultVertex)) break; else resultVertices.add(resultVertex); // naechste Iteration vorbereiten lastRay = currentRay; currentRay = intersectionRay; // Abbruchkriterium pruefen doContinue = checkTermination(startRay, firstHit, currentRay, intersectionPoint); } return resultVertices; }