/******************************************************************************** * * * This file is part of IfcOpenShell. * * * * IfcOpenShell is free software: you can redistribute it and/or modify * * it under the terms of the Lesser GNU General Public License as published by * * the Free Software Foundation, either version 3.0 of the License, or * * (at your option) any later version. * * * * IfcOpenShell 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 * * Lesser GNU General Public License for more details. * * * * You should have received a copy of the Lesser GNU General Public License * * along with this program. If not, see . * * * ********************************************************************************/ #include "mapping.h" #define mapping POSTFIX_SCHEMA(mapping) using namespace ifcopenshell::geometry; #include "../profile_helper.h" taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCenterLineProfileDef* inst) { const double d = inst->Thickness() * length_unit_ / 2.; const double eps = settings_.get().get(); if (d < eps) { logger_.Warning("GEO", 403, "Thickness below precision for:", inst); return nullptr; } auto mapped = map(inst->Curve()); if (!mapped) { return nullptr; } auto crv = taxonomy::dcast(mapped); if (!crv || crv->children.empty()) { logger_.Warning("GEO", 404, "Unsupported centerline curve for:", inst); return nullptr; } if (!crv->is_polyhedron()) { logger_.Warning("GEO", 404, "Unsupported curved centerline segments for:", inst); return nullptr; } std::vector pts; pts.reserve(crv->children.size() + 1); for (auto& e : crv->children) { if (e->start.which() != 1 || e->end.which() != 1) { logger_.Warning("GEO", 404, "Unsupported parametric trims on centerline for:", inst); return nullptr; } Eigen::Vector2d p = boost::get(e->start)->ccomponents().head<2>(); Eigen::Vector2d q = boost::get(e->end)->ccomponents().head<2>(); if (pts.empty()) { pts.push_back(p); } else if ((pts.back() - p).norm() > eps) { logger_.Warning("GEO", 405, "Unsupported discontinuous centerline for:", inst); return nullptr; } pts.push_back(q); } if (pts.size() < 2 || (pts.front() - pts.back()).norm() < eps) { logger_.Warning("GEO", 405, "Unsupported closed or degenerate centerline for:", inst); return nullptr; } const size_t n = pts.size(); std::vector normals(n - 1); for (size_t i = 0; i < n - 1; ++i) { Eigen::Vector2d t = pts[i + 1] - pts[i]; const double l = t.norm(); if (l < eps) { logger_.Warning("GEO", 405, "Degenerate centerline segment for:", inst); return nullptr; } t /= l; normals[i] = Eigen::Vector2d(-t.y(), t.x()); } // Straight miter joins at the vertices, because the profile prescribes a // constant thickness along the curve, which the arc joins of a generic // offset algorithm would violate. std::vector miters(n); miters.front() = normals.front(); miters.back() = normals.back(); for (size_t i = 1; i < n - 1; ++i) { const double denom = 1. + normals[i - 1].dot(normals[i]); if (denom < 1.e-9) { logger_.Warning("GEO", 405, "Centerline reverses onto itself for:", inst); return nullptr; } miters[i] = (normals[i - 1] + normals[i]) / denom; } std::vector polygon; polygon.reserve(2 * n); for (size_t i = 0; i < n; ++i) { polygon.push_back(pts[i] - d * miters[i]); } for (size_t i = n; i-- > 0;) { polygon.push_back(pts[i] + d * miters[i]); } double twice_area = 0.; for (size_t i = 0; i < polygon.size(); ++i) { const auto& a = polygon[i]; const auto& b = polygon[(i + 1) % polygon.size()]; twice_area += a.x() * b.y() - b.x() * a.y(); } if (twice_area < 0.) { std::reverse(polygon.begin(), polygon.end()); } std::vector ps; ps.reserve(polygon.size() + 1); for (auto& p : polygon) { ps.push_back(taxonomy::make(p.x(), p.y(), 0.)); } ps.push_back(ps.front()); auto loop = polygon_from_points(ps); auto face = taxonomy::make(); face->children = { loop }; return face; }