# -*- coding: utf-8 -*-
# (c) 18Turbo, 2022

__Name__ = 'Generador3D'
__Comment__ = 'Create a solid with 2 or 3 sketches (views)'
__Author__ = '18Turbo'
__Version__ = '0.1.1'
__Date__ = '2022-09-20'
__License__ = 'LGPL-2.0-or-later'
__Web__ = 'https://github.com/18turbo/Generador3DFreeCAD'
__Wiki__ = 'https://github.com/18turbo/Generador3DFreeCAD/wiki'
__Icon__ = 'G3d.svg'
__Help__ = ''
__Status__ = 'beta'
__Requires__ = 'FreeCAD >= v0.20'
__Contact__ = 'https://github.com/18turbo'
__Communication__ = 'https://github.com/18turbo/Generador3DFreeCAD/issues'
__Files__ = 'G3d.svg,g3d/ui/g3d15.ui'


# (En español:)
# Funcionalidad:    Genera una pieza 3D a partir de 2 o 3 sketch, cada uno en una vista distinta (planta, frontal o lateral)
#
# Descripción:      Una vez ejecutada la macro, habrá que asociar cada sketch a cada vista, y pulsar el botón de OK.
#
#
# Notas de versiones:
# v.0.1.0:      Adaptación del código para varios idiomas
#               Corrección de error en la deselección de vistas
#               Cambio de nombres de ficheros
# v.0.0.16:     Corregido errores en la selección de vistas
#               Evita el error de introducir la longitud menor o igual al offset
# v.0.0.15:     Cambiada la interfaz
#               Añadido Offset y Longitud a cada vista
# v.0.0.14:     Adaptación al Addon Manager
# v.0.0.13:     Depuración de código
# v.0.0.12:     Depuración de código
#               Posibilidad de crear una carpeta automáticamente donde meter las vistas y ocultarlas
# v.0.0.11:     Incluida tolerancia 3D para evitar problemas en los límites.
# v.0.0.10:     Corrección de errores en sketch negativos
# v.0.0.9:      Cambio de interfaz y corrección de errores. Beta funcional.
# v.0.0.8:      Corrección de errores
# v.0.0.7:      Corrección de errores
# v.0.0.6:      Primera versión BETA
# v.0.0.5 ALFA: Genera la pieza 3D con 3 vistas
# v.0.0.4 ALFA: Arregla la selección en las vistas en la pestaña Task (Tarea)
#
#
# BetaTester: Rafael García Rodríguez
# BetaTester: 18Turbo

# Traducción inglés:    Rocco Vicedomini


import FreeCAD as app
import FreeCADGui as gui
import Part

from PySide import QtCore, QtGui
from FreeCAD import Base


