// ------------------------------------------------------------------------------------------------ void ConvertDirection(IfcVector3& out, const IfcDirection& in) { out = IfcVector3(); for(size_t i = 0; i < in.DirectionRatios.size(); ++i) { out[i] = in.DirectionRatios[i]; } const IfcFloat len = out.Length(); if (len<1e-6) { IFCImporter::LogWarn("direction vector magnitude too small, normalization would result in a division by zero"); return; } out /= len; }
// ------------------------------------------------------------------------------------------------ IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut) { const std::vector<IfcVector3>& out = curmesh.verts; IfcMatrix3 m; ok = true; // The input "mesh" must be a single polygon const size_t s = out.size(); assert(curmesh.vertcnt.size() == 1 && curmesh.vertcnt.back() == s); const IfcVector3 any_point = out[s-1]; IfcVector3 nor; // The input polygon is arbitrarily shaped, therefore we might need some tries // until we find a suitable normal. Note that Newell's algorithm would give // a more robust result, but this variant also gives us a suitable first // axis for the 2D coordinate space on the polygon plane, exploiting the // fact that the input polygon is nearly always a quad. bool done = false; size_t i, j; for (i = 0; !done && i < s-2; done || ++i) { for (j = i+1; j < s-1; ++j) { nor = -((out[i]-any_point)^(out[j]-any_point)); if(std::fabs(nor.Length()) > 1e-8f) { done = true; break; } } } if(!done) { ok = false; return m; } nor.Normalize(); norOut = nor; IfcVector3 r = (out[i]-any_point); r.Normalize(); //if(d) { // *d = -any_point * nor; //} // Reconstruct orthonormal basis // XXX use Gram Schmidt for increased robustness IfcVector3 u = r ^ nor; u.Normalize(); m.a1 = r.x; m.a2 = r.y; m.a3 = r.z; m.b1 = u.x; m.b2 = u.y; m.b3 = u.z; m.c1 = -nor.x; m.c2 = -nor.y; m.c3 = -nor.z; return m; }
// ------------------------------------------------------------------------------------------------ void ProcessSweptDiskSolid(const IfcSweptDiskSolid solid, TempMesh& result, ConversionData& conv) { const Curve* const curve = Curve::Convert(*solid.Directrix, conv); if(!curve) { IFCImporter::LogError("failed to convert Directrix curve (IfcSweptDiskSolid)"); return; } const unsigned int cnt_segments = 16; const IfcFloat deltaAngle = AI_MATH_TWO_PI/cnt_segments; const size_t samples = curve->EstimateSampleCount(solid.StartParam,solid.EndParam); result.verts.reserve(cnt_segments * samples * 4); result.vertcnt.reserve((cnt_segments - 1) * samples); std::vector<IfcVector3> points; points.reserve(cnt_segments * samples); TempMesh temp; curve->SampleDiscrete(temp,solid.StartParam,solid.EndParam); const std::vector<IfcVector3>& curve_points = temp.verts; if(curve_points.empty()) { IFCImporter::LogWarn("curve evaluation yielded no points (IfcSweptDiskSolid)"); return; } IfcVector3 current = curve_points[0]; IfcVector3 previous = current; IfcVector3 next; IfcVector3 startvec; startvec.x = 1.0f; startvec.y = 1.0f; startvec.z = 1.0f; unsigned int last_dir = 0; // generate circles at the sweep positions for(size_t i = 0; i < samples; ++i) { if(i != samples - 1) { next = curve_points[i + 1]; } // get a direction vector reflecting the approximate curvature (i.e. tangent) IfcVector3 d = (current-previous) + (next-previous); d.Normalize(); // figure out an arbitrary point q so that (p-q) * d = 0, // try to maximize ||(p-q)|| * ||(p_last-q_last)|| IfcVector3 q; bool take_any = false; for (unsigned int i = 0; i < 2; ++i, take_any = true) { if ((last_dir == 0 || take_any) && std::abs(d.x) > 1e-6) { q.y = startvec.y; q.z = startvec.z; q.x = -(d.y * q.y + d.z * q.z) / d.x; last_dir = 0; break; } else if ((last_dir == 1 || take_any) && std::abs(d.y) > 1e-6) { q.x = startvec.x; q.z = startvec.z; q.y = -(d.x * q.x + d.z * q.z) / d.y; last_dir = 1; break; } else if ((last_dir == 2 && std::abs(d.z) > 1e-6) || take_any) { q.y = startvec.y; q.x = startvec.x; q.z = -(d.y * q.y + d.x * q.x) / d.z; last_dir = 2; break; } } q *= solid.Radius / q.Length(); startvec = q; // generate a rotation matrix to rotate q around d IfcMatrix4 rot; IfcMatrix4::Rotation(deltaAngle,d,rot); for (unsigned int seg = 0; seg < cnt_segments; ++seg, q *= rot ) { points.push_back(q + current); } previous = current; current = next; } // make quads for(size_t i = 0; i < samples - 1; ++i) { const aiVector3D& this_start = points[ i * cnt_segments ]; // locate corresponding point on next sample ring unsigned int best_pair_offset = 0; float best_distance_squared = 1e10f; for (unsigned int seg = 0; seg < cnt_segments; ++seg) { const aiVector3D& p = points[ (i+1) * cnt_segments + seg]; const float l = (p-this_start).SquareLength(); if(l < best_distance_squared) { best_pair_offset = seg; best_distance_squared = l; } } for (unsigned int seg = 0; seg < cnt_segments; ++seg) { result.verts.push_back(points[ i * cnt_segments + (seg % cnt_segments)]); result.verts.push_back(points[ i * cnt_segments + (seg + 1) % cnt_segments]); result.verts.push_back(points[ (i+1) * cnt_segments + ((seg + 1 + best_pair_offset) % cnt_segments)]); result.verts.push_back(points[ (i+1) * cnt_segments + ((seg + best_pair_offset) % cnt_segments)]); IfcVector3& v1 = *(result.verts.end()-1); IfcVector3& v2 = *(result.verts.end()-2); IfcVector3& v3 = *(result.verts.end()-3); IfcVector3& v4 = *(result.verts.end()-4); if (((v4-v3) ^ (v4-v1)) * (v4 - curve_points[i]) < 0.0f) { std::swap(v4, v1); std::swap(v3, v2); } result.vertcnt.push_back(4); } } IFCImporter::LogDebug("generate mesh procedurally by sweeping a disk along a curve (IfcSweptDiskSolid)"); }