# SPDX-License-Identifier: LGPL-2.1-or-later
# SPDX-FileCopyrightText: 2026 Stefan Tröger <stefantroeger@gmx.net>
# SPDX-FileNotice: Part of the FreeCAD project.

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

__title__ = "GMSH transfinite meshing examples"
__author__ = "Stefan Tröger"
__url__ = "https://www.freecad.org"


# **************************************************************************
#
# Important: The generated file is used for gmsh tests. Do not change
# content (geometry, options, names etc.) or add/remove content without
# regenerating the gmsh test data files
#
# **************************************************************************


import math

import FreeCAD
import Part

import ObjectsFem
from .manager import init_doc

def get_information():
    return {
        "name": "GMSH manual transfinite meshing",
        "meshgeneration": "Transfinite",
        "hasanalysis": False
    }


def get_explanation(header=""):
    return (
        header
        + """

To run the example from Python console use:
from femexamples.gmsh_transfinite import setup
setup()

Shows many ways how the transfinite gmsh options can be used to create structured meshes.

"""
    )

def create_example(doc, name, description):
    # setup of a full example document structure
    # returns the part and gmsh mesh object

    group = doc.addObject("App::DocumentObjectGroup", name)
    part_obj = group.newObject("Part::Feature", "Geometry")
    mesh_obj = ObjectsFem.makeMeshGmsh(doc, "Mesh")
    mesh_obj.Shape = part_obj
    mesh_obj.CharacteristicLengthMax = 2
    group.addObject(mesh_obj)

    explanation = group.newObject("App::TextDocument","Explanation")
    explanation.Text = description

    if FreeCAD.GuiUp:
        mesh_obj.ViewObject.BackfaceCulling = False

    return part_obj, mesh_obj


