from OCC.Core.AIS import *
from OCC.Core.Adaptor2d import *
from OCC.Core.Adaptor3d import *
from OCC.Core.Addons import *
from OCC.Core.AdvApp2Var import *
from OCC.Core.AdvApprox import *
from OCC.Core.AppBlend import *
from OCC.Core.AppCont import *
from OCC.Core.AppDef import *
from OCC.Core.AppParCurves import *
from OCC.Core.AppStd import *
from OCC.Core.AppStdL import *
from OCC.Core.Approx import *
from OCC.Core.ApproxInt import *
from OCC.Core.Aspect import *
from OCC.Core.BOPAlgo import *
from OCC.Core.BOPCol import *
from OCC.Core.BOPDS import *
from OCC.Core.BOPInt import *
from OCC.Core.BOPTools import *
from OCC.Core.BRep import *
from OCC.Core.BRepAdaptor import *
from OCC.Core.BRepAlgo import *
from OCC.Core.BRepAlgoAPI import *
from OCC.Core.BRepApprox import *
from OCC.Core.BRepBlend import *
from OCC.Core.BRepBndLib import *
from OCC.Core.BRepBuilderAPI import *
from OCC.Core.BRepCheck import *
from OCC.Core.BRepClass import *
from OCC.Core.BRepClass3d import *
from OCC.Core.BRepExtrema import *
from OCC.Core.BRepFeat import *
from OCC.Core.BRepFill import *
from OCC.Core.BRepFilletAPI import *
from OCC.Core.BRepGProp import *
from OCC.Core.BRepIntCurveSurface import *
from OCC.Core.BRepLProp import *
from OCC.Core.BRepLib import *
from OCC.Core.BRepMAT2d import *
from OCC.Core.BRepMesh import *
from OCC.Core.BRepOffset import *
from OCC.Core.BRepOffsetAPI import *
from OCC.Core.BRepPrim import *
from OCC.Core.BRepPrimAPI import *
from OCC.Core.BRepProj import *
from OCC.Core.BRepSweep import *
from OCC.Core.BRepTools import *
from OCC.Core.BRepTopAdaptor import *
from OCC.Core.BSplCLib import *
from OCC.Core.BSplSLib import *
from OCC.Core.BiTgte import *
from OCC.Core.Bisector import *
from OCC.Core.Blend import *
from OCC.Core.BlendFunc import *
from OCC.Core.Bnd import *
from OCC.Core.BndLib import *
from OCC.Core.CDF import *
from OCC.Core.CDM import *
from OCC.Core.CPnts import *
from OCC.Core.CSLib import *
from OCC.Core.ChFi2d import *
from OCC.Core.ChFi3d import *
from OCC.Core.ChFiDS import *
from OCC.Core.ChFiKPart import *
from OCC.Core.Contap import *
from OCC.Core.Convert import *
from OCC.Core.Dico import *
from OCC.Core.Draft import *
from OCC.Core.DsgPrs import *
from OCC.Core.Dynamic import *
from OCC.Core.ElCLib import *
from OCC.Core.ElSLib import *
from OCC.Core.Expr import *
from OCC.Core.ExprIntrp import *
from OCC.Core.Extrema import *
from OCC.Core.FEmTool import *
from OCC.Core.FSD import *
from OCC.Core.FairCurve import *
from OCC.Core.FilletSurf import *
from OCC.Core.GC import *
from OCC.Core.GCE2d import *
from OCC.Core.GCPnts import *
from OCC.Core.GEOMAlgo import *
from OCC.Core.GProp import *
from OCC.Core.GccAna import *
from OCC.Core.GccEnt import *
from OCC.Core.GccGeo import *
from OCC.Core.GccInt import *
from OCC.Core.GccIter import *
from OCC.Core.Geom import *
from OCC.Core.Geom2d import *
from OCC.Core.Geom2dAPI import *
from OCC.Core.Geom2dAdaptor import *
from OCC.Core.Geom2dConvert import *
from OCC.Core.Geom2dGcc import *
from OCC.Core.Geom2dHatch import *
from OCC.Core.Geom2dInt import *
from OCC.Core.Geom2dLProp import *
from OCC.Core.GeomAPI import *
from OCC.Core.GeomAbs import *
from OCC.Core.GeomAdaptor import *
from OCC.Core.GeomConvert import *
from OCC.Core.GeomFill import *
from OCC.Core.GeomInt import *
from OCC.Core.GeomLProp import *
from OCC.Core.GeomLib import *
from OCC.Core.GeomPlate import *
from OCC.Core.GeomProjLib import *
from OCC.Core.GeomToStep import *
from OCC.Core.GeomTools import *
from OCC.Core.GraphDS import *
from OCC.Core.GraphTools import *
from OCC.Core.Graphic3d import *
from OCC.Core.HLRAlgo import *
from OCC.Core.HLRAppli import *
from OCC.Core.HLRBRep import *
from OCC.Core.HLRTopoBRep import *
from OCC.Core.Hatch import *
from OCC.Core.HatchGen import *
from OCC.Core.Hermit import *
from OCC.Core.IFSelect import *
from OCC.Core.IGESCAFControl import *
from OCC.Core.IGESControl import *
from OCC.Core.Image import *
from OCC.Core.IncludeLibrary import *
from OCC.Core.IntAna import *
from OCC.Core.IntAna2d import *
from OCC.Core.IntCurve import *
from OCC.Core.IntCurveSurface import *
from OCC.Core.IntCurvesFace import *
from OCC.Core.IntImp import *
from OCC.Core.IntImpParGen import *
from OCC.Core.IntPatch import *
from OCC.Core.IntPoly import *
from OCC.Core.IntPolyh import *
from OCC.Core.IntRes2d import *
from OCC.Core.IntStart import *
from OCC.Core.IntSurf import *
from OCC.Core.IntTools import *
from OCC.Core.IntWalk import *
from OCC.Core.Interface import *
from OCC.Core.InterfaceGraphic import *
from OCC.Core.Intf import *
from OCC.Core.Intrv import *
from OCC.Core.LProp import *
from OCC.Core.LProp3d import *
from OCC.Core.Law import *
from OCC.Core.LocOpe import *
from OCC.Core.LocalAnalysis import *
from OCC.Core.MAT import *
from OCC.Core.MAT2d import *
from OCC.Core.MMgt import *
from OCC.Core.Materials import *
from OCC.Core.MeshVS import *
from OCC.Core.Message import *
from OCC.Core.NCollection import *
from OCC.Core.NIS import *
from OCC.Core.NLPlate import *
from OCC.Core.OSD import *
from OCC.Core.PCDM import *
from OCC.Core.PLib import *
from OCC.Core.Plate import *
from OCC.Core.Plugin import *
from OCC.Core.Poly import *
from OCC.Core.Precision import *
from OCC.Core.Primitives import *
from OCC.Core.ProjLib import *
from OCC.Core.Prs3d import *
from OCC.Core.PrsMgr import *
from OCC.Core.Quantity import *
from OCC.Core.RWStepAP203 import *
from OCC.Core.RWStepAP214 import *
from OCC.Core.RWStepBasic import *
from OCC.Core.RWStepDimTol import *
from OCC.Core.RWStepElement import *
from OCC.Core.RWStepFEA import *
from OCC.Core.RWStepGeom import *
from OCC.Core.RWStepRepr import *
from OCC.Core.RWStepShape import *
from OCC.Core.RWStepVisual import *
from OCC.Core.RWStl import *
from OCC.Core.Resource import *
from OCC.Core.STEPCAFControl import *
from OCC.Core.STEPConstruct import *
from OCC.Core.STEPControl import *
from OCC.Core.STEPEdit import *
from OCC.Core.STEPSelections import *
from OCC.Core.Select3D import *
from OCC.Core.SelectBasics import *
from OCC.Core.SelectMgr import *
from OCC.Core.ShapeAlgo import *
from OCC.Core.ShapeAnalysis import *
from OCC.Core.ShapeBuild import *
from OCC.Core.ShapeConstruct import *
from OCC.Core.ShapeCustom import *
from OCC.Core.ShapeExtend import *
from OCC.Core.ShapeFix import *
from OCC.Core.ShapeProcess import *
from OCC.Core.ShapeProcessAPI import *
from OCC.Core.ShapeUpgrade import *
from OCC.Core.SortTools import *
from OCC.Core.Standard import *
from OCC.Core.StdFail import *
from OCC.Core.StdPrs import *
from OCC.Core.StdSelect import *
from OCC.Core.StepAP203 import *
from OCC.Core.StepAP209 import *
from OCC.Core.StepAP214 import *
from OCC.Core.StepBasic import *
from OCC.Core.StepDimTol import *
from OCC.Core.StepElement import *
from OCC.Core.StepFEA import *
from OCC.Core.StepGeom import *
from OCC.Core.StepRepr import *
from OCC.Core.StepShape import *
from OCC.Core.StepToGeom import *
from OCC.Core.StepToTopoDS import *
from OCC.Core.StepVisual import *
from OCC.Core.StlAPI import *
from OCC.Core.StlMesh import *
from OCC.Core.StlTransfer import *
from OCC.Core.Storage import *
from OCC.Core.Sweep import *
from OCC.Core.TColGeom import *
from OCC.Core.TColGeom2d import *
from OCC.Core.TColQuantity import *
from OCC.Core.TColStd import *
from OCC.Core.TColgp import *
from OCC.Core.TCollection import *
from OCC.Core.TDF import *
from OCC.Core.TDataStd import *
from OCC.Core.TDataXtd import *
from OCC.Core.TDocStd import *
from OCC.Core.TFunction import *
from OCC.Core.TNaming import *
from OCC.Core.TPrsStd import *
from OCC.Core.TShort import *
from OCC.Core.TopAbs import *
from OCC.Core.TopBas import *
from OCC.Core.TopClass import *
from OCC.Core.TopCnx import *
from OCC.Core.TopExp import *
from OCC.Core.TopLoc import *
from OCC.Core.TopOpeBRep import *
from OCC.Core.TopOpeBRepBuild import *
from OCC.Core.TopOpeBRepDS import *
from OCC.Core.TopOpeBRepTool import *
from OCC.Core.TopTools import *
from OCC.Core.TopTrans import *
from OCC.Core.TopoDS import *
from OCC.Core.TopoDSToStep import *
from OCC.Core.Transfer import *
from OCC.Core.TransferBRep import *
from OCC.Core.UTL import *
from OCC.Core.Units import *
from OCC.Core.UnitsAPI import *
from OCC.Core.V3d import *
from OCC.Core.Visual3d import *
from OCC.Core.Visualization import *
from OCC.Core.Voxel import *
from OCC.Core.XBRepMesh import *
from OCC.Core.XCAFApp import *
from OCC.Core.XCAFDoc import *
from OCC.Core.XCAFPrs import *
from OCC.Core.XSControl import *
from OCC.Core.gce import *
from OCC.Core.gp import *
from OCC.Core.math import *

