#
##
##  This file is part of pyFormex 1.0.5  (Sat Feb 16 10:40:32 CET 2019)
##  pyFormex is a tool for generating, manipulating and transforming 3D
##  geometrical models by sequences of mathematical operations.
##  Home page: http://pyformex.org
##  Project page:  http://savannah.nongnu.org/projects/pyformex/
##  Copyright 2004-2018 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be)
##  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.
"""
from __future__ import absolute_import, division, print_function


import pyformex as pf
from pyformex.plugins.curve import PolyLine
from pyformex import simple
from pyformex.gui.draw import *
from pyformex.arraytools import isInt

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.Formex):
        F = Formex(F)
    return formex.connect([F, F], bias=[0, 1], loop=close)


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 = 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 = 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)


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 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, []


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 = 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


def drawCircles(sections, ctr, diam):
    """Draw circles as approximation of Formices."""
    circle = simple.circle().rotate(-90, 1)
    cross = Formex(simple.Pattern['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
