Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geometry improvements #4221

Merged
merged 90 commits into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
cc3a0b7
Polygon3d class with various useful methods (area etc). join method t…
ggartside Feb 22, 2021
b160f4b
Fnishes joinAll for Polygon3d. Added uniut test to repro issue 1614 a…
ggartside Feb 22, 2021
c8b56ff
Some basic Polygon3d unit tests in Geometry
ggartside Feb 22, 2021
00f711b
Merge branch 'develop' into Geometry_Improvements
ggartside Feb 22, 2021
5d1d00f
Changed the names of class methods for Polygon3d to match naming con…
ggartside Feb 24, 2021
2df7b7e
Added joinAllPolygons method that takes Point3dVectors as arguments a…
ggartside Feb 24, 2021
08b8946
Fixes an accidenmtal compile error
ggartside Feb 24, 2021
784f028
Fixes a compile error reported in the pull request
ggartside Feb 24, 2021
f2b8d95
Fixing cppcheck/clang format issues
ggartside Feb 24, 2021
3ae5b31
clang-format (with clang-format-12)
jmarrec Feb 25, 2021
5550a1d
Fix some cppcheck errors
jmarrec Feb 25, 2021
ea25e0e
These are reported as cppcheck style warnings (unused variables). Con…
jmarrec Feb 25, 2021
78c96eb
Renamed more methods to conform to coding standards (removed get prefix)
ggartside Feb 25, 2021
ced8117
joinAllPolygons was broken by a rename
ggartside Feb 25, 2021
8b86198
Update src/utilities/geometry/Polygon.cpp to use size_t to avoid -Wer…
jmarrec Feb 25, 2021
3ac5f46
adding joinAllWithBuffer
ggartside Mar 8, 2021
d234a47
Merge differences from recent pull
ggartside Mar 8, 2021
3371648
Updated comments, added new Polygon3d constructor. Hoping thios one g…
ggartside Mar 8, 2021
e004637
Merge remote-tracking branch 'origin/develop' into Geometry_Improvements
jmarrec Mar 9, 2021
15db807
Lint geometry_improvements and use const refs + rename to NewallVector
jmarrec Mar 9, 2021
5f7742b
Merge pull request #4238 from NREL/geometry_improvements_mod
ggartside Mar 9, 2021
d2a075f
Adds method removeSpikesEx. This method uses buffer to shrink and exp…
ggartside Mar 9, 2021
362bb8d
Merges joinAllWithBuffer branch (2527)
ggartside Mar 9, 2021
dca4325
Removes unused variables that are causing buildind fails
ggartside Mar 11, 2021
8b5e98a
One last effort to fix these warnings
ggartside Mar 12, 2021
212aea6
Added RemoveSpikesAndOverlaps test to verify removeSpikes fixes issue…
ggartside Mar 15, 2021
9d3b85f
Suppress cppcheck warnings
jmarrec Mar 16, 2021
947ef2e
Fix build error. The OSM file(s) referenced isn't in source control t…
jmarrec Mar 16, 2021
b68c10a
Fix build error in Intersection_GTest due to unused variables
jmarrec Mar 16, 2021
51d5648
Fix more issues in Space_GTest
jmarrec Mar 16, 2021
9dfc54a
Give an example of how to register and use test OSM(s) for use in test.
jmarrec Mar 16, 2021
94f4521
clang format [chore]
jmarrec Mar 16, 2021
fe52006
Merge remote-tracking branch 'upstream/develop' into Geometry_Improve…
jmarrec Mar 16, 2021
d7ff466
Adds stripped down test for removing spikes where process was stuck i…
ggartside Mar 19, 2021
7c2cb9a
Added overlap method to Polygon3d. Created Polygon3d_overlap unit test
ggartside Mar 22, 2021
1553650
Create example of selectively adding tests for windows only
kbenne Mar 22, 2021
ac2580c
Adds a unit test for calculating exposed perimeter
ggartside Mar 22, 2021
7060587
Adds exterior perimeter to Building and space
ggartside Mar 23, 2021
b19f89d
Renamed Space.exposedPerimeter added call to ExposedPerimeter test
ggartside Mar 23, 2021
4d95d0f
Using unused variable result2
ggartside Mar 23, 2021
82ee291
Merge branch 'Geometry_Improvements' of https://github.com/NREL/OpenS…
ggartside Mar 23, 2021
260901d
Polygon.overlap, Building.exteriorPerimeter, Space.exposedPerimeter, …
ggartside Mar 23, 2021
c6da5c5
Adds a test to repro 1683, adds a test to repro 3982, ensures all Geo…
ggartside Mar 23, 2021
8e3acb3
Removed surface shattering test as it is not in the current scope
ggartside Mar 23, 2021
d52f56f
(Hopefully) removed warnings that translate into errors
ggartside Mar 23, 2021
d9e2a59
Exzclude two tests that rely on model files (one takes 16 minutes to …
ggartside Mar 24, 2021
6785082
clang-format changes
ggartside Mar 24, 2021
08e7d19
Added unit test for 3982 - am not getting any triangulated surfaces b…
ggartside Mar 24, 2021
d01ea5f
Unit test for issue 2560, changes to Geometry.i
ggartside Mar 24, 2021
0e025e9
Test for issue 3982
ggartside Mar 25, 2021
1e00fee
Adds unit test for issue 2560
ggartside Mar 25, 2021
480971b
Fixes to unit tests Issue_3982 and Surface_Intersect_ConcaveSurfaces …
ggartside Mar 25, 2021
e1e1429
Adjust BoundingBox intersection tolerance to 10mm from 1mm.
ggartside Mar 25, 2021
78dc6d8
Renamed Polygon.hpp(cpp) -> Polygon3d.hpp(cpp)
ggartside Mar 25, 2021
b624855
Relaxes the tolerances for the area check in Surface::comupteIntersec…
ggartside Mar 26, 2021
dba1300
Merge branch 'GeometryShared' into Geometry_Improvements
ggartside Mar 26, 2021
3d768ef
Fixes a build/merge issue
ggartside Mar 26, 2021
05a44a9
clang!!!
ggartside Mar 26, 2021
feed490
Fixes issues that are causing Space_intersectSurfaces_degenerate2 to …
ggartside Mar 26, 2021
d17df78
Adds exposedPerimeter for Surface
ggartside Mar 29, 2021
b5657e1
Formatting corrections
ggartside Mar 29, 2021
28f842a
Added ruby tests for the geometry improvements
ggartside Mar 31, 2021
8474eb1
Changes geometry.i for polygon3d
ggartside Mar 31, 2021
4f4c7fc
SOm enew tests some updated tests
ggartside Mar 31, 2021
d94a96c
Moved model files to resources/model, updated cmake file
ggartside Mar 31, 2021
0f38555
Removing failing tests
ggartside Mar 31, 2021
bf23615
Fixes error in file name
ggartside Mar 31, 2021
c0e8704
Fixing a missing swig type optional Point3dVectorVector
ggartside Apr 5, 2021
33ae7be
Merge branch 'develop' into Geometry_Improvements
jmarrec Apr 7, 2021
5777ebb
Whitespace changes in ruby tests
jmarrec Apr 7, 2021
8a4a5d3
Use correct name for `7-7_Windows_Complete.osm`
jmarrec Apr 7, 2021
ddf711b
Adjust and disable via commenting all saving of output (which is for …
jmarrec Apr 7, 2021
fb25ab9
Infer where to save, but also disable saving models in ruby test (thi…
jmarrec Apr 7, 2021
ef3201b
Some cleanup in comments
jmarrec Apr 7, 2021
52daa4f
Space_Gtest: use EXPECT_ insert of ASSERT_ where possible
jmarrec Apr 7, 2021
0ba9769
Space_Gtest: Remove macro code: enable tests on other than win32. For…
jmarrec Apr 7, 2021
2f1fa96
change model version back down to 3.1.0 (or they won't load, 3.1.1 is…
jmarrec Apr 7, 2021
284002c
Move disabled tests at end. surface shattering fails (logically), the…
jmarrec Apr 7, 2021
ac3d471
TEMPORARY: change jenkinsfile to point to the geometry_improvements b…
jmarrec Apr 7, 2021
c32ba21
output log file in a folder that exists.
jmarrec Apr 7, 2021
ad99016
Use const ref where possible, remove unused variables, break one-line…
jmarrec Apr 7, 2021
0be4881
Change comments in Surface by adjusting units from mm2 to cm2 as I th…
jmarrec Apr 7, 2021
8c07f5a
Use std::abs(float) instead of cstdlib abs(int)
jmarrec Apr 7, 2021
eed6954
Remove kyle's example of selectively adding test files in CMakeLists.txt
jmarrec Apr 7, 2021
f7ca31e
Merge pull request #4275 from NREL/Geometry_improvements_review
tijcolem Apr 7, 2021
1063eb0
Added method to create a test model for perimeter calculations, rempv…
ggartside Apr 8, 2021
7efb19f
Merge develop to branch
ggartside Apr 9, 2021
26ee1fc
Resolved merge conflicts
ggartside Apr 9, 2021
fa14e8a
Fixed clan clanger (all clang did was remove an extra blank line from…
ggartside Apr 9, 2021
debd914
Added ruby scripts that create the floorplan_school model via the api
ggartside Apr 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/utilities/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ set(geometry_src
geometry/Transformation.cpp
geometry/Vector3d.hpp
geometry/Vector3d.cpp
geometry/Polygon.hpp
geometry/Polygon.cpp
../polypartition/polypartition.cpp
)

Expand Down
3 changes: 3 additions & 0 deletions src/utilities/geometry/Geometry.i
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using namespace openstudio;
#include <utilities/geometry/Vector3d.hpp>
#include <utilities/geometry/Point3d.hpp>
#include <utilities/geometry/Polygon.hpp>
#include <utilities/geometry/PointLatLon.hpp>
#include <utilities/geometry/Plane.hpp>
#include <utilities/geometry/EulerAngles.hpp>
Expand Down Expand Up @@ -51,6 +52,7 @@
%template(OptionalThreeGeometry) boost::optional<openstudio::ThreeGeometry>;
%template(OptionalFloorplanJS) boost::optional<openstudio::FloorplanJS>;
%template(OptionalFloorplanObject) boost::optional<openstudio::FloorplanObject>;
%template(OptionalPolygon3d) boost::optional<openstudio::Polygon3d>;

// create an instantiation of the vector classes
// Note JM 2019-04-16: No need to ignore std::vector<T>::vector/resize when you have a default constructor
Expand Down Expand Up @@ -100,6 +102,7 @@
%include <utilities/geometry/Intersection.hpp>
%include <utilities/geometry/ThreeJS.hpp>
%include <utilities/geometry/FloorplanJS.hpp>
%include <utilities/geometry/Polygon.hpp>

%extend openstudio::Vector3d{
std::string __str__() const {
Expand Down
180 changes: 180 additions & 0 deletions src/utilities/geometry/Intersection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1054,4 +1054,184 @@ std::vector<Point3d> simplify(const std::vector<Point3d>& vertices, bool removeC
return result;
}

/// <summary>
/// Converts a Polygon to a BoostPolygon
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
boost::optional<BoostPolygon> BoostPolygonFromPolygon(const Polygon3d& polygon) {
BoostPolygon boostPolygon;

for (const Point3d& vertex : polygon.getOuterPath()) {

// should all have zero z coordinate now
//double z = vertex.z();
//if (abs(z) > tol) {
// LOG_FREE(Error, "utilities.geometry.boostPolygonFromVertices", "All points must be on z = 0 plane");
// return boost::none;
//}
boost::geometry::append(boostPolygon, boost::make_tuple(vertex.x(), vertex.y()));
}

const Point3dVector& path = polygon.getOuterPath();
const Point3d& first = path.front();
boost::geometry::append(boostPolygon, boost::make_tuple(first.x(), first.y()));

return boostPolygon;
}

Polygon3d PolygonFromBoostPolygon(const BoostPolygon& boostPolygon) {
Polygon3d p;
BoostRing outer = boostPolygon.outer();
if (outer.empty()) {
return p;
}

Point3dVector points;
for (unsigned i = 0; i < outer.size() - 1; ++i) {
Point3d point3d(outer[i].x(), outer[i].y(), 0.0);
points.push_back(point3d);
}

points = removeCollinearLegacy(points);
for (auto point : points)
p.addPoint(point);

for (auto inner : boostPolygon.inners()) {
Point3dVector hole;
for (unsigned i = 0; i < inner.size() - 1; ++i) {
Point3d point3d(inner[i].x(), inner[i].y(), 0.0);
hole.push_back(point3d);
}
p.addHole(hole);
}

return p;
}

// Non class member stuff
boost::optional<Polygon3d> join(const Polygon3d& polygon1, const Polygon3d& polygon2) {

// Convert polygons to boost polygon (not ring obvs)
boost::optional<BoostPolygon> boostPolygon1 = BoostPolygonFromPolygon(polygon1);
if (!boostPolygon1) {
return boost::none;
}

boost::optional<BoostPolygon> boostPolygon2 = BoostPolygonFromPolygon(polygon2);
if (!boostPolygon2) {
return boost::none;
}

std::vector<BoostPolygon> unionResult;
try {
boost::geometry::union_(*boostPolygon1, *boostPolygon2, unionResult);
} catch (const boost::geometry::overlay_invalid_input_exception&) {
LOG_FREE(Error, "utilities.geometry.join", "overlay_invalid_input_exception");
return boost::none;
}

// Smooth the result
unionResult = removeSpikes(unionResult);

//unionResult = removeSpikesEx(unionResult); // This one will buffer -> and <- buffer when it is written

// Check the result - we do not have to bail for holes but we bail for > 1 poly
if (unionResult.empty()) {
return boost::none;
} else if (unionResult.size() > 1) {
return boost::none;
}

// Convert back to polygon
Polygon3d p = PolygonFromBoostPolygon(unionResult.front());
return p;
}

std::vector<Polygon3d> joinAllPolygons(const std::vector<std::vector<Point3d>>& polygons, double tol) {
std::vector<Polygon3d> inputPolygons;

// CReate Polygon3d from point3dvectors
for (auto polygon : polygons) {
Polygon3d inputPolygon;
for (auto point : polygon) {
inputPolygon.addPoint(point);
}
inputPolygons.push_back(polygon);
}

return joinAll(inputPolygons, tol);
}

std::vector<Polygon3d> joinAll(const std::vector<Polygon3d>& polygons, double tol) {

std::vector<Polygon3d> result;

size_t N = polygons.size();
if (N <= 1) {
return polygons;
}

std::vector<double> polygonAreas(N, 0.0);
for (unsigned i = 0; i < N; ++i) {
auto area = getArea(polygons[i].getOuterPath());
if (area) {
polygonAreas[i] = *area;
}
}

// compute adjacency matrix
Matrix A(N, N, 0.0);
for (unsigned i = 0; i < N; ++i) {
A(i, i) = 1.0;
for (unsigned j = i + 1; j < N; ++j) {
if (join(polygons[i], polygons[j] /*,tol*/)) {
A(i, j) = 1.0;
A(j, i) = 1.0;
}
}
}

std::vector<std::vector<unsigned>> connectedComponents = findConnectedComponents(A);
for (const std::vector<unsigned>& component : connectedComponents) {

std::vector<unsigned> orderedComponent(component);
std::sort(orderedComponent.begin(), orderedComponent.end(), [&polygonAreas](int ia, int ib) { return polygonAreas[ia] > polygonAreas[ib]; });

Polygon3d polygon;
std::set<unsigned> joinedComponents;
// try to join at most component.size() times
for (unsigned n = 0; n < component.size(); ++n) {

// loop over polygons to join in order
for (unsigned i : orderedComponent) {
if (polygon.getOuterPath().empty()) {
polygon = polygons[i];
joinedComponents.insert(i);
} else {
// if not already joined
if (joinedComponents.find(i) == joinedComponents.end()) {
boost::optional<Polygon3d> joined = join(polygon, polygons[i] /*, tol*/);
if (joined) {
polygon = *joined;
joinedComponents.insert(i);
}
}
}
}

// if all polygons have been joined then we are done
if (joinedComponents.size() == component.size()) {
break;
}
}

if (joinedComponents.size() != component.size()) {
LOG_FREE(Error, "utilities.geometry.joinAll", "Could not join all connected components");
}
result.push_back(polygon);
}

return result;
}
} // namespace openstudio
10 changes: 9 additions & 1 deletion src/utilities/geometry/Intersection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
#include "../UtilitiesAPI.hpp"

#include "Point3d.hpp"

#include "Polygon.hpp"
#include <vector>
#include <boost/optional.hpp>

Expand Down Expand Up @@ -80,9 +80,17 @@ UTILITIES_API bool pointInPolygon(const Point3d& point, const std::vector<Point3
/// compute the union of two overlapping polygons, requires that all vertices are in clockwise order on the z = 0 plane (i.e. in face coordinates but reversed)
UTILITIES_API boost::optional<std::vector<Point3d>> join(const std::vector<Point3d>& polygon1, const std::vector<Point3d>& polygon2, double tol);

UTILITIES_API boost::optional<Polygon3d> join(const Polygon3d& polygon1, const Polygon3d& polygon2);

/// compute the union of many polygons, requires that all vertices are in clockwise order on the z = 0 plane (i.e. in face coordinates but reversed)
UTILITIES_API std::vector<std::vector<Point3d>> joinAll(const std::vector<std::vector<Point3d>>& polygons, double tol);

/// compute the union of many polygons, requires that all vertices are in clockwise order on the z = 0 plane (i.e. in face coordinates but reversed)
UTILITIES_API std::vector<Polygon3d> joinAll(const std::vector<Polygon3d>& polygons, double tol);

/// compute the union of many polygons, requires that all vertices are in clockwise order on the z = 0 plane (i.e. in face coordinates but reversed)
UTILITIES_API std::vector<Polygon3d> joinAllPolygons(const std::vector<std::vector<Point3d>>& polygons, double tol);

/// intersect two polygons, requires that all vertices are in clockwise order on the z = 0 plane (i.e. in face coordinates but reversed)
UTILITIES_API boost::optional<IntersectionResult> intersect(const std::vector<Point3d>& polygon1, const std::vector<Point3d>& polygon2, double tol);

Expand Down
147 changes: 147 additions & 0 deletions src/utilities/geometry/Polygon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/***********************************************************************************************************************
* OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer.
*
* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
*
* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products
* derived from this software without specific prior written permission from the respective party.
*
* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works
* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior
* written permission from Alliance for Sustainable Energy, LLC.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED
* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***********************************************************************************************************************/

#include "Polygon.hpp"
#include "Vector3d.hpp"
#include "Geometry.hpp"

namespace openstudio {

Polygon3d::Polygon3d() {}

Polygon3d::Polygon3d(Point3dVector outerPath) {
for (auto p : outerPath)
points.push_back(p);
}

/// <summary>
/// Adds a point to the polygon perimeter
/// </summary>
/// <param name="point"></param>
void Polygon3d::addPoint(Point3d& point) {
points.push_back(point);
}

/// <summary>
/// Sets the perimeter of the polygonb
/// </summary>
/// <param name="perimeter"></param>
void Polygon3d::setOuterPath(Point3dVector outerPath) {
points = outerPath;
}

Point3dVector Polygon3d::getOuterPath() const {
return points;
}

Point3dVectorVector Polygon3d::getInnerPaths() const {
return innerPaths;
}

/// <summary>
/// Adds a hole to the polygon
/// </summary>
/// <param name="hole"></param>
void Polygon3d::addHole(Point3dVector hole) {
innerPaths.push_back(hole);
}

Vector3d Polygon3d::newellVector() {
OptionalVector3d v = openstudio::getNewallVector(points);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of these two is a typo


if (v) {
return v.get();
}

return Vector3d();
}

Vector3d Polygon3d::outwardNormal() {
return openstudio::getOutwardNormal(points).get();
}

double Polygon3d::grossArea() {

boost::optional<double> area = openstudio::getArea(points);
if (area == boost::none)
return 0;
else
return area.get();
return 0;
}

double Polygon3d::netArea() {

double netArea = grossArea();
for (auto hole : innerPaths) {
boost::optional<double> area = openstudio::getArea(hole);
if (area != boost::none) netArea -= area.get();
}

return netArea;
}

double Polygon3d::getPerimeter() {

double perimeter = 0;
for (size_t i = 0; i < points.size(); i++) {
Point3d p1 = points[i];
Point3d p2 = points[(i + 1) % points.size()];
perimeter += openstudio::getDistance(p1, p2);
}

return perimeter;
}

bool Polygon3d::getIsClockwise() {
OptionalVector3d normal = getOutwardNormal(points);
if (normal == boost::none)
return true;
else
return normal.get().z() > 0;
}

Point3d Polygon3d::getCentroid() {
boost::optional p = openstudio::getCentroid(points);
if (p == boost::none)
return Point3d();
else
return p.get();
}

//bool Polygon3d::PointInPolygon(Point3d testPoint) {
// return true;
//}

//typedef boost::geometry::model::polygon<BoostPoint> BoostPolygon;
//typedef boost::geometry::model::d2::point_xy<double> BoostPoint;

// TODO: I want to inlude all the boolean joining etc code here
jmarrec marked this conversation as resolved.
Show resolved Hide resolved

} // namespace openstudio
Loading