def setup(doc=None, solver=None):

    # init FreeCAD document
    if doc is None:
        doc = init_doc()

    triangle = Part.makePolygon([FreeCAD.Vector(0,0,0),
                                 FreeCAD.Vector(10,0,0),
                                 FreeCAD.Vector(5,8,0),
                                 FreeCAD.Vector(0,0,0)])

    # 1: simple 3 edged face with tf surface
    explanation = ("A face with 3 edges can be made transfinite. For this the two guiding "
                   "edges need to have the same amount of mesh subdivisions on them.\n\n"
                   " - The guiding edges of a triangle are Edge1 and Edge3 by default, this "
                   " means the two edges connecting to Vertex1\n"
                   " - In this example the number of elements on those edges is equal, as they "
                   "are the same length and we use CharachteristicLengthMax setting to subdivide")

    part_obj, mesh_obj = create_example(doc, "Triangle Surface", explanation)
    part_obj.Shape = Part.makeFace(triangle)
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1"])]

    # 2: simple 3 edged face with tf surface and quad mesh
    explanation = ("The transfinite surface of the left example can be created with quad elements "
                   "instead of triangles by using the Recombine option")

    part_obj, mesh_obj = create_example(doc, "Triangle Surface Recombined", explanation)
    part_obj.Shape = Part.makeFace(triangle).translate(FreeCAD.Vector(15,0,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1"])]
    tf_surf.Recombine = True

    # 3: simple 3 edged face with tf surface, quad mesh and transfinite curve guiding
    explanation = ("All edges of the transfinite surface can have arbitrary node distribution, and "
                   "we can specify it for example with transfinite curves\n\n"
                   " - The requirement of same amount of subdivisions for Edge1 and Edge3 still holds. "
                   "This is why we applied the same transfinite curve to those 2. Note: N nodes give N-1 subdivisions.\n"
                   " - The two guiding edges can have different transfinite curve settings, as long "
                   "as the number of subdivisons is the same\n"
                   " - It is also possible to add a transfinite curve to the non guiding surface "
                   "Edge2, this can have any number of subdivisions")

    part_obj, mesh_obj = create_example(doc, "Triangle Surface Recombined Bumped", explanation)
    part_obj.Shape = Part.makeFace(triangle).translate(FreeCAD.Vector(30,0,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1"])]
    tf_surf.Recombine = True
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge1", "Edge3"])]
    tf_curv.Distribution = "Bump"
    tf_curv.Coefficient = 5

    # 4: simple 3 sided face with tf surface, quad mesh but with changed orientation
    explanation = ("Sometimes the default orientation of the transfinite meshing is not wanted, and "
                   "we want to use different guide edges. This can be achieved with specifying the "
                   "vertex order manual in the transfinite surface\n\n"
                   " - The order of selection is important: The vertex which connects both guide edges "
                   "needs to be the first one in the list\n"
                   " - You always need to add all 3 vertexes\n"
                   " - This only works for a single surface, when specifying vertexes you cannot use "
                   "multiple surfaces in that object. But you can add more transfinite surface objects, "
                   "each having a single face with vertexes")

    part_obj, mesh_obj = create_example(doc, "Triangle Surface Recombined Orientated", explanation)
    part_obj.Shape = Part.makeFace(triangle).translate(FreeCAD.Vector(45,0,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1", "Vertex3", "Vertex2", "Vertex1"])]
    tf_surf.Recombine = True

    smalltriangle = Part.makePolygon([FreeCAD.Vector(0,0,0),
                                      FreeCAD.Vector(10,0,0),
                                      FreeCAD.Vector(5,5,0),
                                      FreeCAD.Vector(0,0,0)])

    # 5: simple 3 sided face with tf surface, made to work with transfinite curves
    explanation = ("If the guiding edges do not have the same amount of subdivisions by default we need to ensure "
                   "this manually. In this example we have chosen to use transfinite curves to ensure both "
                   "edges have the same amount of nodes, and hence subdivisions")

    part_obj, mesh_obj = create_example(doc, "Triangle Surface Recombined Unequal Guided", explanation)
    part_obj.Shape = Part.makeFace(smalltriangle).translate(FreeCAD.Vector(60,0,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1"])]
    tf_surf.Recombine = True
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge1", "Edge3"])]

    # 6: simple 3 sided face with tf surface, made to work with changed orientation
    explanation = ("To overcome the unequal amount of subdivisions in this example we chose different guiding edges, "
                   "choosing the two edges which have equal length and hence equal subdivision count. Before we did this "
                   "by choosing all 3 vertices. However, we can have it easier and only selecting the 1 corner vertex.")

    part_obj, mesh_obj = create_example(doc, "Triangle Surface Recombined Unequal Oriented", explanation)
    part_obj.Shape = Part.makeFace(smalltriangle).translate(FreeCAD.Vector(75,0,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1", "Vertex3"])]
    tf_surf.Recombine = True

    quad = Part.makePolygon([FreeCAD.Vector(0,0,0),
                             FreeCAD.Vector(10,0,0),
                             FreeCAD.Vector(10,10,0),
                             FreeCAD.Vector(0,10,0),
                             FreeCAD.Vector(0,0,0)])

    # 7: simple 4 sided face with tf surface
    explanation = ("A 4 sided surface can be made transfinite. For this the opposite edges need to have "
                   "the same amount of subdivisions on them.\n\n"
                   " - For 4 sided faces the edge order does not matter, only the ones opposite to each other are relevant\n"
                   " - In this example the number of subdivisions on those edges is equal, as they have the same length, "
                   "and we use CharachteristicLengthMax setting to subdivide")

    part_obj, mesh_obj = create_example(doc, "Quad Surface", explanation)
    part_obj.Shape = Part.makeFace(quad).translate(FreeCAD.Vector(0,-15,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1"])]

    # 8: simple 4 sided face with tf surface and quad mesh
    explanation = ("The transfinite surface of the left example can be created with quad elements "
                   "instead of triangles by using the Recombine option")

    part_obj, mesh_obj = create_example(doc, "Quad Surface Recombined", explanation)
    part_obj.Shape = Part.makeFace(quad).translate(FreeCAD.Vector(15,-15,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1"])]
    tf_surf.Recombine = True

    # 9: simple 4 sided face with tf surface, quad mesh and transfinite curve guiding
    explanation = ("All edges of the transfinite surface can have arbitrary node distribution, and "
                   "we can manipulate it for example with transfinite curves\n\n"
                   " - The requirement of same amount of subdivisions for opposite edges still holds. "
                   "This is why we applied the same transfinite curve to the opposite edges\n"
                   " - The opposite edges can have different transfinite curve settings, as long "
                   "as the subdivision count is the same (in this case node count is the same)\n"
                   " - It is also possible to only add a transfinite curve to some edges. no matter "
                   "which or how many")

    part_obj, mesh_obj = create_example(doc, "Quad Surface Recombined Bumped", explanation)
    part_obj.Shape = Part.makeFace(quad).translate(FreeCAD.Vector(30,-15,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1"])]
    tf_surf.Recombine = True
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge1", "Edge3"])]
    tf_curv.Distribution = "Bump"
    tf_curv.Coefficient = 5
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge2", "Edge4"])]
    tf_curv.Distribution = "Bump"
    tf_curv.Coefficient = 5
    tf_curv.Invert = True


    # 10: distorded 4 sided face with tf surface, quad mesh and transfinite curve guiding
    distorted_quad = Part.makePolygon([FreeCAD.Vector(0,0,0),
                                       FreeCAD.Vector(10,3,0),
                                       FreeCAD.Vector(7,10,0),
                                       FreeCAD.Vector(3,5,0),
                                       FreeCAD.Vector(0,0,0)])

    explanation = ("If the edges of the face are not equal length we need to ensure that the subdivision counts"
                   "are correct. The simplest way is to make all 4 edges transfinite.")

    part_obj, mesh_obj = create_example(doc, "Quad Distorted Surface Recombined Guided", explanation)
    part_obj.Shape = Part.makeFace(distorted_quad).translate(FreeCAD.Vector(45,-15,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1"])]
    tf_surf.Recombine = True
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge1", "Edge2", "Edge3", "Edge4"])]


    # 11: Unregular 4 sided face with tf surface, quad mesh and transfinite curve guiding
    bSpline1 = Part.BSplineCurve()
    bSpline1.interpolate([FreeCAD.Vector(0,0,0), FreeCAD.Vector(3.3,0.5,0),
                          FreeCAD.Vector(6.6,-0.5,0), FreeCAD.Vector(10,0,0)])
    bSpline2 = Part.BSplineCurve()
    bSpline2.interpolate([FreeCAD.Vector(10,0,0), FreeCAD.Vector(10.5,3.3,0),
                          FreeCAD.Vector(9.5,6.6,0), FreeCAD.Vector(10,10,0)])
    bSpline3 = Part.BSplineCurve()
    bSpline3.interpolate([FreeCAD.Vector(10,10,0), FreeCAD.Vector(6.6,10.5,0),
                          FreeCAD.Vector(3.3,9.5,0), FreeCAD.Vector(0,10,0)])
    bSpline4 = Part.BSplineCurve()
    bSpline4.interpolate([FreeCAD.Vector(0,10,0), FreeCAD.Vector(-0.5,7,0), FreeCAD.Vector(0.5,5,0),
                          FreeCAD.Vector(-0.5,3,0), FreeCAD.Vector(0,0,0)])

    wire = Part.Wire([Part.Edge(bSpline1), Part.Edge(bSpline2), Part.Edge(bSpline3), Part.Edge(bSpline4)])
    face = Part.Face(wire)

    explanation = ("The edges of the 4 sided face can be of any form, and also be hevaily curved. This holds also "
                   "for triangles, by the way.")

    part_obj, mesh_obj = create_example(doc, "Quad Curved Surface Recombined Guided", explanation)
    part_obj.Shape = face.translate(FreeCAD.Vector(60,-15,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1"])]
    tf_surf.Recombine = True
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge1", "Edge2", "Edge3", "Edge4"])]


    # 12: Unregular 3D 4 sided face with tf surface, quad mesh and transfinite curve guiding
    bSpline1 = Part.BSplineCurve()
    bSpline1.interpolate([FreeCAD.Vector(0,0,0), FreeCAD.Vector(3.3,1,1),
                          FreeCAD.Vector(6.6,-1,-1), FreeCAD.Vector(10,0,1)])
    bSpline2 = Part.BSplineCurve()
    bSpline2.interpolate([FreeCAD.Vector(10,0,1), FreeCAD.Vector(10,3.3,0),
                          FreeCAD.Vector(10,6.6,2), FreeCAD.Vector(10,10,1)])
    bSpline3 = Part.BSplineCurve()
    bSpline3.interpolate([FreeCAD.Vector(10,10,1), FreeCAD.Vector(6.6,11,-1),
                          FreeCAD.Vector(3.3,9,1), FreeCAD.Vector(0,10,0)])
    bSpline4 = Part.BSplineCurve()
    bSpline4.interpolate([FreeCAD.Vector(0,10,0), FreeCAD.Vector(-1,7,-1), FreeCAD.Vector(1,5,1),
                          FreeCAD.Vector(-1,3,-1), FreeCAD.Vector(0,0,0)])

    wire = Part.Wire([Part.Edge(bSpline1), Part.Edge(bSpline2), Part.Edge(bSpline3), Part.Edge(bSpline4)])
    face = Part.makeFilledFace(wire)

    explanation = ("And of course all transfinite settings works for 3D cuved faces as well")

    part_obj, mesh_obj = create_example(doc, "Quad 3D Curved Surface Recombined Guided", explanation)
    part_obj.Shape = face.translate(FreeCAD.Vector(75,-15,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, ["Face1"])]
    tf_surf.Recombine = True
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge1", "Edge2", "Edge3", "Edge4"])]


    # 13: Hexagon face with 3-sided tf surface
    points = []
    for i in range(6):
        angle = i * 2 * math.pi / 6
        x = 5*math.cos(angle)
        y = 5*math.sin(angle)
        points.append(FreeCAD.Vector(x, y, 0))

    points.append(points[0]) # to close wire
    hexagon = Part.makePolygon(points)

    explanation = ("It is possible to create transfinite surface meshes for faces with an arbitrary amount of edges. "
                   "However, for this we need to specify what the corner points are, to guide the transfinite algorithm "
                   "This is done by providing the relevant vertexes in the reference section of transfinite surfaces\n\n"
                   " - There can be 3 or 4 corner points, making the face a 3 or 4 sided transfinite surface. "
                   "This example uses 3.\n"
                   " - The amount of subdivisions between the corner points need to be equal for the guiding surface. This "
                   "is the same as for normal 3 sided faces, just now one side is created of multiple edges, for which "
                   "the subdivisions are added up."
                   " - In this example each edge has the same length, hence subdivisions. As each side of the transfinite surface "
                   "consists of 2 edges, the overall subdivision count is the same per side")

    part_obj, mesh_obj = create_example(doc, "Hexagon 3-Sided Surface Recombined", explanation)
    part_obj.Shape = Part.makeFace(hexagon).translate(FreeCAD.Vector(5,-25,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, ["Face1", "Vertex1", "Vertex3", "Vertex5"])]

    # 13: Hexagon face with 4-sided tf surface
    explanation = ("The hexagon can also be turned into a 4 sided transfinite surface by selecting 4 corner vertexes."
                   "For 4 sided transfinite surfaces the opposite sides need to have the same amount of subdivisions."
                   "Therefore we group the 6 edges in a way so that the sides with 2 edges face each other, and the"
                   "sides with 1 edge face each other"
                   )

    part_obj, mesh_obj = create_example(doc, "Hexagon 4-Sided Surface Recombined", explanation)
    part_obj.Shape = Part.makeFace(hexagon).translate(FreeCAD.Vector(20,-25,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, ["Face1", "Vertex2", "Vertex3", "Vertex5", "Vertex6"])]

    # 14: Hexagon face with 3-sided uneven distributed tf surface
    explanation = ("The 6 edges of the hexagon can also be distributed unevenly into 3 sides of the transfinite surface. "
                   "We then need to ensure the same subdivision count for the guiding sides. This is done with transfinite curves\n\n"
                   " - The subdivisions of an edge can be calculated from the nodes used on that edge. For N nodes there are N-1 "
                   " subdivisions\n"
                   " - When connecting multiple edges into a single side of a transfinite surface the subdivisions of that side can"
                   " be simply calculated as the addition of all its edge subdivisions\n"
                   " - Therefore, the total subdivision count of N connected equally subdivided edges is N * (Node - N). This needs "
                   "to be considered when setting the transfinite curve node number for each edge\n"
                   " - For our example:\n"
                   "    - Side 1: 13 Nodes/Edge leads to 12 subdivisions (1*(13-1)=12)\n"
                   "    - Side 2:  5 Nodes/Edge leads to 12 subdivisions (3*(5-1)=12)\n"
                   "    - Side 3:  7 Nodes/Edge leads to 12 subdivisions (2*(7-1)=12) "
                   )

    part_obj, mesh_obj = create_example(doc, "Hexagon 3-Sided Non-symmetric Surface Recombined", explanation)
    part_obj.Shape = Part.makeFace(hexagon).translate(FreeCAD.Vector(35,-25,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, ["Face1", "Vertex1", "Vertex2", "Vertex5"])]
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge1"])]
    tf_curv.Nodes = 13
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge2", "Edge3", "Edge4"])]
    tf_curv.Nodes = 5
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge5", "Edge6"])]
    tf_curv.Nodes = 7

    # 15: Hexagon face with 4-sided uneven distributed tf surface
    explanation = ("The 6 edges hexagon can also be used as 4 sided transfinite surface, as shown in this example. The same requirement "
                   "on the sides subdivisions hold as before: Opposite sides of the transfinite surface must have the same amount of "
                   "subdivisions. In this example we achieve this with transfinite curves.\n\n"
                   " - The total subdivision count of N connected equally subdivided edges is N * (Node - N)\n"
                   " - For our example:\n"
                   "    - Side 1:  5 Nodes/Edge leads to 12 subdivisions (3*(5-1)=12)\n"
                   "    - Side 3: 13 Nodes/Edge leads to 12 subdivisions (1*(13-1)=12)\n"
                   "    - Side 2 & 4: 6 Nodes/Edge leads to 5 subdivisions (1*(6-1)=5) ")

    part_obj, mesh_obj = create_example(doc, "Hexagon 4-Sided Non-symmetric Surface Recombined", explanation)
    part_obj.Shape = Part.makeFace(hexagon).translate(FreeCAD.Vector(50,-25,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, ["Face1", "Vertex1", "Vertex4", "Vertex5", "Vertex6"])]
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge1", "Edge2", "Edge3"])]
    tf_curv.Nodes = 5
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge5"])]
    tf_curv.Nodes = 13
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge4", "Edge6"])]
    tf_curv.Nodes = 6


    # 16: Two faces
    triangle_2f = Part.makePolygon([FreeCAD.Vector(0,5,0),
                                    FreeCAD.Vector(5,5,0),
                                    FreeCAD.Vector(2.5,9,0),
                                    FreeCAD.Vector(0,5,0)])
    quad_2f = Part.makePolygon([FreeCAD.Vector(0,0,0),
                                FreeCAD.Vector(5,0,0),
                                FreeCAD.Vector(5,5,0),
                                FreeCAD.Vector(0,5,0),
                                FreeCAD.Vector(0,0,0)])

    combined = Part.makeFace(triangle_2f).generalFuse(Part.makeFace(quad_2f))[0]

    explanation = ("It is possible to mesh multiple connected faces with transfinite surfaces. In this example the quad faces sides are "
                   "equally long, and the triangles sides are also same length. Therefore we can directly make the two surfaces "
                   "transfinite in a single object, as the sides subdivisions will match correctly")

    part_obj, mesh_obj = create_example(doc, "Multiface Surface", explanation)
    part_obj.Shape = combined.copy().translate(FreeCAD.Vector(2.5,-45,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, ["Face1", "Face2"])]

    # 17: Two faces, one oriented
    explanation = ("If we want to reorient the triangle we can do this with the manual definition of the corner points. As shown in "
                   "earlier examples this requires adding the Vertexes in the surface definition. As this can only be done for a single "
                   "face, the second face requires a separate transfinite surface object.\n\n"
                   " - The second transfinite surface refinement could have selected multiple faces again\n"
                   " - You can combine as many transfinite surface refinements as you like")

    part_obj, mesh_obj = create_example(doc, "Multiface Surface Oriented", explanation)
    part_obj.Shape = combined.copy().translate(FreeCAD.Vector(17.5,-45,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, ["Face1", "Vertex3", "Vertex2", "Vertex1"])]
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, ["Face2"])]

    # 18: Two faces, one oriented, guided
    explanation = ("We can further specify transfinite curves to have more control over the meshing. Those definitions are used by both "
                   "transfinite surface definitions, and can also be applied to the shared edge")

    part_obj, mesh_obj = create_example(doc, "Multiface Surface Oriented Bumped", explanation)
    part_obj.Shape = combined.copy().translate(FreeCAD.Vector(32.5,-45,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, ["Face1", "Vertex3", "Vertex2", "Vertex1"])]
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, ["Face2"])]
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, ["Edge1", "Edge4"])]
    tf_curv.Nodes = 10
    tf_curv.Distribution = "Bump"
    tf_curv.Coefficient = 5

    # 19: Wedge as 5 sided volume
    wedge = Part.makeFace(triangle).extrude(FreeCAD.Vector(2,0,10))
    explanation = ("Transfinite volumes extend the principles of surfaces into 3D. Every 5 or 6 sided volume can be meshed with this "
                   "structured algorithm. The important boundary condition is that all surfaces of the volume must be set to be "
                   "transfinite. \n\n"
                   " - All 5 faces of the volume can be handled in a single transfinite surface refinement\n"
                   " - As all edges of the volume have equal length, they subdivide equally, and hence the transfinite "
                   "surfaces work just as is"
                   " - Note that from the outside a transfinite volume looks exactly the same as applying only the 5 transfinite surfaces. "
                   "However, the elements inside the volume are not structured without the application of transfinite volume.")

    part_obj, mesh_obj = create_example(doc, "Wedge Volume", explanation)
    part_obj.Shape = wedge.copy().translate(FreeCAD.Vector(0,-60,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, [f"Face{i}" for i in range(1,6)])]
    tf_vol = ObjectsFem.makeMeshTransfiniteVolume(doc, mesh_obj)
    tf_vol.References = [(part_obj, ["Solid1"])]

    # 20: Wedge as 5 sided volume
    explanation = ("The transfinite volume derives its element types from the surfaces. If all transfinite surfaces are recombined, "
                   "the volume will ocnsist of hexaedron elements.")

    part_obj, mesh_obj = create_example(doc, "Wedge Volume Recombined", explanation)
    part_obj.Shape = wedge.copy().translate(FreeCAD.Vector(15,-60,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, [f"Face{i}" for i in range(1,6)])]
    tf_vol = ObjectsFem.makeMeshTransfiniteVolume(doc, mesh_obj)
    tf_vol.References = [(part_obj, ["Solid1"])]

    # 21: Wedge as 5 sided volume, mixed elements
    explanation = ("The surfaces of the volume can be of mixed type, some recombined and some not. This leads to a combination of "
                   "hexahedron elements and pyramids within the transfinite volume. This mixing of elements must be explicitly enabled"
                   "with the mixed mesh option of the volume")

    part_obj, mesh_obj = create_example(doc, "Wedge Volume Mixed", explanation)
    part_obj.Shape = wedge.copy().translate(FreeCAD.Vector(30,-60,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, [f"Face{i}" for i in range(1,4)])]
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, [f"Face{i}" for i in range(4,6)])]
    tf_vol = ObjectsFem.makeMeshTransfiniteVolume(doc, mesh_obj)
    tf_vol.MixedElements = True
    tf_vol.References = [(part_obj, ["Solid1"])]

    # 22: Wedge as 5 sided volume, mixed elements, Bumped
    explanation = ("Further tuning of the structured mesh algorithm can be done with well placed transfinite curves. The curves need "
                   "to be consistent for every surface, then it will be applied to the volume correctly.\n\n"
                   " - If the curves are not consistent to the surfaces some will fail to be generated and hence the volume will fail too")

    part_obj, mesh_obj = create_example(doc, "Wedge Volume Recombined Bumped", explanation)
    part_obj.Shape = wedge.copy().translate(FreeCAD.Vector(45,-60,0))
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, [f"Edge{i}" for i in range(1,10)])]
    tf_curv.Nodes = 7
    tf_curv.Distribution = "Bump"
    tf_curv.Coefficient = 5
    tf_curv.Invert=True
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, [f"Face{i}" for i in range(1,6)])]
    tf_vol = ObjectsFem.makeMeshTransfiniteVolume(doc, mesh_obj)
    tf_vol.MixedElements = True
    tf_vol.References = [(part_obj, ["Solid1"])]

    # Pyramid seems impossible, even though it has 5 sides
    # # 23: Pyramid as 5 sided volume, Recombined
    # point = Part.Vertex(FreeCAD.Vector(5,5,6))
    # pyramid = Part.makeLoft([quad, point], solid=True)
    # explanation = ("Pyramid seems impossible")

    # part_obj, mesh_obj = create_example(doc, "Pyramid Volume Recombined", explanation)
    # part_obj.Shape = pyramid.copy().translate(FreeCAD.Vector(60,-60,0))
    # tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    # tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    # tf_surf.References = [(part_obj, [f"Face{i}" for i in range(1,6)])]
    # tf_vol = ObjectsFem.makeMeshTransfiniteVolume(doc, mesh_obj)
    # tf_vol.MixedElements = True
    # tf_vol.References = [(part_obj, ["Solid1"])]

    # 23: Curved Wedge as 5 sided volume, Bumped
    rotation = Part.makeFace(triangle).revolve(FreeCAD.Vector(15,0,0), FreeCAD.Vector(-0.2,1,0), 120)
    explanation = ("5 Sided volumes work with arbitrarily shapes outlines")

    part_obj, mesh_obj = create_example(doc, "Wedge Volume Curved Recombined Bumped", explanation)
    part_obj.Shape = rotation.copy().translate(FreeCAD.Vector(60,-60,0))
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, [f"Edge{i}" for i in range(1,10)])]
    tf_curv.Nodes = 7
    tf_curv.Distribution = "Bump"
    tf_curv.Coefficient = 5
    tf_curv.Invert=True
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, [f"Face{i}" for i in range(1,6)])]
    tf_vol = ObjectsFem.makeMeshTransfiniteVolume(doc, mesh_obj)
    tf_vol.MixedElements = True
    tf_vol.References = [(part_obj, ["Solid1"])]


    # 24: Cube as 6 sided volume
    cube = Part.makeFace(quad).extrude(FreeCAD.Vector(2,0,10))
    explanation = ("")

    part_obj, mesh_obj = create_example(doc, "Cube Volume", explanation)
    part_obj.Shape = cube.copy().translate(FreeCAD.Vector(0,-75,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.References = [(part_obj, [f"Face{i}" for i in range(1,7)])]
    tf_vol = ObjectsFem.makeMeshTransfiniteVolume(doc, mesh_obj)
    tf_vol.References = [(part_obj, ["Solid1"])]

    # 25: Cube as 6 sided volume, quad meshing
    explanation = ("")

    part_obj, mesh_obj = create_example(doc, "Cube Volume Recombined", explanation)
    part_obj.Shape = cube.copy().translate(FreeCAD.Vector(15,-75,0))
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, [f"Face{i}" for i in range(1,7)])]
    tf_vol = ObjectsFem.makeMeshTransfiniteVolume(doc, mesh_obj)
    tf_vol.References = [(part_obj, ["Solid1"])]

    # 26: Cube as 6 sided volume, quad meshing, bumped
    explanation = ("")

    part_obj, mesh_obj = create_example(doc, "Cube Volume Recombined Bumped", explanation)
    part_obj.Shape = cube.copy().translate(FreeCAD.Vector(30,-75,0))
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, [f"Edge{i}" for i in range(1,13)])]
    tf_curv.Distribution = "Bump"
    tf_curv.Coefficient = 5
    tf_curv.Invert=True
    tf_curv.Nodes = 7
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, [f"Face{i}" for i in range(1,7)])]
    tf_vol = ObjectsFem.makeMeshTransfiniteVolume(doc, mesh_obj)
    tf_vol.References = [(part_obj, ["Solid1"])]


    # 27: Unregular 3D 4 sided cube
    bSpline1 = Part.BSplineCurve()
    bSpline1.interpolate([FreeCAD.Vector(0,0,0), FreeCAD.Vector(3.3,1,1),
                          FreeCAD.Vector(6.6,-1,-1), FreeCAD.Vector(10,0,1)])
    bSpline2 = Part.BSplineCurve()
    bSpline2.interpolate([FreeCAD.Vector(10,0,1), FreeCAD.Vector(10,3.3,0),
                          FreeCAD.Vector(10,6.6,2), FreeCAD.Vector(10,10,1)])
    bSpline3 = Part.BSplineCurve()
    bSpline3.interpolate([FreeCAD.Vector(10,10,1), FreeCAD.Vector(6.6,11,-1),
                          FreeCAD.Vector(3.3,9,1), FreeCAD.Vector(0,10,0)])
    bSpline4 = Part.BSplineCurve()
    bSpline4.interpolate([FreeCAD.Vector(0,10,0), FreeCAD.Vector(-1,7,-1), FreeCAD.Vector(1,5,1),
                          FreeCAD.Vector(-1,3,-1), FreeCAD.Vector(0,0,0)])

    wire = Part.Wire([Part.Edge(bSpline1), Part.Edge(bSpline2), Part.Edge(bSpline3), Part.Edge(bSpline4)])
    face = Part.makeFilledFace(wire)
    cube = face.revolve(FreeCAD.Vector(15,0,0), FreeCAD.Vector(-0.2,1,0), 120)

    explanation = ("And of course all transfinite settings works for 3D cuved faces as well")

    part_obj, mesh_obj = create_example(doc, "Cube Volume Curved Recombined Bumped", explanation)
    part_obj.Shape = cube.translate(FreeCAD.Vector(45,-75,0))
    tf_curv = ObjectsFem.makeMeshTransfiniteCurve(doc, mesh_obj)
    tf_curv.References = [(part_obj, [f"Edge{i}" for i in range(1,13)])]
    tf_curv.Distribution = "Bump"
    tf_curv.Coefficient = 5
    tf_curv.Invert=True
    tf_curv.Nodes = 7
    tf_surf = ObjectsFem.makeMeshTransfiniteSurface(doc, mesh_obj)
    tf_surf.Recombine = True
    tf_surf.References = [(part_obj, [f"Face{i}" for i in range(1,7)])]
    tf_vol = ObjectsFem.makeMeshTransfiniteVolume(doc, mesh_obj)
    tf_vol.References = [(part_obj, ["Solid1"])]


    return doc







