// ------------------------------------------------------------------------------------------------ void ConvertAxisPlacement(IfcMatrix4& out, const IfcAxis2Placement& in, ConversionData& conv) { if(const IfcAxis2Placement3D* pl3 = in.ResolveSelectPtr<IfcAxis2Placement3D>(conv.db)) { ConvertAxisPlacement(out,*pl3); } else if(const IfcAxis2Placement2D* pl2 = in.ResolveSelectPtr<IfcAxis2Placement2D>(conv.db)) { ConvertAxisPlacement(out,*pl2); } else { IFCImporter::LogWarn("skipping unknown IfcAxis2Placement entity"); } }
// Extrudes the given polygon along the direction, converts it into an opening or applies all openings as necessary. void ProcessExtrudedArea(const IfcExtrudedAreaSolid& solid, const TempMesh& curve, const IfcVector3& extrusionDir, TempMesh& result, ConversionData &conv, bool collect_openings) { // Outline: 'curve' is now a list of vertex points forming the underlying profile, extrude along the given axis, // forming new triangles. const bool has_area = solid.SweptArea->ProfileType == "AREA" && curve.verts.size() > 2; if( solid.Depth < 1e-6 ) { if( has_area ) { result.Append(curve); } return; } result.verts.reserve(curve.verts.size()*(has_area ? 4 : 2)); result.vertcnt.reserve(curve.verts.size() + 2); std::vector<IfcVector3> in = curve.verts; // First step: transform all vertices into the target coordinate space IfcMatrix4 trafo; ConvertAxisPlacement(trafo, solid.Position); IfcVector3 vmin, vmax; MinMaxChooser<IfcVector3>()(vmin, vmax); BOOST_FOREACH(IfcVector3& v, in) { v *= trafo; vmin = std::min(vmin, v); vmax = std::max(vmax, v); }
// ------------------------------------------------------------------------------------------------ void ProcessParametrizedProfile(const IfcParameterizedProfileDef& def, TempMesh& meshout, ConversionData& conv) { (void)conv; if(const IfcRectangleProfileDef* const cprofile = def.ToPtr<IfcRectangleProfileDef>()) { const IfcFloat x = cprofile->XDim*0.5f, y = cprofile->YDim*0.5f; meshout.verts.reserve(meshout.verts.size()+4); meshout.verts.push_back( IfcVector3( x, y, 0.f )); meshout.verts.push_back( IfcVector3(-x, y, 0.f )); meshout.verts.push_back( IfcVector3(-x,-y, 0.f )); meshout.verts.push_back( IfcVector3( x,-y, 0.f )); meshout.vertcnt.push_back(4); } else if( const IfcCircleProfileDef* const circle = def.ToPtr<IfcCircleProfileDef>()) { if( const IfcCircleHollowProfileDef* const hollow = def.ToPtr<IfcCircleHollowProfileDef>()) { // TODO } const size_t segments = 32; const IfcFloat delta = AI_MATH_TWO_PI_F/segments, radius = circle->Radius; meshout.verts.reserve(segments); IfcFloat angle = 0.f; for(size_t i = 0; i < segments; ++i, angle += delta) { meshout.verts.push_back( IfcVector3( cos(angle)*radius, sin(angle)*radius, 0.f )); } meshout.vertcnt.push_back(segments); } else if( const IfcIShapeProfileDef* const ishape = def.ToPtr<IfcIShapeProfileDef>()) { // construct simplified IBeam shape const IfcFloat offset = (ishape->OverallWidth - ishape->WebThickness) / 2; const IfcFloat inner_height = ishape->OverallDepth - ishape->FlangeThickness * 2; meshout.verts.reserve(12); meshout.verts.push_back(IfcVector3(0,0,0)); meshout.verts.push_back(IfcVector3(0,ishape->FlangeThickness,0)); meshout.verts.push_back(IfcVector3(offset,ishape->FlangeThickness,0)); meshout.verts.push_back(IfcVector3(offset,ishape->FlangeThickness + inner_height,0)); meshout.verts.push_back(IfcVector3(0,ishape->FlangeThickness + inner_height,0)); meshout.verts.push_back(IfcVector3(0,ishape->OverallDepth,0)); meshout.verts.push_back(IfcVector3(ishape->OverallWidth,ishape->OverallDepth,0)); meshout.verts.push_back(IfcVector3(ishape->OverallWidth,ishape->FlangeThickness + inner_height,0)); meshout.verts.push_back(IfcVector3(offset+ishape->WebThickness,ishape->FlangeThickness + inner_height,0)); meshout.verts.push_back(IfcVector3(offset+ishape->WebThickness,ishape->FlangeThickness,0)); meshout.verts.push_back(IfcVector3(ishape->OverallWidth,ishape->FlangeThickness,0)); meshout.verts.push_back(IfcVector3(ishape->OverallWidth,0,0)); meshout.vertcnt.push_back(12); } else { IFCImporter::LogWarn("skipping unknown IfcParameterizedProfileDef entity, type is " + def.GetClassName()); return; } IfcMatrix4 trafo; ConvertAxisPlacement(trafo, *def.Position); meshout.Transform(trafo); }
// ------------------------------------------------------------------------------------------------ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& result, ConversionData& conv, bool collect_openings) { TempMesh meshout; // First read the profile description if(!ProcessProfile(*solid.SweptArea,meshout,conv) || meshout.verts.size()<=1) { return; } IfcVector3 dir; ConvertDirection(dir,solid.ExtrudedDirection); dir *= solid.Depth; /* if(conv.collect_openings && !conv.apply_openings) { dir *= 1000.0; } */ // Outline: assuming that `meshout.verts` is now a list of vertex points forming // the underlying profile, extrude along the given axis, forming new // triangles. std::vector<IfcVector3>& in = meshout.verts; const size_t size=in.size(); const bool has_area = solid.SweptArea->ProfileType == "AREA" && size>2; if(solid.Depth < 1e-6) { if(has_area) { result = meshout; } return; } result.verts.reserve(size*(has_area?4:2)); result.vertcnt.reserve(meshout.vertcnt.size()+2); // First step: transform all vertices into the target coordinate space IfcMatrix4 trafo; ConvertAxisPlacement(trafo, solid.Position); IfcVector3 vmin, vmax; MinMaxChooser<IfcVector3>()(vmin, vmax); BOOST_FOREACH(IfcVector3& v,in) { v *= trafo; vmin = std::min(vmin, v); vmax = std::max(vmax, v); }
// ------------------------------------------------------------------------------------------------ void ProcessParametrizedProfile(const IfcParameterizedProfileDef& def, TempMesh& meshout, ConversionData& conv) { if(const IfcRectangleProfileDef* const cprofile = def.ToPtr<IfcRectangleProfileDef>()) { const IfcFloat x = cprofile->XDim*0.5f, y = cprofile->YDim*0.5f; meshout.verts.reserve(meshout.verts.size()+4); meshout.verts.push_back( IfcVector3( x, y, 0.f )); meshout.verts.push_back( IfcVector3(-x, y, 0.f )); meshout.verts.push_back( IfcVector3(-x,-y, 0.f )); meshout.verts.push_back( IfcVector3( x,-y, 0.f )); meshout.vertcnt.push_back(4); } else if( const IfcCircleProfileDef* const circle = def.ToPtr<IfcCircleProfileDef>()) { if( const IfcCircleHollowProfileDef* const hollow = def.ToPtr<IfcCircleHollowProfileDef>()) { // TODO } const size_t segments = 32; const IfcFloat delta = AI_MATH_TWO_PI_F/segments, radius = circle->Radius; meshout.verts.reserve(segments); IfcFloat angle = 0.f; for(size_t i = 0; i < segments; ++i, angle += delta) { meshout.verts.push_back( IfcVector3( ::cos(angle)*radius, ::sin(angle)*radius, 0.f )); } meshout.vertcnt.push_back(segments); } else { IFCImporter::LogWarn("skipping unknown IfcParameterizedProfileDef entity, type is " + def.GetClassName()); return; } IfcMatrix4 trafo; ConvertAxisPlacement(trafo, *def.Position); meshout.Transform(trafo); }
// ------------------------------------------------------------------------------------------------ void ProcessRevolvedAreaSolid(const IfcRevolvedAreaSolid& solid, TempMesh& result, ConversionData& conv) { TempMesh meshout; // first read the profile description if(!ProcessProfile(*solid.SweptArea,meshout,conv) || meshout.verts.size()<=1) { return; } IfcVector3 axis, pos; ConvertAxisPlacement(axis,pos,solid.Axis); IfcMatrix4 tb0,tb1; IfcMatrix4::Translation(pos,tb0); IfcMatrix4::Translation(-pos,tb1); const std::vector<IfcVector3>& in = meshout.verts; const size_t size=in.size(); bool has_area = solid.SweptArea->ProfileType == "AREA" && size>2; const IfcFloat max_angle = solid.Angle*conv.angle_scale; if(std::fabs(max_angle) < 1e-3) { if(has_area) { result = meshout; } return; } const unsigned int cnt_segments = std::max(2u,static_cast<unsigned int>(16 * std::fabs(max_angle)/AI_MATH_HALF_PI_F)); const IfcFloat delta = max_angle/cnt_segments; has_area = has_area && std::fabs(max_angle) < AI_MATH_TWO_PI_F*0.99; result.verts.reserve(size*((cnt_segments+1)*4+(has_area?2:0))); result.vertcnt.reserve(size*cnt_segments+2); IfcMatrix4 rot; rot = tb0 * IfcMatrix4::Rotation(delta,axis,rot) * tb1; size_t base = 0; std::vector<IfcVector3>& out = result.verts; // dummy data to simplify later processing for(size_t i = 0; i < size; ++i) { out.insert(out.end(),4,in[i]); } for(unsigned int seg = 0; seg < cnt_segments; ++seg) { for(size_t i = 0; i < size; ++i) { const size_t next = (i+1)%size; result.vertcnt.push_back(4); const IfcVector3& base_0 = out[base+i*4+3],base_1 = out[base+next*4+3]; out.push_back(base_0); out.push_back(base_1); out.push_back(rot*base_1); out.push_back(rot*base_0); } base += size*4; } out.erase(out.begin(),out.begin()+size*4); if(has_area) { // leave the triangulation of the profile area to the ear cutting // implementation in aiProcess_Triangulate - for now we just // feed in two huge polygons. base -= size*8; for(size_t i = size; i--; ) { out.push_back(out[base+i*4+3]); } for(size_t i = 0; i < size; ++i ) { out.push_back(out[i*4]); } result.vertcnt.push_back(size); result.vertcnt.push_back(size); } IfcMatrix4 trafo; ConvertAxisPlacement(trafo, solid.Position); result.Transform(trafo); IFCImporter::LogDebug("generate mesh procedurally by radial extrusion (IfcRevolvedAreaSolid)"); }
// ------------------------------------------------------------------------------------------------ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBoundedHalfSpace* hs, TempMesh& result, const TempMesh& first_operand, ConversionData& conv) { ai_assert(hs != NULL); const IfcPlane* const plane = hs->BaseSurface->ToPtr<IfcPlane>(); if(!plane) { IFCImporter::LogError("expected IfcPlane as base surface for the IfcHalfSpaceSolid"); return; } // extract plane base position vector and normal vector IfcVector3 p,n(0.f,0.f,1.f); if (plane->Position->Axis) { ConvertDirection(n,plane->Position->Axis.Get()); } ConvertCartesianPoint(p,plane->Position->Location); if(!IsTrue(hs->AgreementFlag)) { n *= -1.f; } n.Normalize(); // obtain the polygonal bounding volume boost::shared_ptr<TempMesh> profile = boost::shared_ptr<TempMesh>(new TempMesh()); if(!ProcessCurve(hs->PolygonalBoundary, *profile.get(), conv)) { IFCImporter::LogError("expected valid polyline for boundary of boolean halfspace"); return; } IfcMatrix4 proj_inv; ConvertAxisPlacement(proj_inv,hs->Position); // and map everything into a plane coordinate space so all intersection // tests can be done in 2D space. IfcMatrix4 proj = proj_inv; proj.Inverse(); // clip the current contents of `meshout` against the plane we obtained from the second operand const std::vector<IfcVector3>& in = first_operand.verts; std::vector<IfcVector3>& outvert = result.verts; std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin(), end = first_operand.vertcnt.end(), iit; outvert.reserve(in.size()); result.vertcnt.reserve(first_operand.vertcnt.size()); std::vector<size_t> intersected_boundary_segments; std::vector<IfcVector3> intersected_boundary_points; // TODO: the following algorithm doesn't handle all cases. unsigned int vidx = 0; for(iit = begin; iit != end; vidx += *iit++) { if (!*iit) { continue; } unsigned int newcount = 0; bool was_outside_boundary = !PointInPoly(proj * in[vidx], profile->verts); // used any more? //size_t last_intersected_boundary_segment; IfcVector3 last_intersected_boundary_point; bool extra_point_flag = false; IfcVector3 extra_point; IfcVector3 enter_volume; bool entered_volume_flag = false; for(unsigned int i = 0; i < *iit; ++i) { // current segment: [i,i+1 mod size] or [*extra_point,i] if extra_point_flag is set const IfcVector3& e0 = extra_point_flag ? extra_point : in[vidx+i]; const IfcVector3& e1 = extra_point_flag ? in[vidx+i] : in[vidx+(i+1)%*iit]; // does the current segment intersect the polygonal boundary? const IfcVector3& e0_plane = proj * e0; const IfcVector3& e1_plane = proj * e1; intersected_boundary_segments.clear(); intersected_boundary_points.clear(); const bool is_outside_boundary = !PointInPoly(e1_plane, profile->verts); const bool is_boundary_intersection = is_outside_boundary != was_outside_boundary; IntersectsBoundaryProfile(e0_plane, e1_plane, profile->verts, intersected_boundary_segments, intersected_boundary_points); ai_assert(!is_boundary_intersection || !intersected_boundary_segments.empty()); // does the current segment intersect the plane? // (no extra check if this is an extra point) IfcVector3 isectpos; const Intersect isect = extra_point_flag ? Intersect_No : IntersectSegmentPlane(p,n,e0,e1,isectpos); #ifdef _DEBUG if (isect == Intersect_Yes) { const IfcFloat f = fabs((isectpos - p)*n); ai_assert(f < 1e-5); } #endif const bool is_white_side = (e0-p)*n >= -1e-6; // e0 on good side of plane? (i.e. we should keep all geometry on this side) if (is_white_side) { // but is there an intersection in e0-e1 and is e1 in the clipping // boundary? In this case, generate a line that only goes to the // intersection point. if (isect == Intersect_Yes && !is_outside_boundary) { outvert.push_back(e0); ++newcount; outvert.push_back(isectpos); ++newcount; /* // this is, however, only a line that goes to the plane, but not // necessarily to the point where the bounding volume on the // black side of the plane is hit. So basically, we need another // check for [isectpos-e1], which should yield an intersection // point. extra_point_flag = true; extra_point = isectpos; was_outside_boundary = true; continue; */ // [isectpos, enter_volume] potentially needs extra points. // For this, we determine the intersection point with the // bounding volume and project it onto the plane. /* const IfcVector3& enter_volume_proj = proj * enter_volume; const IfcVector3& enter_isectpos = proj * isectpos; intersected_boundary_segments.clear(); intersected_boundary_points.clear(); IntersectsBoundaryProfile(enter_volume_proj, enter_isectpos, profile->verts, intersected_boundary_segments, intersected_boundary_points); if(!intersected_boundary_segments.empty()) { vec = vec + ((p - vec) * n) * n; } */ //entered_volume_flag = true; } else { outvert.push_back(e0); ++newcount; } } // e0 on bad side of plane, e1 on good (i.e. we should remove geometry on this side, // but only if it is within the bounding volume). else if (isect == Intersect_Yes) { // is e0 within the clipping volume? Insert the intersection point // of [e0,e1] and the plane instead of e0. if(was_outside_boundary) { outvert.push_back(e0); } else { if(entered_volume_flag) { const IfcVector3& fix_point = enter_volume + ((p - enter_volume) * n) * n; outvert.push_back(fix_point); ++newcount; } outvert.push_back(isectpos); } entered_volume_flag = false; ++newcount; } else { // no intersection with plane or parallel; e0,e1 are on the bad side // did we just pass the boundary line to the poly bounding? if (is_boundary_intersection) { // and are now outside the clipping boundary? if (is_outside_boundary) { // in this case, get the point where the clipping boundary // was entered first. Then, get the point where the clipping // boundary volume was left! These two points with the plane // normal form another plane that intersects the clipping // volume. There are two ways to get from the first to the // second point along the intersection curve, try to pick the // one that lies within the current polygon. // TODO this approach doesn't handle all cases // ... IfcFloat d = 1e20; IfcVector3 vclosest; BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) { const IfcFloat dn = (v-e1_plane).SquareLength(); if (dn < d) { d = dn; vclosest = v; } } vclosest = proj_inv * vclosest; if(entered_volume_flag) { const IfcVector3& fix_point = vclosest + ((p - vclosest) * n) * n; outvert.push_back(fix_point); ++newcount; entered_volume_flag = false; } outvert.push_back(vclosest); ++newcount; //outvert.push_back(e1); //++newcount; } else { entered_volume_flag = true; // we just entered the clipping boundary. Record the point // and the segment where we entered and also generate this point. //last_intersected_boundary_segment = intersected_boundary_segments.front(); //last_intersected_boundary_point = intersected_boundary_points.front(); outvert.push_back(e0); ++newcount; IfcFloat d = 1e20; IfcVector3 vclosest; BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) { const IfcFloat dn = (v-e0_plane).SquareLength(); if (dn < d) { d = dn; vclosest = v; } } enter_volume = proj_inv * vclosest; outvert.push_back(enter_volume); ++newcount; } } // if not, we just keep the vertex else if (is_outside_boundary) { outvert.push_back(e0); ++newcount; entered_volume_flag = false; } } was_outside_boundary = is_outside_boundary; extra_point_flag = false; }