<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def mesh_multiplane(mesh, plane_origin, plane_normal, heights):
 """ A utility function for slicing a mesh by multiple parallel planes, which caches the dot product operation. Parameters mesh : trimesh.Trimesh Geometry to be sliced by planes plane_normal : (3,) float Normal vector of plane plane_origin : (3,) float Point on a plane heights : (m,) float Offset distances from plane to slice at Returns lines : (m,) sequence of (n, 2, 2) float Lines in space for m planes to_3D : (m, 4, 4) float Transform to move each section back to 3D face_index : (m,) sequence of (n,) int Indexes of mesh.faces for each segment """

    # check input plane
    plane_normal = util.unitize(plane_normal)
    plane_origin = np.asanyarray(plane_origin,
                                 dtype=np.float64)
    heights = np.asanyarray(heights, dtype=np.float64)

    # dot product of every vertex with plane
    vertex_dots = np.dot(plane_normal,
                         (mesh.vertices - plane_origin).T)

    # reconstruct transforms for each 2D section
    base_transform = geometry.plane_transform(origin=plane_origin,
                                              normal=plane_normal)
    base_transform = np.linalg.inv(base_transform)

    # alter translation Z inside loop
    translation = np.eye(4)

    # store results
    transforms = []
    face_index = []
    segments = []

    # loop through user specified heights
    for height in heights:
        # offset the origin by the height
        new_origin = plane_origin + (plane_normal * height)
        # offset the dot products by height and index by faces
        new_dots = (vertex_dots - height)[mesh.faces]
        # run the intersection with the cached dot products
        lines, index = mesh_plane(mesh=mesh,
                                  plane_origin=new_origin,
                                  plane_normal=plane_normal,
                                  return_faces=True,
                                  cached_dots=new_dots)

        # get the transforms to 3D space and back
        translation[2, 3] = height
        to_3D = np.dot(base_transform, translation)
        to_2D = np.linalg.inv(to_3D)
        transforms.append(to_3D)

        # transform points to 2D frame
        lines_2D = transformations.transform_points(
            lines.reshape((-1, 3)),
            to_2D)

        # if we didn't screw up the transform all
        # of the Z values should be zero
        assert np.allclose(lines_2D[:, 2], 0.0)

        # reshape back in to lines and discard Z
        lines_2D = lines_2D[:, :2].reshape((-1, 2, 2))
        # store (n, 2, 2) float lines
        segments.append(lines_2D)
        # store (n,) int indexes of mesh.faces
        face_index.append(face_index)

    # (n, 4, 4) transforms from 2D to 3D
    transforms = np.array(transforms, dtype=np.float64)

    return segments, transforms, face_index