# -*- coding: utf-8 -*-
# Exploded Assembly Animation workbench for FreeCAD
# (c) 2016 Javier Martínez García
#***************************************************************************
#*   (c) Javier Martínez García 2016                                       *
#*                                                                         *
#*   This program is free software; you can redistribute it and/or modify  *
#*   it under the terms of the GNU General Public License (GPL)            *
#*   as published by the Free Software Foundation; either version 2 of     *
#*   the License, or (at your option) any later version.                   *
#*   for detail see the LICENCE text file.                                 *
#*                                                                         *
#*   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 Lesser General Public License for more details.                   *
#*                                                                         *
#*   You should have received a copy of the GNU Library General Public     *
#*   License along with FreeCAD; if not, write to the Free Software        *
#*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
#*   USA                                                                   *
#*                                                                         *
#***************************************************************************/

from __future__ import division
import os
import time
import FreeCAD
import Part


# Container python folder 'ExplodedAssembly'
class ExplodedAssemblyFolder:
    def __init__(self, obj):
        obj.addProperty('App::PropertyPythonObject', 'InitialPlacements').InitialPlacements = {}
        obj.addProperty('App::PropertyInteger', 'AnimationStep').AnimationStep = 0
        obj.addProperty('App::PropertyInteger', 'CurrentTrajectory').CurrentTrajectory = 0
        obj.addProperty('App::PropertyBool', 'ResetAnimation').ResetAnimation = False
        obj.addProperty('App::PropertyBool', 'InAnimation').InAnimation = False
        obj.addProperty('App::PropertyBool', 'RemoveAllTrajectories').RemoveAllTrajectories = False
        obj.Proxy = self

    def execute(self, fp):
        if fp.ResetAnimation:
            resetPlacement()
            fp.ResetAnimation = False

        if fp.RemoveAllTrajectories:
            fp.RemoveAllTrajectories = False
            for obj in fp.Group:
                FreeCAD.ActiveDocument.removeObject(obj.Name)

            # reset initial placement dict
            fp.InitialPlacements = {}


class ExplodedAssemblyFolderViewProvider:
    def __init__(self, obj):
        obj.Proxy = self

    def getIcon(self):
        __dir__ = os.path.dirname(__file__)
        return __dir__ + '/icons/WorkbenchIcon.svg'


def checkDocumentStructure():
    # checks the existence of the folder 'ExplodedAssembly' and creates it if not
    try:
        folder = FreeCAD.ActiveDocument.ExplodedAssembly

    except:
        folder = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroupPython', 'ExplodedAssembly')
        ExplodedAssemblyFolder(folder)
        ExplodedAssemblyFolderViewProvider(folder.ViewObject)


# Bolt group trajectory ########################################################
class BoltGroupObject:
    def __init__(self, obj):
        obj.addProperty('App::PropertyPythonObject', 'names').names = []
        obj.addProperty('App::PropertyPythonObject', 'dir_vectors').dir_vectors = []
        obj.addProperty('App::PropertyPythonObject', 'rot_vectors').rot_vectors = []
        obj.addProperty('App::PropertyPythonObject', 'rot_centers').rot_centers = []
        #
        obj.addProperty('App::PropertyFloat', 'Distance').Distance = 20.0
        obj.addProperty('App::PropertyFloat', 'Revolutions').Revolutions = 0.0
        obj.addProperty('App::PropertyFloat', 'AnimationStepTime').AnimationStepTime = 0.0
        obj.addProperty('App::PropertyInteger', 'AnimationSteps').AnimationSteps = 20
        obj.Proxy = self

    def onChanged(self, fp, prop):
        pass

    def execute(self, fp):
        resetPlacement()
        goToEnd()
        FreeCAD.ActiveDocument.ExplodedAssembly.CurrentTrajectory = -1

class BoltGroupObjectViewProvider:
    def __init__(self, obj):
        obj.Proxy = self

    def getIcon(self):
        __dir__ = os.path.dirname(__file__)
        return __dir__ + '/icons/BoltGroup.svg'


