| 112 | } |
| 113 | |
| 114 | Expected<Mesh> bendContoursAlongCurve( const Contours2f& contours, const CurveFunc& curve, const BendContoursAlongCurveParams& params ) |
| 115 | { |
| 116 | MR_TIMER; |
| 117 | auto contoursMesh = PlanarTriangulation::triangulateContours( contours ); |
| 118 | auto bbox = contoursMesh.computeBoundingBox(); |
| 119 | if ( !bbox.valid() ) |
| 120 | return unexpected( "Contours mesh is empty" ); |
| 121 | |
| 122 | if ( curve.totalLength <= 0.0f ) |
| 123 | { |
| 124 | assert( !"invalid curve length" ); |
| 125 | return unexpected( "Invalid curve length" ); |
| 126 | } |
| 127 | |
| 128 | const float cStartDepth = bbox.diagonal() * 0.05f; // use relative depth to avoid floating errors |
| 129 | addBaseToPlanarMesh( contoursMesh, -cStartDepth ); |
| 130 | contoursMesh.invalidateCaches(); |
| 131 | auto diagonal = bbox.size(); |
| 132 | diagonal.z = cStartDepth; |
| 133 | const float pivotInBoxX = lerp( bbox.min.x, bbox.max.x, params.pivotBoxPoint.x ); |
| 134 | const float pivotInBoxY = lerp( bbox.min.y, bbox.max.y, params.pivotBoxPoint.y ); |
| 135 | |
| 136 | const float plusOffset = std::abs( params.extrusion ); |
| 137 | const float minusOffset = cStartDepth - std::abs( params.extrusion ); |
| 138 | |
| 139 | auto& contoursMeshPoints = contoursMesh.points; |
| 140 | VertId firstBottomVert( contoursMeshPoints.size() / 2 ); |
| 141 | |
| 142 | const auto components = MeshComponents::getAllComponents( contoursMesh ); |
| 143 | const auto stretchMod = curve.totalLength / diagonal.x; |
| 144 | const auto startCurvePos = params.pivotCurveTime * curve.totalLength; |
| 145 | // independently for each component of contoursMesh |
| 146 | ParallelFor( components, [&]( size_t icomp ) |
| 147 | { |
| 148 | const auto & compFaces = components[icomp]; |
| 149 | const auto compCenter = contoursMesh.computeBoundingBox( &compFaces ).center(); |
| 150 | float xInBox = compCenter.x - pivotInBoxX; |
| 151 | if ( params.stretch ) |
| 152 | xInBox *= stretchMod; |
| 153 | |
| 154 | float curveTime = startCurvePos + xInBox; |
| 155 | if ( params.periodicCurve ) |
| 156 | curveTime = curveTime - std::floor( curveTime / curve.totalLength ) * curve.totalLength; |
| 157 | const auto pos = curve.func( curveTime ); |
| 158 | |
| 159 | const auto vecx = pos.dir; |
| 160 | const auto norm = pos.snorm; |
| 161 | const auto vecy = cross( vecx, -norm ).normalized(); |
| 162 | |
| 163 | auto rotQ = Quaternionf( Vector3f::plusX(), vecx ); |
| 164 | // handle degenerated case |
| 165 | auto newY = rotQ( Vector3f::plusY() ); |
| 166 | auto dotY = dot( newY, vecy ); |
| 167 | if ( std::abs( std::abs( dotY ) - 1.0f ) < 10.0f * std::numeric_limits<float>::epsilon() ) |
| 168 | { |
| 169 | if ( dotY < 0.0f ) |
| 170 | rotQ = Quaternionf( vecx, PI_F ) * rotQ; |
| 171 | } |
no test coverage detected