import tempfile
import pathlib
from PySide2.QtCore import *
from PySide2.QtWidgets import *
import vtk
import vmi

import warnings


def ignore_warnings(*args):
    pass


warnings.simplefilter("ignore")
warnings.simplefilter = ignore_warnings

tr = QObject()
tr = tr.tr

ShapeType = {'compound': TopAbs_COMPOUND,
             'compsolid': TopAbs_COMPSOLID,
             'solid': TopAbs_SOLID,
             'shell': TopAbs_SHELL,
             'face': TopAbs_FACE,
             'wire': TopAbs_WIRE,
             'edge': TopAbs_EDGE,
             'vertex': TopAbs_VERTEX}

toShapeType = {'compound': topods.Compound,
               'compsolid': topods.CompSolid,
               'solid': topods.Solid,
               'shell': topods.Shell,
               'face': topods.Face,
               'wire': topods.Wire,
               'edge': topods.Edge,
               'vertex': topods.Vertex}


class Shape(QObject, vmi.Menu):
    def __init__(self, name=None):
        QObject.__init__(self)

        self.name = name if name else tr('形状 （Shape)')
        vmi.Menu.__init__(self, name=self.name)

        self.actions = {'ExportSTEP': QAction(tr('导出 (Export) STEP')),
                        'ExportSTL': QAction(tr('导出 (Export) STL')), }
        self.actions['ExportSTEP'].triggered.connect(self.exportSTEP)
        self.actions['ExportSTL'].triggered.connect(self.exportSTL)

        def aboutToShow():
            self.menu.clear()
            self.menu.addAction(self.actions['ExportSTEP'])
            self.menu.addAction(self.actions['ExportSTL'])

        self.menu.aboutToShow.connect(aboutToShow)

        self._Shape = TopoDS_Shape()
        self._Dataset = vtk.vtkPolyData()

    def __setstate__(self, s):
        self.__init__(s['name'])
        self.__dict__.update(s)
        s = self.__dict__

        self._Shape = vmi.loads(s['_Dumps']['_Shape'])
        self._Dataset.DeepCopy(mkPoly(self._Shape))

    def __getstate__(self):
        s = self.__dict__.copy()
        for kw in ['menu', 'actions', '_Dataset', '__METAOBJECT__']:
            if kw in s:
                del s[kw]

        s['_Dumps'] = {'_Shape': vmi.dumps(self._Shape)}
        for kw in s['_Dumps']:
            del s[kw]
        return s

    def clone(self, shape):
        if self._Shape != shape:
            if shape is None:
                self._Shape = TopoDS_Shape()
            else:
                self._Shape = shape
            self._Dataset.DeepCopy(mkPoly(self._Shape))

    def shape(self):
        return self._Shape

    def importSTEP(self):
        importSTEP(self)

    def exportSTEP(self):
        exportSTEP(self)

    def exportSTL(self):
        exportSTL(self)


