<commit_before>from compas.geometry import intersection_segment_plane
from compas.geometry import length_vector
from compas.geometry import subtract_vectors
from compas.geometry import dot_vectors


__all__ = ['mesh_slice_plane']


def mesh_slice_plane(mesh, plane):
    """Slice a mesh with a plane and construct the resulting submeshes.

    Parameters
    ----------
    mesh : compas.datastructures.Mesh
        The original mesh.
    plane : compas.geometry.Plane
        The cutting plane.

    Returns
    -------
    None or tuple of :class:`compas.datastructures.Mesh`
        If the mesh and plane do not intersect,
        or if the intersection is degenerate (point or line),
        the function returns ``None``.
        Otherwise, the "positive" and "negative" submeshes are returned.

    Examples
    --------
    >>> from compas.geometry import Plane
    >>> from compas.geometry import Box
    >>> from compas.datastructures import Mesh
    >>> plane = Plane((0, 0, 0), (1, 0, 0))
    >>> box = Box.from_width_height_depth(1, 1, 1)
    >>> mesh = Mesh.from_shape(box)
    >>> result = mesh_slice_plane(mesh, plane)
    >>> len(result) == 2
    True
    """
    intersection = IntersectionMeshPlane(mesh, plane)
    if not intersection.is_polygon:
        return None
    return intersection.split()


class IntersectionMeshPlane(object):

    def __init__(self, mesh, plane):
        self.mesh = mesh
        self.plane = plane
        self._intersections = []
        self.intersect()

    @property
    def meshtype(self):
        return type(self.mesh)

    @property
    def intersections(self):
        return self._intersections

    @property
    def is_none(self):
        return len(self.intersections) == 0

    @property
    def is_point(self):
        return len(self.intersections) == 1

    @property
    def is_line(self):
        return len(self.intersections) == 2

    @property
    def is_polygon(self):
        return len(self.intersections) >= 3
    
    @property
    def is_mesh_closed(self):
        return self.mesh.is_closed()

    @property
    def positive(self):
        if self.is_none:
            return
        vertices = []
        for key in self.mesh.vertices():
            if self.is_positive(key):
                vertices.append(key)
        faces = []
        for key in vertices:
            faces += self.mesh.vertex_faces(key)
        faces = list(set(faces))
        vdict = {key: self.mesh.vertex_coordinates(key) for key in vertices + self.intersections}
        fdict = [self.mesh.face_vertices(fkey) for fkey in faces]
        mesh = self.meshtype.from_vertices_and_faces(vdict, fdict)
        if self.is_mesh_closed:
            mesh.add_face(mesh.vertices_on_boundary())
        return mesh

    def is_positive(self, key):
        o = self.plane.point<commit_msg>remove white space<commit_after>from compas.geometry import intersection_segment_plane
from compas.geometry import length_vector
from compas.geometry import subtract_vectors
from compas.geometry import dot_vectors


__all__ = ['mesh_slice_plane']


def mesh_slice_plane(mesh, plane):
    """Slice a mesh with a plane and construct the resulting submeshes.

    Parameters
    ----------
    mesh : compas.datastructures.Mesh
        The original mesh.
    plane : compas.geometry.Plane
        The cutting plane.

    Returns
    -------
    None or tuple of :class:`compas.datastructures.Mesh`
        If the mesh and plane do not intersect,
        or if the intersection is degenerate (point or line),
        the function returns ``None``.
        Otherwise, the "positive" and "negative" submeshes are returned.

    Examples
    --------
    >>> from compas.geometry import Plane
    >>> from compas.geometry import Box
    >>> from compas.datastructures import Mesh
    >>> plane = Plane((0, 0, 0), (1, 0, 0))
    >>> box = Box.from_width_height_depth(1, 1, 1)
    >>> mesh = Mesh.from_shape(box)
    >>> result = mesh_slice_plane(mesh, plane)
    >>> len(result) == 2
    True
    """
    intersection = IntersectionMeshPlane(mesh, plane)
    if not intersection.is_polygon:
        return None
    return intersection.split()


class IntersectionMeshPlane(object):

    def __init__(self, mesh, plane):
        self.mesh = mesh
        self.plane = plane
        self._intersections = []
        self.intersect()

    @property
    def meshtype(self):
        return type(self.mesh)

    @property
    def intersections(self):
        return self._intersections

    @property
    def is_none(self):
        return len(self.intersections) == 0

    @property
    def is_point(self):
        return len(self.intersections) == 1

    @property
    def is_line(self):
        return len(self.intersections) == 2

    @property
    def is_polygon(self):
        return len(self.intersections) >= 3

    @property
    def is_mesh_closed(self):
        return self.mesh.is_closed()

    @property
    def positive(self):
        if self.is_none:
            return
        vertices = []
        for key in self.mesh.vertices():
            if self.is_positive(key):
                vertices.append(key)
        faces = []
        for key in vertices:
            faces += self.mesh.vertex_faces(key)
        faces = list(set(faces))
        vdict = {key: self.mesh.vertex_coordinates(key) for key in vertices + self.intersections}
        fdict = [self.mesh.face_vertices(fkey) for fkey in faces]
        mesh = self.meshtype.from_vertices_and_faces(vdict, fdict)
        if self.is_mesh_closed:
            mesh.add_face(mesh.vertices_on_boundary())
        return mesh

    def is_positive(self, key):
        o = self.plane.point
{'fraction_non_alphanumeric': 0.10301640500970188, 'fraction_numerical': 0.0049391427059446115, 'mean_word_length': 2.4218467109233557, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6599617', 'n_tokens_mistral': 1714, 'n_tokens_neox': 1638, 'n_words': 443}