bool circularEqual(const Point3dVector& points1, const Point3dVector& points2, double tol) { unsigned N = points1.size(); if (N != points2.size()){ return false; } if (N == 0){ return true; } bool result = false; // look for a common starting point for (unsigned i = 0; i < N; ++i){ if (getDistance(points1[0], points2[i]) <= tol){ result = true; // check all other points for (unsigned j = 0; j < N; ++j){ if (getDistance(points1[j], points2[(i + j) % N]) > tol){ result = false; break; } } } if (result){ return result; } } return result; }
/// compute centroid from surface as Point3dVector OptionalPoint3d getCentroid(const Point3dVector& points) { OptionalPoint3d result; if (points.size() >= 3){ // convert to face coordinates Transformation alignFace = Transformation::alignFace(points); Point3dVector surfacePoints = alignFace.inverse()*points; unsigned N = surfacePoints.size(); double A = 0; double cx = 0; double cy = 0; for (unsigned i = 0; i < N; ++i){ double x1, x2, y1, y2; if (i == N-1){ x1 = surfacePoints[i].x(); x2 = surfacePoints[0].x(); y1 = surfacePoints[i].y(); y2 = surfacePoints[0].y(); }else{ x1 = surfacePoints[i].x(); x2 = surfacePoints[i+1].x(); y1 = surfacePoints[i].y(); y2 = surfacePoints[i+1].y(); } double dA = (x1*y2-x2*y1); A += 0.5*dA; cx += (x1+x2)*dA; cy += (y1+y2)*dA; } if (A > 0){ // centroid in face coordinates Point3d surfaceCentroid(cx/(6.0*A), cy/(6.0*A), 0.0); // centroid result = alignFace*surfaceCentroid; } } return result; }
TEST(Radiance, ForwardTranslator_SurfaceOnlyOnGround) { Model model; Building building = model.getUniqueModelObject<Building>(); Space space(model); Point3dVector vertices; vertices.push_back(Point3d(0,0,0)); vertices.push_back(Point3d(1,0,0)); vertices.push_back(Point3d(1,1,0)); vertices.push_back(Point3d(0,1,0)); Surface surface(vertices, model); surface.setSpace(space); Point3dVector polygon = ForwardTranslator::getPolygon(surface); EXPECT_EQ(vertices.size(), polygon.size()); BOOST_FOREACH(const Point3d& vertex, polygon){ LOG_FREE(::Info, "Radiance", vertex); }
TEST_F(ModelFixture, IlluminanceMap_SpaceSetTransformation) { Model model; Space space(model); IlluminanceMap map(model); map.setXLength(2); map.setYLength(2); map.setNumberofXGridPoints(2); map.setNumberofYGridPoints(2); map.setOriginXCoordinate(1); map.setOriginYCoordinate(0); map.setOriginZCoordinate(2); map.setSpace(space); Point3dVector testPoints = map.referencePoints(); ASSERT_EQ(4u, testPoints.size()); EXPECT_DOUBLE_EQ(0, testPoints[0].x()); EXPECT_DOUBLE_EQ(0, testPoints[0].y()); EXPECT_DOUBLE_EQ(0, testPoints[0].z()); EXPECT_DOUBLE_EQ(2, testPoints[3].x()); EXPECT_DOUBLE_EQ(2, testPoints[3].y()); EXPECT_DOUBLE_EQ(0, testPoints[3].z()); testPoints = space.transformation()*map.transformation()*map.referencePoints(); EXPECT_DOUBLE_EQ(1, testPoints[0].x()); EXPECT_DOUBLE_EQ(0, testPoints[0].y()); EXPECT_DOUBLE_EQ(2, testPoints[0].z()); EXPECT_DOUBLE_EQ(3, testPoints[3].x()); EXPECT_DOUBLE_EQ(2, testPoints[3].y()); EXPECT_DOUBLE_EQ(2, testPoints[3].z()); EXPECT_TRUE(space.setTransformation(Transformation::translation(Vector3d(1,0,0)))); testPoints = space.transformation()*map.transformation()*map.referencePoints(); EXPECT_DOUBLE_EQ(2, testPoints[0].x()); EXPECT_DOUBLE_EQ(0, testPoints[0].y()); EXPECT_DOUBLE_EQ(2, testPoints[0].z()); EXPECT_DOUBLE_EQ(4, testPoints[3].x()); EXPECT_DOUBLE_EQ(2, testPoints[3].y()); EXPECT_DOUBLE_EQ(2, testPoints[3].z()); EXPECT_TRUE(space.setTransformation(Transformation::translation(Vector3d(1,0,0))*Transformation::rotation(Vector3d(0,0,1),-openstudio::degToRad(90)))); testPoints = space.transformation()*map.transformation()*map.referencePoints(); EXPECT_DOUBLE_EQ(1, testPoints[0].x()); EXPECT_DOUBLE_EQ(-1, testPoints[0].y()); EXPECT_DOUBLE_EQ(2, testPoints[0].z()); EXPECT_DOUBLE_EQ(3, testPoints[3].x()); EXPECT_DOUBLE_EQ(-3, testPoints[3].y()); EXPECT_DOUBLE_EQ(2, testPoints[3].z()); }
// compute Newall vector from Point3dVector, direction is same as outward normal // magnitude is twice the area OptionalVector3d getNewallVector(const Point3dVector& points) { OptionalVector3d result; unsigned N = points.size(); if (N >= 3){ Vector3d vec; for (unsigned i = 1; i < N-1; ++i){ Vector3d v1 = points[i] - points[0]; Vector3d v2 = points[i+1] - points[0]; vec += v1.cross(v2); } result = vec; } return result; }
/// reorder points to upper-left-corner convention Point3dVector reorderULC(const Point3dVector& points) { unsigned N = points.size(); if (N < 3){ return Point3dVector(); } // transformation to align face Transformation t = Transformation::alignFace(points); Point3dVector facePoints = t.inverse()*points; // find ulc index in face coordinates double maxY = std::numeric_limits<double>::min(); double minX = std::numeric_limits<double>::max(); unsigned ulcIndex = 0; for(unsigned i = 0; i < N; ++i){ OS_ASSERT(std::abs(facePoints[i].z()) < 0.001); if ((maxY < facePoints[i].y()) || ((maxY < facePoints[i].y() + 0.00001) && (minX > facePoints[i].x()))){ ulcIndex = i; maxY = facePoints[i].y(); minX = facePoints[i].x(); } } // no-op if (ulcIndex == 0){ return points; } // create result Point3dVector result; std::copy (points.begin() + ulcIndex, points.end(), std::back_inserter(result)); std::copy (points.begin(), points.begin() + ulcIndex, std::back_inserter(result)); OS_ASSERT(result.size() == N); return result; }
std::vector<Point3d> removeColinear(const Point3dVector& points, double tol) { unsigned N = points.size(); if (N < 3){ return points; } std::vector<Point3d> result; Point3d lastPoint = points[0]; result.push_back(lastPoint); for (unsigned i = 1; i < N; ++i){ Point3d currentPoint = points[i]; Point3d nextPoint = points[0]; if (i < N-1){ nextPoint = points[i+1]; } Vector3d a = (currentPoint - lastPoint); Vector3d b = (nextPoint - currentPoint); // if these fail to normalize we have zero length vectors (e.g. adjacent points) if (a.normalize()){ if (b.normalize()){ Vector3d c = a.cross(b); if (c.length() >= tol){ // cross product is significant result.push_back(currentPoint); lastPoint = currentPoint; }else{ // see if dot product is near -1 double d = a.dot(b); if (d <= -1.0 + tol){ // this is a line reversal result.push_back(currentPoint); lastPoint = currentPoint; } } } } } return result; }
std::vector<std::vector<Point3d> > computeTriangulation(const Point3dVector& vertices, const std::vector<std::vector<Point3d> >& holes, double tol) { std::vector<std::vector<Point3d> > result; // check input if (vertices.size () < 3){ return result; } boost::optional<Vector3d> normal = getOutwardNormal(vertices); if (!normal || normal->z() > -0.999){ return result; } for (const auto& hole : holes){ normal = getOutwardNormal(hole); if (!normal || normal->z() > -0.999){ return result; } } std::vector<Point3d> allPoints; // PolyPartition does not support holes which intersect the polygon or share an edge // if any hole is not fully contained we will use boost to remove all the holes bool polyPartitionHoles = true; for (const std::vector<Point3d>& hole : holes){ if (!within(hole, vertices, tol)){ // PolyPartition can't handle this polyPartitionHoles = false; break; } } if (!polyPartitionHoles){ // use boost to do all the intersections std::vector<std::vector<Point3d> > allFaces = subtract(vertices, holes, tol); std::vector<std::vector<Point3d> > noHoles; for (const std::vector<Point3d>& face : allFaces){ std::vector<std::vector<Point3d> > temp = computeTriangulation(face, noHoles); result.insert(result.end(), temp.begin(), temp.end()); } return result; } // convert input to vector of TPPLPoly std::list<TPPLPoly> polys; TPPLPoly outerPoly; // must be counter-clockwise, input vertices are clockwise outerPoly.Init(vertices.size()); outerPoly.SetHole(false); unsigned n = vertices.size(); for(unsigned i = 0; i < n; ++i){ // should all have zero z coordinate now double z = vertices[n-i-1].z(); if (abs(z) > tol){ LOG_FREE(Error, "utilities.geometry.computeTriangulation", "All points must be on z = 0 plane for triangulation methods"); return result; } Point3d point = getCombinedPoint(vertices[n-i-1], allPoints, tol); outerPoly[i].x = point.x(); outerPoly[i].y = point.y(); } outerPoly.SetOrientation(TPPL_CCW); polys.push_back(outerPoly); for (const std::vector<Point3d>& holeVertices : holes){ if (holeVertices.size () < 3){ LOG_FREE(Error, "utilities.geometry.computeTriangulation", "Hole has fewer than 3 points, ignoring"); continue; } TPPLPoly innerPoly; // must be clockwise, input vertices are clockwise innerPoly.Init(holeVertices.size()); innerPoly.SetHole(true); //std::cout << "inner :"; for(unsigned i = 0; i < holeVertices.size(); ++i){ // should all have zero z coordinate now double z = holeVertices[i].z(); if (abs(z) > tol){ LOG_FREE(Error, "utilities.geometry.computeTriangulation", "All points must be on z = 0 plane for triangulation methods"); return result; } Point3d point = getCombinedPoint(holeVertices[i], allPoints, tol); innerPoly[i].x = point.x(); innerPoly[i].y = point.y(); } innerPoly.SetOrientation(TPPL_CW); polys.push_back(innerPoly); } // do partitioning TPPLPartition pp; std::list<TPPLPoly> resultPolys; int test = pp.Triangulate_EC(&polys,&resultPolys); if (test == 0){ test = pp.Triangulate_MONO(&polys, &resultPolys); } if (test == 0){ LOG_FREE(Error, "utilities.geometry.computeTriangulation", "Failed to partition polygon"); return result; } // convert back to vertices std::list<TPPLPoly>::iterator it, itend; //std::cout << "Start" << std::endl; for(it = resultPolys.begin(), itend = resultPolys.end(); it != itend; ++it){ it->SetOrientation(TPPL_CW); std::vector<Point3d> triangle; for (long i = 0; i < it->GetNumPoints(); ++i){ TPPLPoint point = it->GetPoint(i); triangle.push_back(Point3d(point.x, point.y, 0)); } //std::cout << triangle << std::endl; result.push_back(triangle); } //std::cout << "End" << std::endl; return result; }
TEST_F(ModelFixture, IlluminanceMap_Transformation) { Model model; IlluminanceMap map(model); map.setXLength(2); map.setYLength(2); map.setNumberofXGridPoints(2); map.setNumberofYGridPoints(2); map.setOriginXCoordinate(1); map.setOriginYCoordinate(0); map.setOriginZCoordinate(2); Point3dVector testPoints = map.referencePoints(); ASSERT_EQ(4u, testPoints.size()); EXPECT_NEAR(0, testPoints[0].x(), 0.000001); EXPECT_NEAR(0, testPoints[0].y(), 0.000001); EXPECT_NEAR(0, testPoints[0].z(), 0.000001); EXPECT_NEAR(2, testPoints[3].x(), 0.000001); EXPECT_NEAR(2, testPoints[3].y(), 0.000001); EXPECT_NEAR(0, testPoints[3].z(), 0.000001); testPoints = map.transformation()*map.referencePoints(); EXPECT_NEAR(1, testPoints[0].x(), 0.000001); EXPECT_NEAR(0, testPoints[0].y(), 0.000001); EXPECT_NEAR(2, testPoints[0].z(), 0.000001); EXPECT_NEAR(3, testPoints[3].x(), 0.000001); EXPECT_NEAR(2, testPoints[3].y(), 0.000001); EXPECT_NEAR(2, testPoints[3].z(), 0.000001); Transformation transformation = Transformation::translation(Vector3d(1,0,2)); EXPECT_TRUE(map.setTransformation(transformation)); EXPECT_TRUE(transformation.matrix() == map.transformation().matrix()); testPoints = map.referencePoints(); ASSERT_EQ(4u, testPoints.size()); EXPECT_NEAR(0, testPoints[0].x(), 0.000001); EXPECT_NEAR(0, testPoints[0].y(), 0.000001); EXPECT_NEAR(0, testPoints[0].z(), 0.000001); EXPECT_NEAR(2, testPoints[3].x(), 0.000001); EXPECT_NEAR(2, testPoints[3].y(), 0.000001); EXPECT_NEAR(0, testPoints[3].z(), 0.000001); testPoints = map.transformation()*map.referencePoints(); EXPECT_NEAR(1, testPoints[0].x(), 0.000001); EXPECT_NEAR(0, testPoints[0].y(), 0.000001); EXPECT_NEAR(2, testPoints[0].z(), 0.000001); EXPECT_NEAR(3, testPoints[3].x(), 0.000001); EXPECT_NEAR(2, testPoints[3].y(), 0.000001); EXPECT_NEAR(2, testPoints[3].z(), 0.000001); transformation = Transformation::translation(Vector3d(1,0,2))*Transformation::rotation(Vector3d(0,0,1),-openstudio::degToRad(90)); EXPECT_TRUE(map.setTransformation(transformation)); EXPECT_TRUE(transformation.matrix() == map.transformation().matrix()) << transformation.matrix() << std::endl << map.transformation().matrix(); testPoints = map.referencePoints(); ASSERT_EQ(4u, testPoints.size()); EXPECT_NEAR(0, testPoints[0].x(), 0.000001); EXPECT_NEAR(0, testPoints[0].y(), 0.000001); EXPECT_NEAR(0, testPoints[0].z(), 0.000001); EXPECT_NEAR(2, testPoints[3].x(), 0.000001); EXPECT_NEAR(2, testPoints[3].y(), 0.000001); EXPECT_NEAR(0, testPoints[3].z(), 0.000001); testPoints = map.transformation()*map.referencePoints(); EXPECT_NEAR(1, testPoints[0].x(), 0.000001); EXPECT_NEAR(0, testPoints[0].y(), 0.000001); EXPECT_NEAR(2, testPoints[0].z(), 0.000001); EXPECT_NEAR(3, testPoints[3].x(), 0.000001); EXPECT_NEAR(-2, testPoints[3].y(), 0.000001); EXPECT_NEAR(2, testPoints[3].z(), 0.000001); }