def typeShape(shape):
    if isinstance(shape, Shape):
        shape = shape.shape()
    for kw in ShapeType:
        if ShapeType[kw] == shape.ShapeType():
            return kw


def expShape(shape, t=None):
    if isinstance(shape, Shape):
        shape = shape.shape()
    if t is None:
        shapes = []
        for kw in ShapeType:
            shapes += expShape(shape, kw)
        return shapes
    if t in ShapeType:
        shapes, exp = [], TopExp_Explorer(shape, ShapeType[t])
        while exp.More():
            shapes.append(toShapeType[t](exp.Current()))
            exp.Next()
        return shapes


def subShape(shape):
    if isinstance(shape, Shape):
        shape = shape.shape()
    shapes, it = [], TopoDS_Iterator(shape)
    while it.More():
        shapes.append(it.Value())
        it.Next()
    return shapes


def importSTEP(file=None):
    if file is None:
        file = vmi.askOpenFile(tr('导入 (Import) STEP'), '*.stp')
        if file is None:
            return
    r = STEPControl_Reader()
    if IFSelect_RetDone == r.ReadFile(file):
        r.TransferRoots()
        s = r.OneShape()
        if typeShape(s) == 'compound' and len(subShape(s)) == 1:
            s = subShape(s)[0]
        return s


