#
##
##  SPDX-FileCopyrightText: © 2007-2022 Benedict Verhegghe <bverheg@gmail.com>
##  SPDX-License-Identifier: GPL-3.0-or-later
##
##  This file is part of pyFormex 3.1  (Sat May 21 14:49:50 CEST 2022)
##  pyFormex is a tool for generating, manipulating and transforming 3D
##  geometrical models by sequences of mathematical operations.
##  Home page: https://pyformex.org
##  Project page: https://savannah.nongnu.org/projects/pyformex/
##  Development: https://gitlab.com/bverheg/pyformex
##  Distributed under the GNU General Public License version 3 or later.
##
##  This program is free software: you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation, either version 3 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program.  If not, see http://www.gnu.org/licenses/.
##
#
"""Isosurface: surface reconstruction algorithms
This module contains the marching cube algorithm.
Some of the code is based on the example by Paul Bourke from
http://paulbourke.net/geometry/polygonise/
"""
import numpy as np
from pyformex.multi import multitask, cpu_count, splitar
[docs]def isosurface(data, level, nproc=-1, tet=0):
    """Create an isosurface through data at given level.
    Parameters
    ----------
    data: :term:`array_like`
        An (nx,ny,nz) shaped float array of data values at points with
        coordinates equal to their indices. This defines a 3D volume
        [0,nx-1], [0,ny-1], [0,nz-1].
    level: float
        The data value for which the isosurface is to be constructed.
    nproc: int
        The number of parallel processes to use. On multiprocessor machines
        this may be used to speed up the processing. If <= 0 , the number of
        processes will be set equal to the number of available processors,
        to achieve a maximal speedup.
    Returns
    -------
    array:
        An (ntr,3,3) float array defining the triangles of the isosurface.
        The result may be empty (if level is outside the data range).
    """
    if nproc is None:
        nproc = -1
    if nproc < 1:
        nproc = cpu_count()
    if nproc == 1:
        # Perform single process isosurface (accelerated)
        from pyformex.lib import misc
        data = data.astype(np.float32)
        level = np.float32(level)
        tri = misc.isosurface(data, level, tet)
    else:
        # Perform parallel isosurface
        # 1. Split in blocks (and remember shift)
        datablocks = splitar(data, nproc, close=True)
        shift = (np.array([d.shape[0] for d in datablocks]) - 1).cumsum()
        # 2. Solve blocks independently
        tasks = [(isosurface, (d, level, 1, tet)) for d in datablocks]
        tri = multitask(tasks, nproc)
        # 3. Shift and merge blocks
        for t, s in zip(tri[1:], shift[:-1]):
            t[:, :, 2] += s
        tri = np.concatenate(tri, axis=0)
    return tri 
[docs]def isoline(data, level, nproc=-1):
    """Create an isocontour through data at given level.
    Parameters
    ----------
    data: :term:`array_like`
        An (nx,ny) shaped array of data values at points with
        coordinates equal to their indices. This defines a 2D area
        [0,nx-1], [0,ny-1].
    level: float
        The data value for which the isocontour is to be constructed.
    nproc: int
        The number of parallel processes to use. On multiprocessor machines
        this may be used to speed up the processing. If <= 0 , the number of
        processes will be set equal to the number of available processors,
        to achieve a maximal speedup.
    Returns
    -------
    array:
        An (nseg,2,2) float array defining the 2D coordinates of the
        segments of the isocontour.
        The result may be empty (if level is outside the data range).
    """
    if nproc is None:
        nproc = -1
    if nproc < 1:
        nproc = cpu_count()
    if nproc == 1:
        # Perform single process isoline (accelerated)
        from pyformex.lib import misc
        data = data.astype(np.float32)
        level = np.float32(level)
        seg = misc.isoline(data, level)
    else:
        # Perform parallel isoline
        # 1. Split in blocks (and remember shift)
        datablocks = splitar(data, nproc, close=True)
        shift = (np.array([d.shape[0] for d in datablocks]) - 1).cumsum()
        # 2. Solve blocks independently
        tasks = [(isoline, (d, level, 1)) for d in datablocks]
        seg = multitask(tasks, nproc)
        # 3. Shift and merge blocks
        for t, s in zip(seg[1:], shift[:-1]):
            t[:, :, 1] += s
        seg = np.concatenate(seg, axis=0)
    return seg 
# End