Computes a (somewhat) optimal oriented bounding box around the triangulated geometry described in elem by constructing a local reference frame based on the prevalent triangle normals Args: file (ifcopenshell.file): file containing elem elem (TriangulationEle
(self, file, elem, min_thickness=0.01, force=False)
| 201 | return len(ConvexHull(points[:, 0:2]).vertices) == len(points) |
| 202 | |
| 203 | def substitute_with_box(self, file, elem, min_thickness=0.01, force=False): |
| 204 | """ |
| 205 | Computes a (somewhat) optimal oriented bounding box around the triangulated geometry described in elem by constructing a local reference frame based on the prevalent triangle normals |
| 206 | |
| 207 | Args: |
| 208 | file (ifcopenshell.file): file containing elem |
| 209 | elem (TriangulationElement): triangulated geometry |
| 210 | min_thickness (float, optional): minimal thickness of the oriented bounding box to create around elem |
| 211 | |
| 212 | Returns: |
| 213 | tuple: <guid, <3x4 matrix, min, max>> with min and max being the local coords in the matrix |
| 214 | """ |
| 215 | vs = numpy.array(elem.geometry.verts).reshape((-1, 3)) |
| 216 | fs = numpy.array(elem.geometry.faces).reshape((-1, 3)) |
| 217 | |
| 218 | def _(): |
| 219 | for f in fs: |
| 220 | p, q, r = vs[f] |
| 221 | pq = q - p |
| 222 | pr = r - p |
| 223 | pq /= numpy.linalg.norm(pq) |
| 224 | pr /= numpy.linalg.norm(pr) |
| 225 | pqr = numpy.cross(pq, pr) |
| 226 | pqr /= numpy.linalg.norm(pqr) |
| 227 | yield pqr |
| 228 | |
| 229 | tri_norms = numpy.array(list(_())) |
| 230 | |
| 231 | def _(): |
| 232 | for f in fs: |
| 233 | p, q, r = vs[f] |
| 234 | pq = q - p |
| 235 | pr = r - p |
| 236 | pqr = numpy.cross(pq, pr) |
| 237 | yield numpy.linalg.norm(pqr) / 2. |
| 238 | |
| 239 | tri_areas = numpy.array(list(_())) |
| 240 | |
| 241 | _, inv, cnts = numpy.unique(numpy.int_(tri_norms * 1000), return_counts=True, return_inverse=True, axis=0) |
| 242 | di = utils.make_default(sorted((j, i) for i, j in enumerate(inv))) |
| 243 | summed_area = [v[1] for v in sorted((k, sum(tri_areas[v])) for k, v in di.items())] |
| 244 | sorted_summed_areas = numpy.argsort(summed_area) |
| 245 | V = numpy.average(tri_norms[di[sorted_summed_areas[-1]]], axis=0) |
| 246 | candidates = [] |
| 247 | for i in range(1, min(10, len(cnts))): |
| 248 | ref = numpy.average(tri_norms[di[sorted_summed_areas[-i]]], axis=0) |
| 249 | candidates.append((abs(ref @ V), ref)) |
| 250 | if not candidates: |
| 251 | refs = [(0, 0, 1), (1, 0, 0)] |
| 252 | for ref in refs: |
| 253 | candidates.append((abs(ref @ V), ref)) |
| 254 | ref = min(candidates, key=operator.itemgetter(0))[1] |
| 255 | Y = numpy.cross(V, ref) |
| 256 | X = numpy.cross(V, Y) |
| 257 | M = numpy.array((X, -Y, V)) |
| 258 | |
| 259 | Mi = numpy.linalg.inv(M) |
| 260 | vsi = numpy.array([v @ Mi for v in vs]) |
no test coverage detected