def exportSTEP(shape, file=None):
    if isinstance(shape, Shape):
        shape = shape.shape()
    if file is None:
        file = vmi.askSaveFile(tr('导出 (Export) STEP'), '*.stp')
        if file is None:
            return
    w = STEPControl_Writer()
    w.Transfer(shape, STEPControl_AsIs)
    status = w.Write(file)
    if status == IFSelect_RetDone:
        return True


def exportSTL(shape, file=None):
    if isinstance(shape, Shape):
        shape = shape.shape()
    if file is None:
        file = vmi.askSaveFile(tr('导出 (Export) STL'), '*.stl')
        if file is None:
            return
    return stlapi.Write(shape, file, False)


def mkPoly(shape):
    if isinstance(shape, Shape):
        shape = shape.shape()
    if shape.IsNull():
        return vtk.vtkPolyData()

    pts = vtk.vtkPoints()
    polydata = vtk.vtkPolyData()
    polydata.SetPoints(pts)
    polydata.Allocate()

    # 1. 离散化
    breptools.Clean(shape)

    discrete = BRepMesh_IncrementalMesh(shape, 1e-3, True, 0.5, True)
    discrete.Perform()

    # 2. 转换Vertex
    exp = TopExp_Explorer(shape, TopAbs_VERTEX)
    while exp.More():
        vertex = topods.Vertex(exp.Current())
        exp.Next()

        pnt = BRep_Tool.Pnt(vertex)
        id = polydata.GetPoints().InsertNextPoint(pnt.X(), pnt.Y(), pnt.Z())
        polydata.InsertNextCell(vtk.VTK_VERTEX, 1, [id])

    # 3. 转换Edge
    exp = TopExp_Explorer(shape, TopAbs_EDGE)
    while exp.More():
        edge = topods.Edge(exp.Current())
        exp.Next()

        if edge.IsNull() or BRep_Tool.Degenerated(edge):
            continue

        p_on_tri = Handle_Poly_PolygonOnTriangulation()
        triangulation = Handle_Poly_Triangulation()
        loc = TopLoc_Location()
        BRep_Tool.PolygonOnTriangulation(edge, p_on_tri, triangulation, loc, 1)
        p_3d = Handle_Poly_Polygon3D()

        if p_on_tri.IsNull():
            p_3d = BRep_Tool.Polygon3D(None, edge, loc)

        if p_3d.IsNull() and p_on_tri.IsNull():
            continue

        trsf = loc.Transformation()

        if not p_3d.IsNull():
            n = p_3d.NbNodes()
            pnts = p_3d.Nodes()
            ids = list(range(1, n + 1))
        else:
            n = p_on_tri.NbNodes()
            pnts = triangulation.Nodes()
            ids = p_on_tri.Nodes()
            ids = [ids.Value(i) for i in range(ids.Lower(), ids.Upper() + 1)]

        if n > 1:
            idList = vtk.vtkIdList()
            for i in ids:
                pnt = pnts.Value(i)
                pnt.Transform(trsf)
                id = polydata.GetPoints().InsertNextPoint(pnt.X(), pnt.Y(), pnt.Z())
                idList.InsertNextId(id)
            polydata.InsertNextCell(vtk.VTK_POLY_LINE, idList)

    # 4. 转换Face并合并
    exp = TopExp_Explorer(shape, TopAbs_FACE)
    if exp.More():
        with tempfile.TemporaryDirectory() as p:
            p = pathlib.Path(p) / '.stl'
            stlapi.Write(shape, str(p), False)

            r = vtk.vtkSTLReader()
            r.SetFileName(str(p))
            r.Update()

            a = vtk.vtkAppendPolyData()
            a.AddInputData(r.GetOutput())
            a.AddInputData(polydata)
            a.Update()

            c = vtk.vtkCleanPolyData()
            c.SetInputData(a.GetOutput())
            c.Update()

            polydata.DeepCopy(a.GetOutput())

    return polydata