def createBoltDisassemble():
    # add object to the Document and initialize it
    SDObj = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroupPython','BoltGroup')
    BoltGroupObject(SDObj)
    BoltGroupObjectViewProvider(SDObj.ViewObject)
    # retrieve selection
    selection = FreeCAD.Gui.Selection.getSelectionEx()
    # try to find the face wich its normal gives the disassemble direction
    dir_vector = FreeCAD.Vector(0, 0, 1)
    for sel_obj in selection:
        for subObject in sel_obj.SubObjects:
            try:
                dir_vector = subObject.normalAt(0, 0)
                break

            except:
                pass

    for sel_obj in selection:
        for subObject in sel_obj.SubObjects:
            try:
                if str(subObject.Curve)[0:6] == 'Circle':
                    # append object name
                    SDObj.names.append(sel_obj.Object.Name)
                    # append unmount direction
                    JSON_dir_vector = (dir_vector[0], dir_vector[1], dir_vector[2])
                    SDObj.dir_vectors.append(JSON_dir_vector)
                    # apend rotation axis
                    JSON_rot_axis = (dir_vector[0], dir_vector[1], dir_vector[2])
                    SDObj.rot_vectors.append(JSON_rot_axis)
                    # append rotation center
                    rot_center = subObject.Curve.Center
                    JSON_rot_center = (rot_center[0], rot_center[1], rot_center[2])
                    SDObj.rot_centers.append(JSON_rot_center)
                    # append initial values for distance, revs, steps
                    SDObj.distance.append(10.0)
                    SDObj.revolutions.append(0)
                    SDObj.animation_steps.append(20.0)

            except:
                pass

    EAFolder = FreeCAD.ActiveDocument.ExplodedAssembly
    # add initial placement if this is the first movement of the parts
    for name in SDObj.names:
        try:
            plm = EAFolder.InitialPlacements[name]

        except:
            an_object = FreeCAD.ActiveDocument.getObject(name)
            # prepare object placement for JSON serialization
            # store base placement as vector
            plm = an_object.Placement
            base = (plm.Base[0], plm.Base[1], plm.Base[2])
            # store rotation as a quaternion
            rot = plm.Rotation.Q
            placement = (base, rot)
            EAFolder.InitialPlacements[name] = placement

    # place inside ExplodedAssembly folder and update document
    EAFolder.addObject(SDObj)
    updateTrajectoryLines()
    FreeCAD.ActiveDocument.recompute()


# simple group trajectory ########################################################
class SimpleGroupObject:
    def __init__(self, obj):
        obj.addProperty('App::PropertyPythonObject', 'names').names = []
        obj.addProperty('App::PropertyPythonObject', 'dir_vectors').dir_vectors = []
        obj.addProperty('App::PropertyPythonObject', 'rot_vectors').rot_vectors = []
        obj.addProperty('App::PropertyPythonObject', 'rot_centers').rot_centers = []
        #
        obj.addProperty('App::PropertyFloat', 'Distance').Distance = 20.0
        obj.addProperty('App::PropertyFloat', 'Revolutions').Revolutions = 0.0
        obj.addProperty('App::PropertyFloat', 'AnimationStepTime').AnimationStepTime = 0.0
        obj.addProperty('App::PropertyInteger', 'AnimationSteps').AnimationSteps = 20
        obj.Proxy = self

    def onChanged(self, fp, prop):
        pass

    def execute(self, fp):
        resetPlacement()
        goToEnd()
        FreeCAD.ActiveDocument.ExplodedAssembly.CurrentTrajectory = -1


class SimpleGroupObjectViewProvider:
    def __init__(self, obj):
        obj.Proxy = self

    def getIcon(self):
        __dir__ = os.path.dirname(__file__)
        return __dir__ + '/icons/SimpleGroup.svg'