strLang = [
    [ # Español
        "Generador 3D",
        "Seleccione un sketch (boceto) en 2 o 3 vistas:",
        "Frontal -Alzado- (Front/Rear):",
        "Marque esta casilla si quiere activar la vista frontal y asígnele un sketch (boceto)",
        "Lateral -Perfil- (Right/Left):",
        "Marque esta casilla si quiere activar la vista lateral y asígnele un sketch (boceto)",
        "Planta -Sup/Inf- (Top/Bott):",
        "Marque esta casilla si quiere activar la vista en planta y asígnele un sketch (boceto)",
        "Tol. 3D (mm.):",
        "Es la distancia en mm. que extruirá de más. No debe influir en el objeto final. Si no desea tolerancia, ponga este valor a 0",
        "Crear un Grupo (Carpeta) para las vistas (sketches)",
        "Ocultar las vistas al generar el sólido",
        "Fusión (interesección)",
        "Versión:",
        "Información Breve de Uso:",
        "Cree un boceto (sketch) para cada vista con la que desee generar un sólido 3D. Necesitará al menos 2 bocetos.",
        "Luego pinche en el icono del script y seleccione un boceto para cada vista.",
        "Finalmente, pulse OK para generar el sólido.",
        "Documentación:",
        "Offset",
        "Es la distancia desde la cual empezará la extrusión en el eje Y (desplazamiento en Y)",
        "Longitud",
        "Es la distancia total de extrusión frontal (en el eje Y), que siempre será mayor que el Offset Frontal",
        "Offset",
        "Es la distancia desde la cual empezará la extrusión en el eje X (desplazamiento en X)",
        "Longitud",
        "Es la distancia total de extrusión lateral (en el eje X), que siempre será mayor que el Offset Lateral",
        "Offset",
        "Es la distancia desde la cual empezará la extrusión en el eje Z (desplazamiento en Z)",
        "Longitud",
        "Es la distancia total de extrusión en planta (en el eje Z), que siempre será mayor que el Offset en Planta",
        "Pieza3D",
        "VistasPieza3D"
    ],
    [ # English
        ## Strings for translation into any language (English Strings)

        "3D Generator",
        "Select a sketch in 2 or 3 views:",
        "Front view - (Front/Rear):",
        "Check this box if you want to activate the front view and assign it a sketch",
        "Side view - (Right/Left):",
        "Check this box if you want to activate the side view and assign it a sketch",
        "Plan view - (Top/Bott):",
        "Check this box if you want to activate the plan view and assign it a sketch",
        "Tol. 3D (mm):",
        "Length in millimetres. This should not influence the final object. If you do not need this tolerance, set this value to 0",
        "Make a new group (folder) with views (sketches)",
        "Hide views when generating the solid",
        "Fusion (intersection)",
        "Version:",
        "Brief guide to use the script:",
        "Create a sketch for each view required to generate your 3D solid. At least two sketches will be required.",
        "Then click on the script icon and select a sketch for each view.",
        "Finally, press the OK button to generate the solid.",
        "Documentation:",
        "Offset",
        "Offset from the sketch plane from which the extrusion along the Y axis will start",
        "Length",
        "Total length of the frontal extrusion along the Y axis (it must be always larger than the frontal offset)",
        "Offset",
        "Offset from the sketch plane from which the extrusion along the X axis will start",
        "Length",
        "Total length of the lateral extrusion along the X axis (it must be always larger than the lateral offset)",
        "Offset",
        "Offset from the sketch plane from which the extrusion along the Z axis will start",
        "Length",
        "Total length of the vertical extrusion along the Z axis (it must be always larger than the vertical offset)",
        "3D-Part",
        "3D-Part_Views"
    ],
    [ ## Stringhe per la traduzione in qualsiasi lingua (stringhe in italiano)

        "Generatore 3D",
        "Seleziona uno schizzo in 2 o 3 viste:",
        "Vista frontale - (Fronte/Retro):",
        "Spunta questa casella se desideri utilizzare la vista frontale ed assegnarle uno schizzo",
        "Vista laterale - (Destra/Sinistra):",
        "Spunta questa casella se desideri utilizzare la vista laterale ed assegnarle uno schizzo",
        "Vista in pianta - (Alto/Basso):",
        "Spunta questa casella se desideri utilizzare la vista in pianta ed assegnarle uno schizzo",
        "Tol. 3D (mm):",
        "Lunghezza in mm di sovraestrusione. Non dovrebbe influenzare l'oggetto finale. Se non si necessita questa tolleranza impostare il valore relativo a 0 mm",
        "Crea un nuovo gruppo (cartella) con le viste (sketch)",
        "Nasconde le viste quando viene generato il solido",
        "Fusione (intersezione)",
        "Versione:",
        "Breve guida all'uso dello script:",
        "Crea uno sketch per ogni vista necessaria a generare il solido. Sono necessari almeno due sketch.",
        "Quindi clicca sull'icona dello script e seleziona uno schizzo per ciascuna vista necessaria.",
        "Infine, premi il pulsante OK per generare il solido.",
        "Documentazione:",
        "Offset",
        "Distanza rispetto al piano dello sketch frontale da cui inizierà l'estrusione lungo l'asse Y (spostamento in Y)",
        "Lunghezza",
        "Lunghezza totale dell'estrusione frontale lungo l'asse Y (dovrà essere sempre più grande dell'offset frontale)",
        "Offset",
        "Distanza rispetto al piano dello sketch laterale da cui inizierà l'estrusione lungo l'asse X (spostamento in X)",
        "Lunghezza",
        "Lunghezza totale dell'estrusione laterale lungo l'asse X (dovrà essere sempre più grande dell'offset laterale)",
        "Offset",
        "Distanza rispetto al piano dello sketch in pianta da cui inizierà l'estrusione lungo l'asse Z (spostamento in Z)",
        "Lunghezza",
        "Lunghezza totale dell'estrusione verticale lungo l'asse Z (dovrà essere sempre più grande dell'offset in pianta)",
        "Pezzo3D",
        "Pezzo3D_Viste"
    ]
        ]


