#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later

#***************************************************************************
#                          BSpline3D
#***************************************************************************
# Create a parametric BSpline from Vertices selected in 3D view
# Select two or more vertices in the 3D view, then execute the macro.
#***************************************************************************
#*   Copyright (c) 2024 Eric Price (CorvusCorax)                           *
#*                      <eric.price[at]tuebingen.mpg.de>                   *
#*                                                                         *
#*   This file is an extension of FreeCAD.                                 *
#*                                                                         *
#*   FreeCAD is free software: you can redistribute it and/or modify it    *
#*   under the terms of the GNU Lesser General Public License as           *
#*   published by the Free Software Foundation, either version 2.1 of the  *
#*   License, or (at your option) any later version.                       *
#*                                                                         *
#*   FreeCAD 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      *
#*   Lesser General Public License for more details.                       *
#*                                                                         *
#*   You should have received a copy of the GNU Lesser General Public      *
#*   License along with FreeCAD. If not, see                               *
#*   <https://www.gnu.org/licenses/>.                                      *
#*                                                                         *
#**************************************************************************/

__Name__ = 'BSpline3D'
__Comment__ = 'Create a parametric BSpline from Vertices selected in 3D view'
__Author__ = 'CorvusCorax'
__Version__ = '0.0.2'
__Date__ = '2024-11-22'
__License__ = 'LGPL-2.1-or-later'
__Web__ = 'https://github.com/CorvusCorax/FreeCAD-BSpline3D'
__Wiki__ = 'https://github.com/CorvusCorax/FreeCAD-BSpline3D/wiki'
__Help__ = 'Select two or more vertices in the 3D view, then execute the macro.'
__Status__ = 'beta'
__Requires__ = 'FreeCAD >= v0.20'
__Contact__ = 'https://github.com/CorvusCorax'
__Communication__ = 'https://github.com/CorvusCorax/FreeCAD-BSpline3D/issues'
__Files__ = ''
__Xpm__ = """
/* XPM */
static char * XFACE[] = {
"32 32 2 1",
"  c #FFFFFF",
". c #000000",
"                                ",
"                                ",
"                                ",
"                                ",
"                                ",
"              ........         .",
"     ....   ...      ...    . . ",
"    ..... ...          .   ...  ",
"    .  ....            ..  .... ",
"    .  ...              .  ...  ",
"    .....               . .     ",
"       ..                       ",
"       .                        ",
"      ..             ....       ",
"      .             .....       ",
"      ..            .  ..       ",
"       .            .  ..       ",
"       ..           ....        ",
"        .                       ",
"        ..                      ",
"         ..                     ",
"          ..                    ",
"           ..                   ",
"      .......                   ",
"     ........                   ",
"     .    ...                   ",
"     .    ...                   ",
"     .    ..                    ",
"     ......                     ",
"                                ",
"                                ",
"                                "};
"""

import FreeCAD, FreeCADGui
import Draft

def BSpline3D():
    selection = FreeCADGui.Selection.getCompleteSelection(Gui.Selection.ResolveMode.NoResolve)
    vertices=[]
    for obj in selection:
        for i,feature in enumerate(obj.SubObjects):
            if (feature.ShapeType!="Vertex"):
                FreeCAD.Console.PrintWarning("Selected object %s is not a Vertex (%s), using CenterOfGravity.\n"%(obj.Object.Label+"."+ obj.SubElementNames[i], feature.ShapeType)) # dirty little secret: we use CenterOfGravity for vertices as well.
            subelements=obj.SubElementNames[i].split('.')
            if len(subelements)==1:
                # The feature selected is an independent shape and not subject to placements
                target="<<"+obj.Document.Label+">>#<<"+obj.Object.Label+">>."
                placement=""
            else:
                # The feature selected is subelement of a Part, Body, Assembly or similar and subject to a chain of placements
                placement="<<"+obj.Document.Label+">>#<<"+obj.Object.Label+">>.Placement * "
                for i in range(0,len(subelements)-2):
                    placement+="<<"+obj.Document.Label+">>#<<"+obj.Document.getObject(subelements[i]).Label+">>.Placement * "
                target="<<"+obj.Document.Label+">>#<<"+obj.Document.getObject(subelements[-2]).Label+">>."
            if subelements[-1]=="":
                #selected element is the whole shape of a feature
                target+="Shape.CenterOfGravity"
            else:
                #selected element is a subelement of a shape (Vertex, Edge, SubShape, ...)
                target+="Shape."+subelements[-1]+".CenterOfGravity"
            vertices.append("( "+placement+target+" )")
    if len(vertices)<2:
        FreeCAD.Console.PrintError("Please select at least 2 Points.\n")
        return False
    reference="list(" + "; ".join(vertices) + ")"
    FreeCADGui.doCommand("import Draft")
    FreeCADGui.doCommand("Draft.make_bspline([ FreeCAD.Vector(0,0,0), FreeCAD.Vector(1,1,1) ], closed=False, face=False, support=None).setExpression('Points', '%s')"%(reference))
    return True

BSpline3D()
#End