def createSimpleDisassemble():
    # add object to the Document and initialize it
    SDObj = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroupPython', 'SimpleGroup')
    SimpleGroupObject(SDObj)
    SimpleGroupObjectViewProvider(SDObj.ViewObject)
    # retrieve selection
    selection = FreeCAD.Gui.Selection.getSelectionEx()
    # the last face of the last object selected determines the disassemble dir vector
    dir_vector = selection[-1].SubObjects[-1].normalAt(0, 0)
    # the rotation center is the center of mass of the last face selected
    rot_center = selection[-1].SubObjects[-1].CenterOfMass
    # create trajectory data
    for sel_obj in selection:
        # append object name
        SDObj.names.append(sel_obj.Object.Name)
        # append unmount direction
        JSON_dir_vector = (dir_vector[0], dir_vector[1], dir_vector[2])
        SDObj.dir_vectors.append(JSON_dir_vector)
        # apend rotation axis
        JSON_rot_axis = (dir_vector[0], dir_vector[1], dir_vector[2])
        SDObj.rot_vectors.append(JSON_rot_axis)
        # append rotation center
        JSON_rot_center = (rot_center[0], rot_center[1], rot_center[2])
        SDObj.rot_centers.append(JSON_rot_center)

    EAFolder = FreeCAD.ActiveDocument.ExplodedAssembly
    # add initial placement if this is the first move of the parts
    for name in SDObj.names:
        try:
            plm = EAFolder.InitialPlacements[name]

        except:
            an_object = FreeCAD.ActiveDocument.getObject(name)
            # prepare object placement for JSON serialization
            # store base placement as vector
            plm = an_object.Placement
            base = (plm.Base[0], plm.Base[1], plm.Base[2])
            # store rotation as a quaternion
            rot = plm.Rotation.Q
            placement = (base, rot)
            EAFolder.InitialPlacements[name] = placement

    # place inside ExplodedAssembly folder and update document
    EAFolder.addObject(SDObj)
    updateTrajectoryLines()
    FreeCAD.ActiveDocument.recompute()


# wire group trajectory ########################################################
class WireGroupObject:
    def __init__(self, obj):
        obj.addProperty('App::PropertyPythonObject', 'names').names = []
        obj.addProperty('App::PropertyFloat', 'AnimationStepTime').AnimationStepTime = 0.0
        obj.addProperty('App::PropertyInteger', 'AnimationStepsEdge').AnimationStepsEdge = 10
        obj.Proxy = self

    def onChanged(self, fp, prop):
        pass

    def execute(self, fp):
        resetPlacement()
        goToEnd()
        FreeCAD.ActiveDocument.ExplodedAssembly.CurrentTrajectory = -1


class WireGroupObjectViewProvider:
    def __init__(self, obj):
        obj.Proxy = self

    def getIcon(self):
        __dir__ = os.path.dirname(__file__)
        return __dir__ + '/icons/WireTrajectory.svg'


def createWireDisassemble():
    # select the objects and finally the trajectory objects
    # Animation will run over the edges of the trajectory object
    sel_objects = FreeCAD.Gui.Selection.getSelection()[0:-2]
    sel_wire = FreeCAD.Gui.Selection.getSelection()[-1]
    # Initialize object
    WDObj = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroupPython', 'WireGroup')
    WireGroupObject(WDObj)
    #WireGroupObjectViewProvider(WDObj.ViewObject)
    # add object names
    for obj in sel_objects:
        obj_name = obj.Name
        WDObj.names.append(obj_name)

    # add trajectory objecto to this new wire group
    WDObj.addObject(sel_wire)
    # place inside ea folder
    EAFolder = FreeCAD.ActiveDocument.ExplodedAssembly
    EAFolder.addObject(WDObj)
    FreeCAD.ActiveDocument.recompute()





def resetPlacement():
    # restore the placement of all objects
    EAFolder = FreeCAD.ActiveDocument.ExplodedAssembly
    # set everything to its initial position
    for traj in EAFolder.Group:
        for name in traj.names:
            obj = FreeCAD.ActiveDocument.getObject(name)
            # create placement from initial placement list
            plm = EAFolder.InitialPlacements[name]
            base = FreeCAD.Vector(plm[0][0], plm[0][1], plm[0][2])
            rot = FreeCAD.Rotation(plm[1][0], plm[1][1], plm[1][2], plm[1][3])
            obj.Placement = FreeCAD.Placement(base, rot)



