# flake8: noqa
##
##  SPDX-FileCopyrightText: © 2007-2023 Benedict Verhegghe <bverheg@gmail.com>
##  SPDX-License-Identifier: GPL-3.0-or-later
##
##  This file is part of pyFormex 3.4  (Thu Nov 16 18:07:39 CET 2023)
##  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/.
##
"""sectionize.py
Create, measure and approximate cross section of a Formex.
"""
import pyformex as pf
from pyformex.curve import PolyLine
from pyformex import simple
from pyformex.gui.draw import *
[docs]def connectPoints(F, close=False):
    """Return a Formex with straight segments connecting subsequent points.
    F can be a Formex or data that can be turned into a Formex (e.g. an (n,3)
    array of points). The result is a plex-2 Formex connecting the subsequent
    points of F or the first point of subsequent elements in case the plexitude
    of F > 1.
    If close=True, the last point is connected back to the first to create a
    closed polyline.
    """
    if not isinstance(F, Formex):
        F = Formex(F)
    return connect([F, F], bias=[0, 1], loop=close) 
[docs]def centerline(F, dir, nx=2, mode=2, th=0.2):
    """Compute the centerline in the direction dir.
    """
    bb = F.bbox()
    x0 = F.center()
    x1 = F.center()
    x0[dir] = bb[0][dir]
    x1[dir] = bb[1][dir]
    n = np.array((0, 0, 0))
    n[dir] = nx
    grid = simple.regularGrid(x0, x1, n, swapaxes=True).reshape((-1, 3))
    th *= (x1[dir]-x0[dir])/nx
    n = np.zeros((3,))
    n[dir] = 1.0
    def localCenter(X, P, n):
        """Return the local center of points X in the plane P,n"""
        test = abs(X.distanceFromPlane(P, n)) < th  # points close to plane
        if mode == 1:
            C = X[test].center()
        elif mode == 2:
            C = X[test].centroid()
        return C
    center = [localCenter(F, P, n) for P in grid]
    return PolyLine(center) 
[docs]def createSegments(F, ns=None, th=None):
    """Create segments along 0 axis for sectionizing the Formex F."""
    bb = F.bbox()
    print("Bounding box = %s" % bb)
    if ns is None or th is None:
        res = askItems([['number of sections', 20],
                        ['relative thickness', 0.1]],
                       'Sectioning Parameters')
        if res:
            ns = int(res['number of sections'])
            th = float(res['relative thickness'])
    if at.isInt(ns) and isinstance(th, float):
        xmin, ymin, zmin = bb[0]
        xmax, ymax, zmax = bb[1]
        xgem, ygem, zgem = F.center()
        A = [xmin, ygem, zgem]
        B = [xmax, ygem, zgem]
        segments = Formex([[A, B]]).subdivide(ns)
        print("Segments: %s" % segments)
        return ns, th, segments
    return 0, 0, [] 
[docs]def sectionize(F, segments, th=0.1, visual=True):
    """Sectionize a Formex in planes perpendicular to the segments.
    F is any Formex.
    segments is a plex-2 Formex.
    Planes are chosen in each center of a segment, perpendicular to
    that segment. Then parts of the Formex F are selected in the
    neighbourhood of each plane. Each part is then approximated by a
    circle in that plane.
    th is the relative thickness of the selected part of the Formex.
    If th = 0.5, that part will be delimited by two planes in the endpoints
    of and perpendicular to the segments.
    """
    sections = []
    ctr = []
    diam = []
    if visual:
        clear()
        linewidth(1)
        draw(F, color='yellow')
        linewidth(2)
    for s in segments:
        c = 0.5 * (s[0]+s[1])
        d = s[1]-s[0]
        l = at.length(d)
        n = d/l
        t = th*l
        test = abs(F.distanceFromPlane(c, n)) < th*l
        test = test.sum(axis=-1)
        G = F.select(test==3)
        if visual:
            draw(G, color='blue', view=None)
            pf.canvas.update()
        print(G)
        C = G.center()
        D = 2 * G.distanceFromLine(C, n).mean()
        print("Section Center: %s; Diameter: %s" % (C, D))
        sections.append(G)
        ctr.append(C)
        diam.append(D)
    return sections, ctr, diam 
[docs]def drawCircles(sections, ctr, diam):
    """Draw circles as approximation of Formices."""
    circle = simple.circle().rotate(-90, 1)
    cross = simple.shape('plus').rotate(-90, 1)
    circles = []
    n = len(sections)
    for i in range(n):
        C = cross.translate(ctr[i])
        B = circle.scale(diam[i]/2).translate(ctr[i])
        S = sections[i]
        clear()
        draw(S, view='left', wait=False)
        draw(C, color='red', bbox=None, wait=False)
        draw(B, color='blue', bbox=None)
        circles.append(B)
    return circles 
def drawAllCircles(F, circles):
    clear()
    linewidth(1)
    draw(F, color='yellow', view='front')
    linewidth(2)
    for circ in circles:
        bb = circ.bbox()
        d = (bb[1] - bb[0]) * 0.5
        bb[0] -= d
        bb[1] += d
        draw(circ, color='blue', bbox=bb)
    zoomAll()
# End