def add3(a, b):
    return [a[i] + b[i] for i in range(3)]


def subtract3(a, b):
    return [a[i] - b[i] for i in range(3)]


def distance3(a, b):
    return norm3(subtract3(a, b))


def dot3(a, b):
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]


def cross3(a, b):
    return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]


def norm3(a):
    return (a[0] ** 2 + a[1] ** 2 + a[2] ** 2) ** 0.5


def normalized3(a):
    norm = norm3(a)
    if norm > 0:
        return [_ / norm for _ in a]
    return [0, 0, 0]


def sign3(a, b):
    return 1 if dot3(a, b) > 0 else (-1 if dot3(a, b) < 0 else 0)


def trsfLandmark(pts0, pts1, mode='rigidbody'):
    if len(pts0) > 2 and len(pts1) > 2:
        pts = [vtk.vtkPoints(), vtk.vtkPoints()]
        for pt in pts0:
            pts[0].InsertNextPoint(pt)
        for pt in pts1:
            pts[1].InsertNextPoint(pt)

        modes = {'rigidbody': vtk.VTK_LANDMARK_RIGIDBODY,
                 'similarity': vtk.VTK_LANDMARK_SIMILARITY,
                 'affine': vtk.VTK_LANDMARK_AFFINE}
        ts = vtk.vtkLandmarkTransform()
        ts.SetSourceLandmarks(pts[0])
        ts.SetTargetLandmarks(pts[1])
        if mode in modes:
            ts.SetMode(modes[mode])
        else:
            ts.SetModeToRigidBody()
        ts.Update()
        return ts


def ptOnPlane(pt, origin, normal):
    t = dot3([pt[i] - origin[i] for i in range(3)], normal)
    return [pt[i] - t * normal[i] for i in range(3)]


def ptsOBB(pts):
    if pts.GetNumberOfPoints() > 0:
        args = {'origin': [0, 0, 0], 'axis': [[0, 0, 0], [0, 0, 0], [0, 0, 0]], 'size': [0, 0, 0]}
        vtk.vtkOBBTree().ComputeOBB(pts, args['origin'], args['axis'][0], args['axis'][1], args['axis'][2],
                                    args['size'])

        args['center'] = args['origin']
        for j in range(3):
            args['axis'][j] = normalized3(args['axis'][j])
            args['center'] = [args['center'][i] + 0.5 * args['size'][j] * args['axis'][j][i] for i in range(3)]
        return args