def runAnimation(start=0, end=0, mode='complete', direction='forward'):
    # runs the animation from a start step number to the end step number
    # mode: 'complete', 'toPoint'
    # 'complete' means forward from start to end and backwars if already in end
    # 'forward' means disassemble from start to end
    # 'backward' means assemble from start to end
    # 'toPoint' means go to an especific end point without animation
    # toggle 'InAnimation variable' to disable other icons temporally
    FreeCAD.ActiveDocument.ExplodedAssembly.InAnimation = True
    # # complete mode
    # start animation
    EAFolder = FreeCAD.ActiveDocument.ExplodedAssembly.Group
    if mode == 'complete':
        number_of_trajectories = len(EAFolder)

    elif mode == 'toPoint':
        number_of_trajectories = len(EAFolder) - start - end

    traj_iterator = range(number_of_trajectories)
    if direction == 'backward':
        traj_iterator = range(number_of_trajectories-1, -1, -1)

    animation_paused = False
    for r in traj_iterator:
        # break animation loop if not InAnimation (this is where pause animation takes place):
        EA = FreeCAD.ActiveDocument.ExplodedAssembly
        if not(EA.InAnimation):
            animation_paused = True
            break

        if direction == 'forward':
            traj = EAFolder[r+start]
            # set current stop point
            EA.CurrentTrajectory = r+start

        elif direction == 'backward':
            traj = EAFolder[r]
            # set current stop point
            EA.CurrentTrajectory = r-1

        # highligh current trajectory
        FreeCAD.Gui.Selection.addSelection(traj)
        # If trajectory is a bolt group or simple group:
        if traj.Name[0:11] == 'SimpleGroup' or traj.Name[0:9] == 'BoltGroup':
            # buffer objects
            objects = []
            for name in traj.names:
                objects.append(FreeCAD.ActiveDocument.getObject(name))

            inc_D = traj.Distance / float(traj.AnimationSteps)
            inc_R = traj.Revolutions / float(traj.AnimationSteps)
            if direction == 'backward':
                inc_D = inc_D*-1.0
                inc_R = inc_R*-1.0

            for i in range(traj.AnimationSteps):
                if i == 0:
                    dir_vectors = []
                    rot_vectors = []
                    rot_centers = []
                    for s in range(len(objects)):
                        dir_vectors.append(FreeCAD.Vector(tuple(traj.dir_vectors[s])))
                        rot_vectors.append(FreeCAD.Vector(tuple(traj.rot_vectors[s])))
                        rot_centers.append(FreeCAD.Vector(tuple(traj.rot_centers[s])))

                for n in range(len(objects)):
                    obj = objects[n]
                    obj_base = dir_vectors[n]*inc_D
                    obj_rot = FreeCAD.Rotation(rot_vectors[n], inc_R*360.0)
                    obj_rot_center = rot_centers[n]
                    incremental_placement = FreeCAD.Placement(obj_base, obj_rot, obj_rot_center)
                    obj.Placement = incremental_placement.multiply(obj.Placement)

                FreeCAD.Gui.updateGui()
                time.sleep(traj.AnimationStepTime)

        else:
            # if traj is a WireGroup object
            steps = traj.AnimationStepsEdge
            edges = traj.Shape.Edges
            for edge in edges:
                points = edge.discretize(steps)
                vectors = []
                for i in range(len(points)-1):
                    pa = points[i]
                    pb = points[i+1]
                    v = (pb[0]+pa[0], pb[1]+pa[1], pb[2]+pa[2])
                    vectors.append(v)

            if direction == 'backward':
                vectors.reverse()

            for v in vectors:
                for i in range((len(traj.names))):
                    obj = FreeCAD.ActiveDocument.getObject(traj.names[i])
                    # incremental placement
                    obj.Placement.x = obj.Placement.x + v[0]
                    obj.Placement.y = obj.Placement.y + v[1]
                    obj.Placement.z = obj.Placement.z + v[2]

                FreeCAD.Gui.udpateGui()
                time.sleep(traj.AnimationStepTime)


        # clear selection
        FreeCAD.Gui.Selection.clearSelection()

    # set pointer to current trajectory index
    EA = FreeCAD.ActiveDocument.ExplodedAssembly
    # toggle InAnimation to activate icons deactivated before
    EA.InAnimation = False
    # set CurrentTrajectory number
    if not(animation_paused):
        if direction == 'forward' and end == 0:
            EA.CurrentTrajectory = -1

        if direction == 'backward' and start == 0:
            EA.CurrentTrajectory = 0