minimoExtrusion = app.Units.Quantity('0.01 mm')


class Generator3d:

    def __init__(self):
        self.doc = app.activeDocument()
        if not self.doc:
            app.Console.PrintWarning('No active document\n')
            return

        ui_path = app.getUserMacroDir(True) + '/g3d/ui/g3d15.ui'
        self.form = gui.PySideUic.loadUi(ui_path)

        # Whether views are active.
        self.frontal = False
        self.lateral = False
        self.planta = False

        # Find all sketches and add to the widgets.
        for candidate_sketch in app.activeDocument().Objects:
            if candidate_sketch.TypeId == 'Sketcher::SketchObject':
                self.form.listWidgetFrontal.addItem(candidate_sketch.Label)
                self.form.listWidgetLateral.addItem(candidate_sketch.Label)
                self.form.listWidgetPlanta.addItem(candidate_sketch.Label)

        self.form.listWidgetFrontal.itemSelectionChanged.connect(self.changeSelectionFront)
        self.form.listWidgetLateral.itemSelectionChanged.connect(self.changeSelectionLat)
        self.form.listWidgetPlanta.itemSelectionChanged.connect(self.changeSelectionTop)

        self.form.offsetF.valueChanged.connect(self.changeValueFront)
        self.form.offsetL.valueChanged.connect(self.changeValueLat)
        self.form.offsetP.valueChanged.connect(self.changeValueTop)

        self.form.etFrontal.clicked.connect(self.changeCheckedFront)
        self.form.etLateral.clicked.connect(self.changeCheckedLat)
        self.form.etPlanta.clicked.connect(self.changeCheckedTop)

        self.initStrings()

    def initStrings(self):
        tr = self.translate
        if g_num_lang != 0:
            title = "(" + strLang[g_num_lang][0] + ")"
        else:
            title = ""
        self.form.setWindowTitle(__Name__ + " " + title)
        self.form.etInfo.setText(tr('Seleccione un sketch (boceto) en 2 o 3 vistas:'))
        self.form.etFrontal.setTitle(tr('Frontal -Alzado- (Front/Rear):'))
        self.form.etFrontal.setToolTip(tr('Marque esta casilla si quiere activar la vista frontal y asígnele un sketch (boceto)'))
        self.form.etLateral.setTitle(tr('Lateral -Perfil- (Right/Left):'))
        self.form.etLateral.setToolTip(tr('Marque esta casilla si quiere activar la vista lateral y asígnele un sketch (boceto)'))
        self.form.etPlanta.setTitle(tr('Planta -Sup/Inf- (Top/Bott):'))
        self.form.etPlanta.setToolTip(tr('Marque esta casilla si quiere activar la vista en planta y asígnele un sketch (boceto)'))
        self.form.etTolerancia.setText(tr('Tol. 3D (mm.):'))
        self.form.etTolerancia.setToolTip(tr('Es la distancia en mm. que extruirá de más. No debe influir en el objeto final. Si no desea tolerancia, ponga este valor a 0'))
        self.form.meterVistasCarpeta.setText(tr('Crear un Grupo (Carpeta) para las vistas (sketches)'))
        self.form.ocultarVistas.setText(tr('Ocultar las vistas al generar el sólido'))
        self.form.radioInterseccion.setText(tr('Fusión (interesección)'))
        self.form.etVersion.setText(tr('Versión:'))
        self.form.etNumVersion.setText(__Version__ + " " + __Status__)
        self.form.etInfoUso.setText(tr('Información Breve de Uso:'))
        descripcionInfoUso = (tr('Cree un boceto (sketch) para cada vista con la que desee generar un sólido 3D. Necesitará al menos 2 bocetos.')
                + " " + tr('Luego pinche en el icono del script y seleccione un boceto para cada vista.')
                + " " + tr('Finalmente, pulse OK para generar el sólido.'))
        self.form.descInfoUso.setText(descripcionInfoUso)
        self.form.etDoc.setText(tr('Documentación:'))
        self.form.etOffsetF.setText(tr('Offset'))
        self.form.etOffsetF.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje Y (desplazamiento en Y)'))
        self.form.offsetF.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje Y (desplazamiento en Y)'))
        self.form.etOffsetL.setText(tr('Offset'))
        self.form.etOffsetL.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje X (desplazamiento en X)'))
        self.form.offsetL.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje X (desplazamiento en X)'))
        self.form.etOffsetP.setText(tr('Offset'))
        self.form.etOffsetP.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje Z (desplazamiento en Z)'))
        self.form.offsetP.setToolTip(tr('Es la distancia desde la cual empezará la extrusión en el eje Z (desplazamiento en Z)'))
        self.form.etLongitudF.setText(tr('Longitud'))
        self.form.etLongitudF.setToolTip(tr('Es la distancia total de extrusión frontal (en el eje Y), que siempre será mayor que el Offset Frontal'))
        self.form.longF.setToolTip(tr('Es la distancia total de extrusión frontal (en el eje Y), que siempre será mayor que el Offset Frontal'))
        self.form.etLongitudL.setText(tr('Longitud'))
        self.form.etLongitudL.setToolTip(tr('Es la distancia total de extrusión lateral (en el eje X), que siempre será mayor que el Offset Lateral'))
        self.form.longL.setToolTip(tr('Es la distancia total de extrusión lateral (en el eje X), que siempre será mayor que el Offset Lateral'))
        self.form.etLongitudP.setText(tr('Longitud'))
        self.form.etLongitudP.setToolTip(tr('Es la distancia total de extrusión en planta (en el eje Z), que siempre será mayor que el Offset en Planta'))
        self.form.longP.setToolTip(tr('Es la distancia total de extrusión en planta (en el eje Z), que siempre será mayor que el Offset en Planta'))

    def translate(self, text):
        if (text not in strLang[0]) or (g_num_lang < 0) or (g_num_lang >= len(strLang)):
            return text
        primerIdioma = strLang[0]
        indice = primerIdioma.index(text)
        if (indice == -1) or (indice >= len(strLang[g_num_lang])):
            return text
        return strLang[g_num_lang][indice]

    def changeValueFront(self):
        self.form.longF.setMinimum(self.form.offsetF.value() + minimoExtrusion)

    def changeValueLat(self):
        self.form.longL.setMinimum(self.form.offsetL.value() + minimoExtrusion)

    def changeValueTop(self):
        self.form.longP.setMinimum(self.form.offsetP.value() + minimoExtrusion)

    def changeCheckedFront(self):
        if not self.form.etFrontal.isChecked():
            self.frontal = False
        else:
            self.frontal = True

    def changeCheckedLat(self):
        if not self.form.etLateral.isChecked():
            self.lateral = False
        else:
            self.lateral = True

    def changeCheckedTop(self):
        if not self.form.etPlanta.isChecked():
            self.planta = False
        else:
            self.planta = True

    def changeSelectionFront(self):
        label = str(self.form.listWidgetFrontal.selectedItems()[0].text())
        self.front_sketch = self.doc.getObjectsByLabel(label)[0]

    def changeSelectionLat(self):
        label = str(self.form.listWidgetLateral.selectedItems()[0].text())
        self.lat_sketch = self.doc.getObjectsByLabel(label)[0]

    def changeSelectionTop(self):
        label = str(self.form.listWidgetPlanta.selectedItems()[0].text())
        self.top_sketch = self.doc.getObjectsByLabel(label)[0]

    def active_view_count(self):
        return sum([
            self.form.etFrontal.isChecked(),
            self.form.etLateral.isChecked(),
            self.form.etPlanta.isChecked()])

    def extrusion(self, nombreExtrusion, nombreEnVista, vista):
        doc = app.activeDocument()
        doc.addObject('Part::Extrusion', nombreExtrusion)
        f = doc.getObject(nombreExtrusion)
        objf = doc.getObjectsByLabel(nombreEnVista)
        f.Base = doc.getObject(objf[0].Name)
        f.DirMode = "Custom"
        if vista == "planta":
            offset = self.form.offsetP.value()
            longitud = self.form.longP.value()
            f.Dir = app.Vector(0.0, 0.0, 1.0)
            if self.lateral:
                maximoLZ = self.lat_sketch.Shape.BoundBox.ZMax
                minimoLZ = self.lat_sketch.Shape.BoundBox.ZMin
                if abs(maximoLZ) > abs(minimoLZ):
                    f.LengthFwd = (abs(maximoLZ) + self.form.tolerancia.value()) * 2
                else:
                    f.LengthFwd = (abs(minimoLZ) + self.form.tolerancia.value()) * 2
            else:
                maximoFZ = self.front_sketch.Shape.BoundBox.ZMax
                minimoFZ = self.front_sketch.Shape.BoundBox.ZMin
                if abs(maximoFZ) > abs(minimoFZ):
                    f.LengthFwd = (abs(maximoFZ) + self.form.tolerancia.value()) * 2
                else:
                    f.LengthFwd = (abs(minimoFZ) + self.form.tolerancia.value()) * 2

        elif vista == "frontal":
            offset = self.form.offsetF.value()
            longitud = self.form.longF.value()
            f.Dir = app.Vector(0.0, 1.0, 0.0)
            if self.lateral:
                maximoLY = self.lat_sketch.Shape.BoundBox.YMax
                minimoLY = self.lat_sketch.Shape.BoundBox.YMin
                if abs(maximoLY) > abs(minimoLY):
                    f.LengthFwd = (abs(maximoLY) + self.form.tolerancia.value()) *2
                else:
                    f.LengthFwd = (abs(minimoLY) + self.form.tolerancia.value()) *2
            else:
                maximoPY = self.top_sketch.Shape.BoundBox.YMax
                minimoPY = self.top_sketch.Shape.BoundBox.YMin
                if abs(maximoPY) > abs(minimoPY):
                    f.LengthFwd = (abs(maximoPY) + self.form.tolerancia.value()) *2
                else:
                    f.LengthFwd = (abs(minimoPY) + self.form.tolerancia.value()) *2

        elif vista == "lateral":
            offset = self.form.offsetL.value()
            longitud = self.form.longL.value()
            f.Dir = app.Vector(1.0, 0.0, 0.0)
            if self.planta:
                maximoPX = self.top_sketch.Shape.BoundBox.XMax
                minimoPX = self.top_sketch.Shape.BoundBox.XMin
                if abs(maximoPX) > abs(minimoPX):
                    f.LengthFwd = (abs(maximoPX) + self.form.tolerancia.value()) *2
                else:
                    f.LengthFwd = (abs(minimoPX) + self.form.tolerancia.value()) *2
            else:
                maximoFX = self.front_sketch.Shape.BoundBox.XMax
                minimoFX = self.front_sketch.Shape.BoundBox.XMin
                if abs(maximoFX) > abs(minimoFX):
                    f.LengthFwd = (abs(maximoFX) + self.form.tolerancia.value()) *2
                else:
                    f.LengthFwd = (abs(minimoFX) + self.form.tolerancia.value()) *2

        f.DirLink = None
        f.Solid = True
        f.Reversed = False
        if (offset > 0) or (longitud > 0):
            f.Symmetric = False
            f.LengthRev = offset * (-1)
            if (longitud > 0):
                f.LengthFwd = longitud
        else:
            f.LengthRev = 0.0
            f.Symmetric = True
        f.TaperAngle = 0.0
        f.TaperAngleRev = 0.0
        doc.recompute()
        return f

    def accept(self):
        #Comprobar que no existe ya una extrusión con este nombre
        numeroCoincidencias = 0
        encontradoCoincidencia = False
        cadenaAdicional = ""
        doc = app.activeDocument()
        for obj in doc.Objects:
            if (('ExtrPlanta18Turbo' in obj.Name) or
                    ('ExtrFrontal18Turbo' in obj.Name) or
                    ('ExtrLateral18Turbo' in obj.Name)):
                numeroCoincidencias = numeroCoincidencias + 1
                encontradoCoincidencia = True
        if encontradoCoincidencia:
            cadenaAdicional = cadenaAdicional + str(numeroCoincidencias)

        if self.active_view_count() >= 2:
            if self.planta:
                # Sketch en Z (Planta XY)
                planta = self.extrusion("ExtrPlanta18Turbo" + cadenaAdicional,
                                        self.top_sketch.Label, "planta")
            if self.frontal:
                # Sketch en Y (Frontal XZ)
                frontal = self.extrusion("ExtrFrontal18Turbo" + cadenaAdicional,
                                         self.front_sketch.Label, "frontal")
            if self.lateral:
                # Sketch en X (Lateral YZ)
                lateral = self.extrusion("ExtrLateral18Turbo" + cadenaAdicional,
                                         self.lat_sketch.Label, "lateral")

            # Intersección
            nombreInterseccion = self.translate('Pieza3D') + cadenaAdicional
            if self.active_view_count() == 3:
                common = doc.addObject("Part::MultiCommon", nombreInterseccion)
                common.Shapes = [planta, frontal, lateral]
            else:
                common = doc.addObject("Part::MultiCommon", nombreInterseccion)
                if (self.planta) and (self.frontal):
                    common.Shapes = [planta, frontal]
                if (self.planta) and (self.lateral):
                    common.Shapes = [planta, lateral]
                if (self.frontal) and (self.lateral):
                    common.Shapes = [frontal, lateral]
            doc.recompute()

            # Meter vistas en Grupo (Carpeta)
            if self.form.meterVistasCarpeta.checkState():
                nombreGrupoVistas = self.translate('VistasPieza3D') + cadenaAdicional
                group = doc.addObject('App::DocumentObjectGroup', nombreGrupoVistas)
                if self.planta:
                    group.addObject(self.top_sketch)
                if self.frontal:
                    group.addObject(self.front_sketch)
                if self.lateral:
                    group.addObject(self.lat_sketch)
            # Ocultar Vistas
            if self.form.ocultarVistas.isChecked():
                if self.planta:
                    self.top_sketch.Visibility = False
                if self.frontal:
                    self.front_sketch.Visibility = False
                if self.lateral:
                    self.lat_sketch.Visibility = False

        gui.Control.closeDialog()


# Se define el g_num_lang con variable global
lang = gui.getLocale()
# Default to English.
g_num_lang = 1
if 'Spanish' in lang:
    g_num_lang = 0
elif 'Italian' in lang:
    g_num_lang = 2

panel = Generator3d()
gui.Control.showDialog(panel)