def rayCastShape(pt, vt, shape):
    if isinstance(shape, Shape):
        shape = shape.shape()

    def dmax(s0, s1):
        b = [Bnd_Box(), Bnd_Box()]
        brepbndlib.Add(s0, b[0])
        brepbndlib.Add(s1, b[1])

        min = [[b[0].Get()[i] for i in range(3)],
               [b[1].Get()[i] for i in range(3)]]
        max = [[b[0].Get()[i] for i in range(3, 6)],
               [b[1].Get()[i] for i in range(3, 6)]]
        xyz = [[gp_XYZ(min[0][0], min[0][1], min[0][2]),
                gp_XYZ(max[0][0], max[0][1], max[0][2])],
               [gp_XYZ(min[1][0], min[1][1], min[1][2]),
                gp_XYZ(max[1][0], max[1][1], max[1][2])]]
        delta = [xyz[0][1].Subtracted(xyz[0][0]),
                 xyz[1][1].Subtracted(xyz[1][0])]

        def dmin(s0, s1):
            b = [Bnd_Box(), Bnd_Box()]
            brepbndlib.Add(s0, b[0])
            brepbndlib.Add(s1, b[1])
            return b[0].Distance(b[1])

        return delta[0].Modulus() + delta[1].Modulus() + dmin(s0, s1)

    d = dmax(mkVertex(pt), shape)
    vtmax = [d * _ for _ in normalized3(vt)]

    sweep = BRepSweep_Prism(mkVertex(pt), vtmax)
    section = mkSection(shape, sweep.Shape())

    origin = mkPnt(pt)

    pts = {}
    for s in expShape(section, 'vertex'):
        pnt = BRep_Tool.Pnt(s)
        pts[pnt.Distance(origin)] = [pnt.X(), pnt.Y(), pnt.Z()]
    return [pts[i] for i in sorted(pts)]


def vtOnPlane(vt, normal):
    n2 = dot3(normal, normal)
    n2 = n2 if n2 != 0 else 1
    t = dot3(vt, normal)
    return [vt[i] - t * normal[i] / n2 for i in range(3)]


def vtOnVector(vt, vector):
    dot = dot3(vector, vector)
    if dot == 0:
        return [0, 0, 0]

    dot = dot3(vt, vector) / dot
    return [dot * vector[i] for i in range(3)]


def mkPnt(arg):
    return gp_Pnt(arg[0], arg[1], arg[2])


def mkVec(arg):
    return gp_Vec(arg[0], arg[1], arg[2])


def mkDir(arg):
    return gp_Dir(arg[0], arg[1], arg[2])


def mkSegment(pt0, pt1):
    pt0 = pt0 if isinstance(pt0, gp_Pnt) else mkPnt(pt0)
    pt1 = pt1 if isinstance(pt1, gp_Pnt) else mkPnt(pt1)
    return GC_MakeSegment(pt0, pt1).Value()


def mkSegments(pts, closed=False):
    pnts_ = [pnt for pnt in pts]
    if closed:
        pnts_.append(pts[0])
    return [mkSegment(pnts_[i], pnts_[i + 1]) for i in range(len(pnts_) - 1)]


def mkBSpline(pts, tgs=None, tgflags=None, closed=False, scale=True, tol=precision.Confusion()):
    if len(pts) < 2:
        return None

    if pts and not isinstance(pts, TColgp_HArray1OfPnt):
        _ = TColgp_HArray1OfPnt(1, len(pts))
        for i in range(len(pts)):
            if isinstance(pts[i], gp_Pnt):
                _.SetValue(i + 1, pts[i])
            else:
                _.SetValue(i + 1, mkPnt(pts[i]))
        pts = _

    if tgs and not isinstance(tgs, TColgp_Array1OfVec):
        _ = TColgp_Array1OfVec(1, len(tgs))
        for i in range(len(tgs)):
            if isinstance(pts[i], gp_Vec):
                _.SetValue(i + 1, tgs[i])
            else:
                _.SetValue(i + 1, mkVec(tgs[i]))
        tgs = _

    if tgs and tgflags is None:
        tgflags = [True for _ in tgs]

    if tgflags and not isinstance(tgflags, TColStd_HArray1OfBoolean):
        _ = TColStd_HArray1OfBoolean(1, len(tgflags))
        for i in range(len(tgflags)):
            _.SetValue(i + 1, tgflags[i])
        tgflags = _

    lower, upper = pts.Lower(), pts.Upper() + (1 if closed else 0)
    knots = TColStd_HArray1OfReal(lower, upper)
    knots.SetValue(lower, 0)

    for i in range(lower, upper):
        if closed and i == upper - 1:
            var = pts.Value(i).Distance(pts.Value(lower)) ** 0.5
        else:
            var = pts.Value(i).Distance(pts.Value(i + 1)) ** 0.5
        knots.SetValue(i + 1, knots.Value(i) + var)

    curve = GeomAPI_Interpolate(pts, closed, tol)
    if tgs is not None:
        curve.Load(tgs, tgflags, scale)
    curve.Perform()
    return curve.Curve() if curve.IsDone() else None