def goToEnd():
    # start animation
    EAFolder = FreeCAD.ActiveDocument.ExplodedAssembly.Group
    for traj in EAFolder:
        objects = []
        for name in traj.names:
            objects.append(FreeCAD.ActiveDocument.getObject(name))

        inc_D = traj.Distance / float(1)
        inc_R = traj.Revolutions / float(1)
        for i in range(1):
            if i == 0:
                dir_vectors = []
                rot_vectors = []
                rot_centers = []
                for s in range(len(objects)):
                    dir_vectors.append(FreeCAD.Vector(tuple(traj.dir_vectors[s])))
                    rot_vectors.append(FreeCAD.Vector(tuple(traj.rot_vectors[s])))
                    rot_centers.append(FreeCAD.Vector(tuple(traj.rot_centers[s])))

            for n in range(len(objects)):
                obj = objects[n]
                obj_base = dir_vectors[n]*inc_D
                obj_rot = FreeCAD.Rotation(rot_vectors[n], inc_R*360)
                obj_rot_center = rot_centers[n]
                incremental_placement = FreeCAD.Placement(obj_base, obj_rot, obj_rot_center)
                obj.Placement = incremental_placement.multiply(obj.Placement)


    FreeCAD.Gui.updateGui()


def goToSelectedTrajectory():
    # retrieve selection
    traj_name = FreeCAD.Gui.Selection.getSelectionEx()[0].Object.Name
    # start animation
    EAFolder = FreeCAD.ActiveDocument.ExplodedAssembly.Group
    for r in range(len(EAFolder)):
        traj = EAFolder[r]
        objects = []
        update_gui = False
        for name in traj.names:
            objects.append(FreeCAD.ActiveDocument.getObject(name))

        if traj_name == traj.Name:
            inc_D = traj.Distance / float(traj.AnimationSteps)
            inc_R = traj.Revolutions / float(traj.AnimationSteps)
            animation_range = traj.AnimationSteps
            update_gui = True

        else:
            inc_D = traj.Distance / float(1)
            inc_R = traj.Revolutions / float(1)
            animation_range = 1

        for i in range(animation_range):
            if i == 0:
                dir_vectors = []
                rot_vectors = []
                rot_centers = []
                for s in range(len(objects)):
                    dir_vectors.append(FreeCAD.Vector(tuple(traj.dir_vectors[s])))
                    rot_vectors.append(FreeCAD.Vector(tuple(traj.rot_vectors[s])))
                    rot_centers.append(FreeCAD.Vector(tuple(traj.rot_centers[s])))

            for n in range(len(objects)):
                obj = objects[n]
                obj_base = dir_vectors[n]*inc_D
                obj_rot = FreeCAD.Rotation(rot_vectors[n], inc_R*360)
                obj_rot_center = rot_centers[n]
                incremental_placement = FreeCAD.Placement(obj_base, obj_rot, obj_rot_center)
                obj.Placement = incremental_placement.multiply(obj.Placement)

            if update_gui:
                FreeCAD.Gui.updateGui()

        if traj_name == traj.Name:
            # exit once selected trajectory has been reached
            break

    # set current trajectory cursor to current trajectory
    FreeCAD.ActiveDocument.ExplodedAssembly.CurrentTrajectory = r

def placeBeforeSelectedTrajectory():
    # select the trajectoreis you want to reallocate and finally,
    # the trajectory before wich you want to place them
    sel_traj = FreeCAD.Gui.Selection.getSelectionEx()
    # retrieve EA folder
    EAFolder = FreeCAD.ActiveDocument.ExplodedAssembly
    # create an auxiliary folder to re organizate exploded assembly
    aux_folder = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroup','ea_aux')
    before_traj = sel_traj[-1].Object
    for traj in EAFolder.Group:
        if traj.Name == before_traj.Name:
            for sel in sel_traj:
                aux_folder.addObject(sel.Object)

        elif traj.Name != before_traj.Name:
            add_traj = True
            for sel in sel_traj:
                if traj.Name == sel.Object.Name:
                    add_traj = False
                    break

            if add_traj:
                aux_folder.addObject(traj)

    # save the attributes of EAFolder'
    EAF_InitialPlacements = EAFolder.InitialPlacements
    EAF_CurrentTrajectory = EAFolder.CurrentTrajectory
    EAF_ResetAnimation = EAFolder.ResetAnimation
    EAF_InAnimation = EAFolder.InAnimation
    EAF_RemoveAllTrajectories = EAFolder.RemoveAllTrajectories
    FreeCAD.ActiveDocument.removeObject(EAFolder.Name)
    # create the EAFolder
    EAFolder = FreeCAD.ActiveDocument.addObject('App::DocumentObjectGroupPython', 'ExplodedAssembly')
    ExplodedAssemblyFolder(EAFolder)
    ExplodedAssemblyFolderViewProvider(EAFolder.ViewObject)
    # restore its content
    for traj in aux_folder.Group:
        EAFolder.addObject(traj)

    EAFolder.InitialPlacements = EAF_InitialPlacements
    EAFolder.CurrentTrajectory = EAF_CurrentTrajectory
    EAFolder.ResetAnimation = EAF_ResetAnimation
    EAFolder.InAnimation = EAF_InAnimation
    EAFolder.RemoveAllTrajectories = EAF_RemoveAllTrajectories
    # remove auxiliary folder
    FreeCAD.ActiveDocument.removeObject(aux_folder.Name)
    # recompute document
    FreeCAD.ActiveDocument.recompute()
    FreeCAD.Gui.updateGui()


