/*- * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * * This file is part of CityDoctor2. * * CityDoctor2 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. * * CityDoctor2 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 CityDoctor2. If not, see . */ package de.hft.stuttgart.citydoctor2.checks.semantics; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import de.hft.stuttgart.citydoctor2.check.Check; import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.RequirementType; import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.error.NotWallError; import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; /** * checks if the normal direction of a polygon labeled as WallSurface are * horizontal or not. * * @author dwagner * @author Matthias Betz - 12bema1bif@hft-stuttgart.de * */ public class IsWallCheck extends Check { private static final String LOWER_ANGLE_NAME = "lowerAngle"; private static final String UPPER_ANGLE_NAME = "upperAngle"; private static final List dependencies; static { ArrayList deps = new ArrayList<>(); deps.add(CheckId.C_GE_R_TOO_FEW_POINTS); deps.add(CheckId.C_GE_R_NOT_CLOSED); deps.add(CheckId.C_GE_R_DUPLICATE_POINT); deps.add(CheckId.C_GE_R_SELF_INTERSECTION); deps.add(CheckId.C_GE_P_NON_PLANAR); dependencies = Collections.unmodifiableList(deps); } private static final Vector3d Z_AXIS = new Vector3d(0, 0, 1); // The SIG3d handbook proposes a +-45 degree to the horizontal plane. To // minimize computation the upper and lower values are calculated only once // and in advance. The algorithm is based on the angle calculation between // the normal of the polygon and the positive z direction vector. private double lowerAngleCos = Math.cos(45 * Math.PI / 180); private double upperAngleCos = Math.cos(135 * Math.PI / 180); @Override public void init(Map params, ParserConfiguration config) { String lowerAngleString = params.get(LOWER_ANGLE_NAME); if (lowerAngleString != null) { lowerAngleCos = Double.parseDouble(lowerAngleString); lowerAngleCos = Math.cos(lowerAngleCos * Math.PI / 180); } String upperAngleString = params.get(UPPER_ANGLE_NAME); if (upperAngleString != null) { upperAngleCos = Double.parseDouble(upperAngleString); upperAngleCos = Math.cos(upperAngleCos * Math.PI / 180); } } @Override public void check(BoundarySurface bs) { // works only on wall surfaces if (bs.getType() != BoundarySurfaceType.WALL) { return; } for (Geometry geom : bs.getGeometries()) { for (Polygon p : geom.getPolygons()) { Vector3d normal = p.calculateNormal(); double dot = normal.dot(Z_AXIS); // The angle should be between 45 and 135 degree, measured // against the z-direction, hence the optimal angle would be 90 degree // CAUTION: This might seems odd, but keep in mind, that the acos won't // be calculated and we won't receive measures in radian or degree. // The values for cosine are: // cos [0°, 90°) > 0 // cos ( 90° ) = 0 // cos ( 90°, 180°] < 0 // Hence we need to check if the calculated angle is above the lower // angle cosine or below the upper angle cosine if (dot > lowerAngleCos || dot < upperAngleCos) { NotWallError err = new NotWallError(bs, p, normal); CheckResult cr = new CheckResult(this, ResultStatus.ERROR, err); bs.addCheckResult(cr); return; } } } CheckResult cr = new CheckResult(this, ResultStatus.OK, null); bs.addCheckResult(cr); } @Override public List getDependencies() { return dependencies; } @Override public Set appliesToRequirements() { return CollectionUtils.singletonSet(Requirement.R_SE_BS_IS_WALL); } @Override public RequirementType getType() { return RequirementType.SEMANTIC; } @Override public Check createNewInstance() { return new IsWallCheck(); } @Override public CheckId getCheckId() { return CheckId.C_SE_BS_IS_WALL; } }