def mkBSplineKochanek(pts, closed=False, tol=precision.Confusion()):
    if len(pts) < 2:
        return None

    points = vtk.vtkPoints()
    for pt in pts:
        points.InsertNextPoint(pt)

    ks = [vtk.vtkKochanekSpline(), vtk.vtkKochanekSpline(), vtk.vtkKochanekSpline()]

    ps = vtk.vtkParametricSpline()
    ps.SetXSpline(ks[0])
    ps.SetYSpline(ks[1])
    ps.SetZSpline(ks[2])
    ps.SetPoints(points)
    ps.SetClosed(1 if closed else 0)
    ps.SetParameterizeByLength(1)
    ps.SetLeftConstraint(1)
    ps.SetRightConstraint(1)

    n = 100 * len(pts)

    pf = vtk.vtkParametricFunctionSource()
    pf.SetParametricFunction(ps)
    pf.SetUResolution(n)
    pf.SetVResolution(n)
    pf.SetWResolution(n)
    pf.Update()

    pts, points = [], pf.GetOutput().GetPoints()

    for i in range(points.GetNumberOfPoints() - (1 if closed else 0)):
        pts.append(points.GetPoint(i))
    curve = mkBSpline(pts, closed=closed, tol=tol)

    obb = vmi.ptsOBB(points)
    if 0 < obb['size'][2] < precision.Confusion():
        curve = mkCurveOnPlane(curve, obb['center'], obb['axis'][2])
    return curve


def mkCurveOnPlane(curve, origin, normal, vt=None):
    if vt is None:
        vt = normal
    return geomprojlib.ProjectOnPlane(curve, mkPlane(origin, normal), mkDir(vt), True)


def mkPlane(origin, normal):
    mk = GC_MakePlane(mkPnt(origin), mkDir(normal))
    return mk.Value() if mk.IsDone() else None


def mkVertex(pt):
    pt = pt if isinstance(pt, gp_Pnt) else mkPnt(pt)
    mk = BRepBuilderAPI_MakeVertex(pt)
    return mk.Vertex() if mk.IsDone() else None


def mkEdge(curve):
    mk = BRepBuilderAPI_MakeEdge(curve)
    return mk.Edge() if mk.IsDone() else None


def mkEdges(curves):
    return [mkEdge(curve) for curve in curves]


def mkWire(edges):
    edges_ = edges
    if not hasattr(edges_, '__iter__'):
        edges_ = [edges_]
    edges_ = [edge if isinstance(edge, TopoDS_Edge) else mkEdge(edge) for edge in edges_]
    mk = BRepBuilderAPI_MakeWire()
    for edges in edges_:
        mk.Add(edges)
    return mk.Wire() if mk.IsDone() else None


def mkFace(wire, pln=None):
    if pln is None:
        mk = BRepBuilderAPI_MakeFace(wire)
    else:
        mk = BRepBuilderAPI_MakeFace(pln, wire)
    return mk.Face() if mk.IsDone() else None


def mkLoft(wires):
    mk = BRepOffsetAPI_ThruSections()
    for wire in wires:
        mk.AddWire(wire)
    mk.Build()
    return mk.Shape() if mk.IsDone() else None


def mkSew(shapes):
    mk = BRepBuilderAPI_Sewing()
    for s in shapes:
        if isinstance(s, Shape):
            s = s.shape()
        mk.Add(s)
    mk.Perform()
    if len(expShape(mk.SewedShape())) > 0:
        return mk.SewedShape()


