From 6e94b4c82d7e7ca49cf0185b29393c884d8bde0d Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 15 Jul 2025 14:47:21 +0200 Subject: [PATCH 1/3] Fix: BoundingBoxCalculator not including BoundarySurfaces --- .../utils/BoundingBoxCalculator.java | 72 ++++++++----------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java index 94a65d45..78acc1d0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java @@ -26,12 +26,9 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.Polygon; -import de.hft.stuttgart.citydoctor2.datastructure.TopLevelTransportFeature; -import de.hft.stuttgart.citydoctor2.datastructure.TrafficSpaceObject; -import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject; -import de.hft.stuttgart.citydoctor2.datastructure.TransportationSpace; import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.math.Vector3d; +import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector; /** * Utility class for calculating axis aligned bounding boxes for different @@ -99,14 +96,13 @@ public class BoundingBoxCalculator { * @return the bounding box of the model */ public static BoundingBox calculateBoundingBox(CityDoctorModel model) { - Vector3d low = new Vector3d(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); + Vector3d low = new Vector3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); Vector3d high = new Vector3d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); - //TODO: Rework this using visitors findMinMax(low, high, model.getBuildings()); findMinMax(low, high, model.getBridges()); findMinMax(low, high, model.getLand()); - findMinMaxTransport(low, high, model.getTransportation()); + findMinMax(low, high, model.getTransportation()); findMinMax(low, high, model.getWater()); findMinMax(low, high, model.getVegetation()); findMinMax(low, high, model.getTunnels()); @@ -159,47 +155,39 @@ public class BoundingBoxCalculator { } } - //TODO: Implement this properly with visitor. Quick and dirty fix for the renderer - private static void findMinMaxTransport(Vector3d low, Vector3d high, List features) { - for (TransportationObject to : features) { - findMinMax(low, high, to); - if (to instanceof TransportationSpace ts) { - findMinMaxTransport(low, high, ts.getTrafficSpaces()); - findMinMaxTransport(low, high, ts.getAuxTrafficSpaces()); - if (to instanceof TopLevelTransportFeature top) { - findMinMaxTransport(low, high, top.getSections()); - findMinMaxTransport(low, high, top.getIntersections()); + + private static void findMinMax(Vector3d low, Vector3d high, CityObject co) { + CityObjectCollector collector = new CityObjectCollector(); + co.accept(collector); + for (CityObject object : collector.getCityObjects()) { + for (Geometry geom : object.getGeometries()) { + if (geom.getVertices() == null) { + geom.updateVertices(); } - } else if (to instanceof TrafficSpaceObject tso) { - findMinMaxTransport(low, high, tso.getTrafficAreas()); + findMinMax(low, high, geom); } } } - private static void findMinMax(Vector3d low, Vector3d high, CityObject co) { - for (Geometry geom : co.getGeometries()) { - if (geom.getVertices() == null) { - geom.updateVertices(); + private static void findMinMax(Vector3d low, Vector3d high, Geometry geom) { + for (Vertex v : geom.getVertices()) { + if (v.getX() < low.getX()) { + low.setX(v.getX()); } - for (Vertex v : geom.getVertices()) { - if (v.getX() < low.getX()) { - low.setX(v.getX()); - } - if (v.getX() > high.getX()) { - high.setX(v.getX()); - } - if (v.getY() < low.getY()) { - low.setY(v.getY()); - } - if (v.getY() > high.getY()) { - high.setY(v.getY()); - } - if (v.getZ() < low.getZ()) { - low.setZ(v.getZ()); - } - if (v.getZ() > high.getZ()) { - high.setZ(v.getZ()); - } + if (v.getX() > high.getX()) { + high.setX(v.getX()); + } + if (v.getY() < low.getY()) { + low.setY(v.getY()); + } + if (v.getY() > high.getY()) { + high.setY(v.getY()); + } + if (v.getZ() < low.getZ()) { + low.setZ(v.getZ()); + } + if (v.getZ() > high.getZ()) { + high.setZ(v.getZ()); } } } -- GitLab From 6aa2677f844410ecc253720a243ecf2673714cdb Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 15 Jul 2025 16:12:14 +0200 Subject: [PATCH 2/3] Fix: Parsing of RelativeGeometry --- .../datastructure/ImplicitGeometryHolder.java | 42 ++++++++++++++++++- .../datastructure/RelativeGeometry.java | 14 ++++--- .../citygml3/Citygml3FeatureMapper.java | 38 +++++++++++------ .../citydoctor2/parser/CityGmlParser.java | 3 ++ 4 files changed, 79 insertions(+), 18 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java index 872725ee..6a01fa84 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java @@ -1,8 +1,13 @@ package de.hft.stuttgart.citydoctor2.datastructure; import java.io.Serial; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.core.ImplicitGeometry; import de.hft.stuttgart.citydoctor2.math.TransformationMatrix; @@ -16,9 +21,11 @@ public class ImplicitGeometryHolder extends Geometry { @Serial private static final long serialVersionUID = -8938931081577196349L; + private static final Logger logger = LogManager.getLogger(ImplicitGeometryHolder.class); - private ImplicitGeometry cgmlImplicitGeometry = null; + private ImplicitGeometry cgmlImplicitGeometry = null; + private static ConcurrentHashMap deferredRelGeomObjects = new ConcurrentHashMap<>(); private final PrototypeGeometryType type; private final Geometry prototypeGeometry; @@ -38,7 +45,40 @@ public class ImplicitGeometryHolder extends Geometry { applyTransformation(); } + public static ImplicitGeometryHolder deferRelativeGeomParsing(ImplicitGeometry ig, Lod lod) { + ImplicitGeometryHolder igh = new ImplicitGeometryHolder(lod); + deferredRelGeomObjects.put(igh, ig); + return igh; + } + + public static void resolveDeferredObjects() { + Set missingGeoms = new HashSet<>(); + for (ImplicitGeometryHolder key : deferredRelGeomObjects.keySet()) { + ImplicitGeometry ig = deferredRelGeomObjects.get(key); + String hrefId = ig.getRelativeGeometry().getHref(); + hrefId = hrefId.startsWith("#") ? hrefId.substring(1) : hrefId; + GmlId gmlId = new GmlId(hrefId); + RelativeGeometry relGeom = RelativeGeometry.of(gmlId); + if (relGeom != null) { + ImplicitGeometryHolder igh = new ImplicitGeometryHolder(ig, relGeom); + CityObject parent = key.getParent(); + parent.removeGeometry(igh.getLod(), igh.getType()); + parent.addGeometry(igh); + } else { + missingGeoms.add(gmlId); + } + } + if (!missingGeoms.isEmpty()) { + logger.warn("RelativeGeometries of the following gmlIDs could not be resolved: {}", missingGeoms); + } + deferredRelGeomObjects.clear(); + } + private ImplicitGeometryHolder(Lod lod) { + super(null, lod, null); + type = null; + prototypeGeometry = new Geometry(null, lod, null); + } /** * Applies the transformation matrix of the implicit geometry to a copy of the reference geometry and copies the resulting * transformed geometry into this object. diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java index bbf61550..48f8ccd4 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java @@ -14,19 +14,23 @@ public class RelativeGeometry extends Geometry { @Serial private static final long serialVersionUID = -686112245455298977L; - private static Map relativeGeometries = new ConcurrentHashMap<>(); + private static final Map relativeGeometries = new ConcurrentHashMap<>(); - public static RelativeGeometry of(Geometry geom) { - if (relativeGeometries.containsKey(geom)) { - return relativeGeometries.get(geom); + public static RelativeGeometry of(GmlId gmlId, Geometry geom) { + if (relativeGeometries.containsKey(gmlId)) { + return relativeGeometries.get(gmlId); } RelativeGeometry relGeo = new RelativeGeometry(geom.getType(), geom.getLod(), geom.getOrientation()); geom.getPolygons().forEach(relGeo::addPolygon); relGeo.updateEdgesAndVertices(); - relativeGeometries.put(geom, relGeo); + relativeGeometries.put(gmlId, relGeo); return relGeo; } + public static RelativeGeometry of(GmlId gmlId) { + return relativeGeometries.getOrDefault(gmlId, null); + } + private RelativeGeometry(GeometryType type, Lod lod, Orientation orientation) { super(type, lod, orientation); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index e5f5c543..ade1f2bb 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -1035,19 +1035,33 @@ public class Citygml3FeatureMapper extends ObjectWalker { } } else if (ig.getRelativeGeometry() != null) { AbstractGeometry aGeom = ig.getRelativeGeometry().getObject(); - Geometry geom = null; Lod lod = Lod.values()[lodInt]; - RelativeGeometry relGeo = null; - if (aGeom instanceof MultiSurface ms) { - geom = parseMultiSurface(ms, lod); - } else if (aGeom instanceof Solid s) { - geom = parseSolid(s, lod, orientation); - } else if (aGeom instanceof CompositeSurface cs) { - geom = parseCompositeSurface(cs, lod); - } - if (geom != null) { - relGeo = RelativeGeometry.of(geom); - igh = new ImplicitGeometryHolder(ig, relGeo); + if (aGeom != null) { + Geometry geom = null; + if (aGeom instanceof MultiSurface ms) { + geom = parseMultiSurface(ms, lod); + } else if (aGeom instanceof Solid s) { + geom = parseSolid(s, lod, orientation); + } else if (aGeom instanceof CompositeSurface cs) { + geom = parseCompositeSurface(cs, lod); + } + if (geom != null) { + geom.setGmlId(new GmlId(aGeom.getId())); + RelativeGeometry relGeo = RelativeGeometry.of(geom.getGmlId(), geom); + igh = new ImplicitGeometryHolder(ig, relGeo); + } + } else if (ig.getRelativeGeometry().getHref() != null) { + String hrefId = ig.getRelativeGeometry().getHref(); + hrefId = hrefId.startsWith("#") ? hrefId.substring(1) : hrefId; + GmlId gmlId = new GmlId(hrefId); + RelativeGeometry relGeo = RelativeGeometry.of(gmlId); + if (relGeo == null) { + igh = ImplicitGeometryHolder.deferRelativeGeomParsing(ig, lod); + } else { + igh = new ImplicitGeometryHolder(ig, relGeo); + } + } else { + logger.warn("Relative geometry of GML-ID {} has neither geometry nor an href", ig.getId()); } } else { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 06d42a00..e8ec04fe 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -44,6 +44,7 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; +import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -528,6 +529,8 @@ public class CityGmlParser { logger.info(Localization.getText("CityGmlParser.parsedObjects"), mapper.getModel().getNumberOfFeatures()); } + // After parsing is complete, try to resolve deferred RelativeGeometries + ImplicitGeometryHolder.resolveDeferredObjects(); mapper.setCityGMLVersion(version); return mapper.getModel(); } -- GitLab From 69907f4906664ae7d944d33c894c657e4810576b Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 16 Jul 2025 10:10:26 +0200 Subject: [PATCH 3/3] Refactor: Rework BBox calculation using Visitors --- .../datastructure/CityDoctorModel.java | 13 +++++ .../datastructure/GenericAttribute.java | 18 ++++++ .../utils/BoundingBoxCalculator.java | 21 ++----- .../utils/visitors/MinMaxExtentVisitor.java | 57 +++++++++++++++++++ 4 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/visitors/MinMaxExtentVisitor.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 41b2b003..0f475c44 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -20,6 +20,7 @@ package de.hft.stuttgart.citydoctor2.datastructure; import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.writer.CityGMLWriterUtils; @@ -121,6 +122,18 @@ public class CityDoctorModel { tunnels.stream(), water.stream(), cityfurniture.stream(), genericObjects.stream()).flatMap(co -> co); } + public void accept(CheckableVisitor visitor) { + buildings.forEach(co -> co.accept(visitor)); + vegetation.forEach(co -> co.accept(visitor)); + bridges.forEach(co -> co.accept(visitor)); + land.forEach(co -> co.accept(visitor)); + roads.forEach(co -> co.accept(visitor)); + tunnels.forEach(co -> co.accept(visitor)); + water.forEach(co -> co.accept(visitor)); + cityfurniture.forEach(co -> co.accept(visitor)); + genericObjects.forEach(co -> co.accept(visitor)); + } + public void saveAs(String file, boolean saveQualityAde) throws CityDoctorWriteException { if (file.endsWith(".off")) { exportAsOff(file); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java index 041c688b..cd8cec53 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java @@ -5,6 +5,8 @@ import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.core.AbstractGenericAttributeProperty; import org.citygml4j.core.model.generics.*; +import java.util.List; + public class GenericAttribute { @@ -45,6 +47,22 @@ public class GenericAttribute { } else if (attributeProperty.getObject() instanceof CodeAttribute ca) { type = String.format("CodeAttribute (%s)", ca.getValue().getLanguage()); value = String.format("'''%s''' %n CodeSpace: %s", ca.getValue().getValue(), ca.getValue().getCodeSpace()); + } else if (attributeProperty.getObject() instanceof GenericAttributeSet gas) { + type = "GenericAttributeSet"; + List attributes = gas.getValue(); + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (AbstractGenericAttributeProperty attribute : attributes) { + if (attribute.getObject() != null) { + sb.append(attribute.getObject().getName()); + sb.append(" = "); + sb.append(attribute.getObject().getValue()); + sb.append("; "); + } + } + sb.replace(sb.length() - 1, sb.length(), "}"); + value = String.format("'%s'", sb); + } else { logger.warn("GenericAttribute {} is of unknown type {}", attributeProperty, attributeProperty.getObject()); value = attributeProperty.getObject().getValue().toString(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java index 78acc1d0..80bace6d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java @@ -29,6 +29,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector; +import de.hft.stuttgart.citydoctor2.utils.visitors.MinMaxExtentVisitor; /** * Utility class for calculating axis aligned bounding boxes for different @@ -96,23 +97,11 @@ public class BoundingBoxCalculator { * @return the bounding box of the model */ public static BoundingBox calculateBoundingBox(CityDoctorModel model) { - Vector3d low = new Vector3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); - Vector3d high = new Vector3d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); + // TODO: Rework to Visitor + MinMaxExtentVisitor mima = new MinMaxExtentVisitor(); + model.accept(mima); - findMinMax(low, high, model.getBuildings()); - findMinMax(low, high, model.getBridges()); - findMinMax(low, high, model.getLand()); - findMinMax(low, high, model.getTransportation()); - findMinMax(low, high, model.getWater()); - findMinMax(low, high, model.getVegetation()); - findMinMax(low, high, model.getTunnels()); - findMinMax(low, high, model.getCityFurniture()); - findMinMax(low, high, model.getGenericCityObjects()); - - Vector3d[] result = new Vector3d[2]; - result[0] = low; - result[1] = high; - return BoundingBox.of(result); + return BoundingBox.of(mima.getMinMaxExtent()); } public static BoundingBox calculateBoundingBoxFromPoints(List points) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/visitors/MinMaxExtentVisitor.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/visitors/MinMaxExtentVisitor.java new file mode 100644 index 00000000..8ba58ac0 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/visitors/MinMaxExtentVisitor.java @@ -0,0 +1,57 @@ +package de.hft.stuttgart.citydoctor2.utils.visitors; + +import de.hft.stuttgart.citydoctor2.check.Checkable; +import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.Geometry; +import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import de.hft.stuttgart.citydoctor2.math.Vector3d; + +public class MinMaxExtentVisitor extends CheckableUtilsVisitor { + + Vector3d low; + Vector3d high; + + public MinMaxExtentVisitor() { + low = new Vector3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + high = new Vector3d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); + } + + public Vector3d[] getMinMaxExtent() { + Vector3d[] result = new Vector3d[2]; + result[0] = low; + result[1] = high; + return result; + } + + @Override + public void check(CityObject co) { + for (Geometry geom : co.getGeometries()) { + if (geom.getVertices() == null) { + geom.updateVertices(); + } + for (Vertex v : geom.getVertices()) { + if (v.getX() < low.getX()) { + low.setX(v.getX()); + } + if (v.getX() > high.getX()) { + high.setX(v.getX()); + } + if (v.getY() < low.getY()) { + low.setY(v.getY()); + } + if (v.getY() > high.getY()) { + high.setY(v.getY()); + } + if (v.getZ() < low.getZ()) { + low.setZ(v.getZ()); + } + if (v.getZ() > high.getZ()) { + high.setZ(v.getZ()); + } + } + } + } + +} -- GitLab