def modifyIndividualObjectTrajectory():
    # multi direction for simple trajectory(at the moment)
    # selection:    trajectory_obj + shape + normal direction
    # selection:    trajectory_obj + normal_direction(selected over the shape)
    sel_traj = FreeCAD.Gui.Selection.getSelectionEx()[0].Object
    sel_objects = FreeCAD.Gui.Selection.getSelectionEx()[1:]
    if len(sel_objects) == 1:
        obj_name = sel_objects[0].Object.Name
        sel_face = sel_objects[0].SubObjects[0]

    else:
        obj_name = sel_objects[0].Object.Name
        sel_face = sel_objects[1].SubObjects[0]

    for i in range(len(sel_traj.names)):
        name = sel_traj.names[i]
        if obj_name == name:
            # if selected trajectory is a simple trajectory:
            if sel_traj.Name[0:11] == 'SimpleGroup':
                # asign new explosion direction to sel normal
                v = sel_face.normalAt(0,0)
                sel_traj.dir_vectors[i] = (v[0], v[1], v[2])
                break

            # if selected trajectory is a bolt group
        elif sel_traj.Name[0:9] == 'BoltGroup':
                # assign new exploded direction, rotation centre and vector
                v = sel_face.normalAt(0,0)
                c = sel_face.CenterOfMass
                sel_traj.dir_vectors[i] = (v[0], v[1], v[2])
                sel_traj.rot_vectors[i] = (v[0], v[1], v[2])
                sel_traj.rot_centers[i] = (c[0], c[1], c[2])
                break



def updateTrajectoryLines():
    EAFolder = FreeCAD.ActiveDocument.ExplodedAssembly.Group
    # remove all the previous trajectory lines
    for traj in EAFolder:
        for lines in traj.Group:
            FreeCAD.ActiveDocument.removeObject(lines.Name)

    # re-draw all trajectories
    for traj in EAFolder:
        lines_compound = []
        objects = []
        for name in traj.names:
            objects.append(FreeCAD.ActiveDocument.getObject(name))

        inc_D = traj.Distance
        dir_vectors = []
        rot_centers = []
        for s in range(len(objects)):
            dir_vectors.append(FreeCAD.Vector(tuple(traj.dir_vectors[s])))
            rot_centers.append(FreeCAD.Vector(tuple(traj.rot_centers[s])))

        for n in range(len(objects)):
            pa = rot_centers[n]# objects[n].Placement.Base
            pb = rot_centers[n] + dir_vectors[n]*inc_D
            lines_compound.append(Part.makeLine(pa, pb))

        l_obj = FreeCAD.ActiveDocument.addObject('Part::Feature','trajectory_line')
        l_obj.Shape = Part.makeCompound(lines_compound)
        l_obj.ViewObject.DrawStyle = "Dashed"
        l_obj.ViewObject.LineWidth = 1.0
        traj.addObject(l_obj)

    FreeCAD.Gui.updateGui()



def visibilityTrajectoryLines(show):
    # show or hide trajectory lines
    EAFolder = FreeCAD.ActiveDocument.ExplodedAssembly.Group
    for traj in EAFolder:
        for lines in traj.Group:
            lines.ViewObject.Visibility = show