def mkSolid(shells):
    mk = BRepBuilderAPI_MakeSolid()
    for shell in shells:
        if typeShape(shell) == 'shell':
            mk.Add(toShapeType['shell'](shell))
    if mk.IsDone() and mk.Solid().Closed():
        return mk.Solid()


def mkFuse(s0, s1):
    if isinstance(s0, Shape):
        s0 = s0.shape()
    if isinstance(s1, Shape):
        s1 = s1.shape()
    if not isinstance(s0, list) or not isinstance(s0, tuple):
        s0 = [s0]
    if not isinstance(s1, list) or not isinstance(s1, tuple):
        s1 = [s1]

    s, args = [s0, s1], [TopTools_ListOfShape(), TopTools_ListOfShape()]

    for j in range(2):
        for arg in s[j]:
            args[j].Append(arg)

    mk = BRepAlgoAPI_Fuse()
    mk.SetArguments(args[0])
    mk.SetTools(args[1])
    mk.SetRunParallel(True)
    mk.Build()

    return mk.Shape() if mk.ErrorStatus() == 0 and mk.WarningStatus() == 0 else None


def mkCut(s0, s1):
    if isinstance(s0, Shape):
        s0 = s0.shape()
    if isinstance(s1, Shape):
        s1 = s1.shape()
    if not isinstance(s0, list) or not isinstance(s0, tuple):
        s0 = [s0]
    if not isinstance(s1, list) or not isinstance(s1, tuple):
        s1 = [s1]

    s, args = [s0, s1], [TopTools_ListOfShape(), TopTools_ListOfShape()]

    for j in range(2):
        for arg in s[j]:
            args[j].Append(arg)

    mk = BRepAlgoAPI_Cut()
    mk.SetArguments(args[0])
    mk.SetTools(args[1])
    mk.SetRunParallel(True)
    mk.Build()

    return mk.Shape() if mk.ErrorStatus() == 0 and mk.WarningStatus() == 0 else None


def mkCommon(s0, s1):
    if isinstance(s0, Shape):
        s0 = s0.shape()
    if isinstance(s1, Shape):
        s1 = s1.shape()
    if not isinstance(s0, list) or not isinstance(s0, tuple):
        s0 = [s0]
    if not isinstance(s1, list) or not isinstance(s1, tuple):
        s1 = [s1]

    s, args = [s0, s1], [TopTools_ListOfShape(), TopTools_ListOfShape()]

    for j in range(2):
        for arg in s[j]:
            args[j].Append(arg)

    mk = BRepAlgoAPI_Common()
    mk.SetArguments(args[0])
    mk.SetTools(args[1])
    mk.SetRunParallel(True)
    mk.Build()

    return mk.Shape() if mk.ErrorStatus() == 0 and mk.WarningStatus() == 0 else None


def mkSection(s0, s1):
    if isinstance(s0, Shape):
        s0 = s0.shape()
    if isinstance(s1, Shape):
        s1 = s1.shape()
    if not isinstance(s0, list) or not isinstance(s0, tuple):
        s0 = [s0]
    if not isinstance(s1, list) or not isinstance(s1, tuple):
        s1 = [s1]

    s, args = [s0, s1], [TopTools_ListOfShape(), TopTools_ListOfShape()]

    for j in range(2):
        for arg in s[j]:
            args[j].Append(arg)

    mk = BRepAlgoAPI_Section()
    mk.SetArguments(args[0])
    mk.SetTools(args[1])
    mk.SetRunParallel(True)
    mk.Build()

    return mk.Shape() if mk.ErrorStatus() == 0 and mk.WarningStatus() == 0 else None


def isInside(shape, pt):
    if isinstance(shape, Shape):
        shape = shape.shape()
    c = BRepClass3d_SolidClassifier(shape)
    c.Perform(mkPnt(pt), precision.Confusion())

    if c.State() == TopAbs_IN or c.State() == TopAbs_ON:
        return True
    return False


def mkPrism(shape, vt, l):
    if isinstance(shape, Shape):
        shape = shape.shape()
    vec = [l * i for i in vmi.normalized3(vt)]
    vec = gp_Vec(vec[0], vec[1], vec[2])
    mk = BRepPrimAPI_MakePrism(shape, vec)
    return mk.Shape() if mk.IsDone() else None
