import numpy as np
from dataclasses import dataclass
from collections import defaultdict
input_points = np.array([
[20., 20., 60.],
[20., 30., 65.],
[55., 30., 80.],
[80., 10., 60.],
[20., 10., 55.],
[60., 30., 70.],
[70., 15., 20.],
])
planes_defined_by_points = [
np.array([[50., 5., 5.], [50., 45., 5.], [70., 45., 95.]]),
np.array([[30., 5., 95.], [30., 45., 95.], [60., 5., 5.]]),
]
@dataclass
class PlaneNormalForm:
"""The plane of all points p with np.dot(self.ortho, p) == self.scalar"""
ortho: np.array
scalar: float
planes_in_normal_form = []
for points in planes_defined_by_points:
ortho = np.cross(points[1] - points[0],
points[2] - points[0])
planes_in_normal_form.append(PlaneNormalForm(
ortho=ortho,
scalar=np.dot(ortho, points[0])))
points_by_relative_position = defaultdict(list)
for point in input_points:
key = tuple(np.dot(plane.ortho, point) > plane.scalar
for plane in planes_in_normal_form)
points_by_relative_position[key].append(point)
for key, points in points_by_relative_position.items():
print(key)
for point in points:
print(" {:5.0f}, {:5.0f}, {:5.0f}".format(*point))
aW1wb3J0IG51bXB5IGFzIG5wCmZyb20gZGF0YWNsYXNzZXMgaW1wb3J0IGRhdGFjbGFzcwpmcm9tIGNvbGxlY3Rpb25zIGltcG9ydCBkZWZhdWx0ZGljdAoKaW5wdXRfcG9pbnRzID0gbnAuYXJyYXkoWwogICAgWzIwLiwgMjAuLCA2MC5dLAogICAgWzIwLiwgMzAuLCA2NS5dLAogICAgWzU1LiwgMzAuLCA4MC5dLAogICAgWzgwLiwgMTAuLCA2MC5dLAogICAgWzIwLiwgMTAuLCA1NS5dLAogICAgWzYwLiwgMzAuLCA3MC5dLAogICAgWzcwLiwgMTUuLCAyMC5dLApdKQoKcGxhbmVzX2RlZmluZWRfYnlfcG9pbnRzID0gWwogICAgbnAuYXJyYXkoW1s1MC4sIDUuLCA1Ll0sIFs1MC4sIDQ1LiwgNS5dLCBbNzAuLCA0NS4sIDk1Ll1dKSwKICAgIG5wLmFycmF5KFtbMzAuLCA1LiwgOTUuXSwgWzMwLiwgNDUuLCA5NS5dLCBbNjAuLCA1LiwgNS5dXSksCl0KCkBkYXRhY2xhc3MKY2xhc3MgUGxhbmVOb3JtYWxGb3JtOgogICAgIiIiVGhlIHBsYW5lIG9mIGFsbCBwb2ludHMgcCB3aXRoIG5wLmRvdChzZWxmLm9ydGhvLCBwKSA9PSBzZWxmLnNjYWxhciIiIgogICAgb3J0aG86IG5wLmFycmF5CiAgICBzY2FsYXI6IGZsb2F0CgpwbGFuZXNfaW5fbm9ybWFsX2Zvcm0gPSBbXQpmb3IgcG9pbnRzIGluIHBsYW5lc19kZWZpbmVkX2J5X3BvaW50czoKICAgIG9ydGhvID0gbnAuY3Jvc3MocG9pbnRzWzFdIC0gcG9pbnRzWzBdLAogICAgICAgICAgICAgICAgICAgICBwb2ludHNbMl0gLSBwb2ludHNbMF0pCiAgICBwbGFuZXNfaW5fbm9ybWFsX2Zvcm0uYXBwZW5kKFBsYW5lTm9ybWFsRm9ybSgKICAgICAgICAgICAgb3J0aG89b3J0aG8sCiAgICAgICAgICAgIHNjYWxhcj1ucC5kb3Qob3J0aG8sIHBvaW50c1swXSkpKQoKcG9pbnRzX2J5X3JlbGF0aXZlX3Bvc2l0aW9uID0gZGVmYXVsdGRpY3QobGlzdCkKZm9yIHBvaW50IGluIGlucHV0X3BvaW50czoKCWtleSA9IHR1cGxlKG5wLmRvdChwbGFuZS5vcnRobywgcG9pbnQpID4gcGxhbmUuc2NhbGFyCgkgICAgICAgICAgICBmb3IgcGxhbmUgaW4gcGxhbmVzX2luX25vcm1hbF9mb3JtKQoJcG9pbnRzX2J5X3JlbGF0aXZlX3Bvc2l0aW9uW2tleV0uYXBwZW5kKHBvaW50KQoKZm9yIGtleSwgcG9pbnRzIGluIHBvaW50c19ieV9yZWxhdGl2ZV9wb3NpdGlvbi5pdGVtcygpOgogICAgcHJpbnQoa2V5KQogICAgZm9yIHBvaW50IGluIHBvaW50czoKICAgIAlwcmludCgiICB7OjUuMGZ9LCB7OjUuMGZ9LCB7OjUuMGZ9Ii5mb3JtYXQoKnBvaW50KSkK