jmplonka / InventorLoader

Workbench for FreeCAD to loads or import Autodesk (R) Inventor (R) files.
GNU General Public License v2.0
114 stars 17 forks source link

Demo-Status-0.6.ipt broken in STEP export by commit 13f51a0284452e8330fe46f2d3b7d7bcaec957de #69

Open marcocecchiscmgroup opened 1 year ago

marcocecchiscmgroup commented 1 year ago

The helicoidal spline curve/surfaces from Demo-Status-0.6.ipt raise exceptions. There are various issues into play:

Acis.py/class SurfaceSpline(Surface)/build(self, face = None): 'helix_spl_circ' is not contemplated in the if .. elif .. elif

Then, returning a Shape fails the if .. elif... in Acis2Step.py/createSurfaceSpline(acisFace)(acisFace), which in turn returns None instead of a pair None, None, that raises a 'cannot unpack non-iterable NoneType object' exception, because of this:

surface, sense = _createSurfaceFaceShape(acisFace)

This used to work before, because the Helix was at least built correctly and now it is not used anymore. Jens, these are your very examples, why don't you at least run them all before committing?

marcocecchiscmgroup commented 1 year ago

By fixing all the data format issues and continuing, another error shows up:

'SurfaceSpline' object has no attribute 'entity' in:

def getSurface(self): return None if (self._surface is None) else self._surface.entity

maybe more defensive checks are needed?

Thanks.

HolographicPrince commented 1 year ago

For the record: building nubs/nurbs is quite straightforward with NURBS-python or splipy (I'd discourage scipy/numpy as being too big only for that task). With those, you can as well interopolate b-splines from points, in the same way as you already do with Part (which we don't want to kick in in Acis2Step.py).

jmplonka commented 1 year ago

@HolographicPrince : this is the reason why numpy was banned a "long time" ago. @marcocecchiscmgroup : mea culpa - but is there a way to build a helical curve/surface in STEP without using Part.BSpline?

jmplonka commented 1 year ago

For the record: building nubs/nurbs is quite straightforward with NURBS-python or splipy (...). With those, you can as well interopolate b-splines from points, in the same way as you already do with Part (...). As long splines have a nubs/nurbs information no problem. It's all about parametric splines (like helical surfaces) for those I have to create a model based on Part.

marcocecchiscmgroup commented 1 year ago

As long splines have a nubs/nurbs information no problem. It's all about parametric splines (like helical surfaces) for those I have to create a model based on Part.

I see. Let's start with the helix. Here you have written a Helix class of your own, without using the Part.Helix (that would have been available, in case). So you're using Part only to do this:

    self.rotateShape(helix, DIR_X, VEC(self.dirMajor.x, self.dirMajor.y, 0), DIR_Z)
    self.rotateShape(helix, DIR_Z, self.vecAxis, DIR_X)
    helix.translate(self.posCenter)

So this can be dealt with quite easily: just transform each single point before creating the spline. Or else, you can transform the control points, as the knots are invariant for rigid transformations.

marcocecchiscmgroup commented 1 year ago

Please check out this version that, by merging new and old approach, provides what follows:

Acis2Step.zip

marcocecchiscmgroup commented 1 year ago

From the -dbg version, implement this snippet below in Part.py, class BSplineCurve(Curve). Then, in Acis.py you transform the single points before interpolating, instead of rotating the whole spline once built. Let me know how it works.

from geomdl import fitting, BSpline as NURBSBSpline
[...]
class BSplineCurve(Curve):
    def __init__(self, points = [], weights = None, knots = None, periodic = False, degree = 3, multiplicities = None, checkrational = False):
        super(BSplineCurve, self).__init__('BSplineCurve')
        self._poles = points
        self._weights = weights
        self.KnotSequence = knots
        self._closed = periodic
        self._mults = multiplicities
        self._knotsExploded = []
        if not knots is None:
            for i in range(0, len(knots)):
                for m in range(0, multiplicities[i]):
                    self._knotsExploded.append(knots[i])
        self.Degree = degree
        self.bSplineCurve = None
    def getPoles(self):
        return self._poles
    def getMultiplicities(self):
        nb_knots = len(self._knotsExploded)
        if (nb_knots == 0):
            return []
        elif (nb_knots == 1):
            return [1]
        else:
            ret = []
            mult = 1
            i = 1
            while i < nb_knots:
                while (i < nb_knots and isclose(self._knotsExploded[i - 1], self._knotsExploded[i])):
                    mult += 1
                    i += 1
                ret.append(mult)
                mult = 1
                i += 1
            return ret
    def getKnots(self):
        return self.KnotSequence
    def getWeights(self):
        return self._weights
    def isRational(self):
        if (not self.bSplineCurve is None):
            return self.bSplineCurve.rational
        else:
            return (self._weights is not None) and (len(self._weights) > 0)
    def isClosed(self):          return self._closed
    def value(self, u):
        #evaluate
        if (not self.bSplineCurve is None):
            value = self.bSplineCurve.evaluate_single(u)
            return Vector(value.x, value.y, value.z)
    def interpolate(self, points, periodicFlag=False, tolerance=1e-6, initialTangent=None, finalTangent=None, tangents=None, tangentFlags=False, parameters=None, scale=1.0):
        pointsV = []
        for p in points:
            pointsV.append((p.x, p.y, p.z))
        self.bSplineCurve = fitting.interpolate_curve(pointsV, self.Degree)
        self._poles = []
        for p in self.bSplineCurve.ctrlpts:
            self._poles.append(Vector(p[0], p[1], p[2])) # lo stesso formato atteso
        self._knotsExploded = self.bSplineCurve.knotvector
        self.KnotSequence = []
        nbKnots = len(self._knotsExploded)
        if nbKnots > 0:
            if nbKnots == 1:
                self.KnotSequence = self._knotsExploded
            else:
                self.KnotSequence.append(self._knotsExploded[0])
                i = 1
                while i < nbKnots:
                    while (i < nbKnots and isclose(self._knotsExploded[i - 1], self._knotsExploded[i])):
                        i += 1
                    if (i < nbKnots):
                        self.KnotSequence.append(self._knotsExploded[i])
                    i += 1
        startp = self.bSplineCurve.evaluate_single(self.KnotSequence[0])
        startpV = Vector(startp[0], startp[1], startp[2])
        endp = self.bSplineCurve.evaluate_single(self.KnotSequence[-1])
        endpV = Vector(endp[0], endp[1], endp[2])
        self._closed  = startpV.distanceToPoint(endpV) < EPS
        self._weights = parameters
    def buildFromPolesMultsKnots(self, poles, mults=(), knots=(), periodic=False, degree=1, weights=None, CheckRational = True):
        lu = len(poles)
        sum_of_mults = sum(mults)
        if (PyObject_IsTrue(periodic) and (sum_of_mults - degree - 1 != lu)): raise Exception("number of poles and sum of mults mismatch")
        if (PyObject_Not(periodic) and (sum_of_mults - degree - 1 != lu)): raise Exception("number of poles and sum of mults mismatch")
        if ((weights is not None) and (lu != len(weights))): raise Exception("number of poles and weights mismatch")
        self._poles   = poles
        self._mults   = mults
        self.KnotSequence   = knots
        self._knotsExploded = []
        for i in range(0, len(knots)):
            for _ in range(0, mults[i]):
                self._knotsExploded.append(knots[i])
        self._closed  = periodic
        self.Degree   = degree
        self._weights = weights
        self.bSplineCurve = NURBSBSpline.Curve()
        self.bSplineCurve.degree = degree
        self.bSplineCurve.weights = weights
        self.bSplineCurve.ctrlpoints = poles
        self.bSplineCurve.knotvector = self._knotsExploded
        return self
marcocecchiscmgroup commented 1 year ago
class BSplineSurface(GeometrySurface):
    def __init__(self):
        super(BSplineSurface, self).__init__('BSplineSurface')
        self._poles   = []
        self.UKnotSequence  = []
        self.VKnotSequence  = []
        self.UDegree  = 3
        self.VDegree  = 3
        self._weights = []
        #MC
        self._uknotsExploded = []
        self._vknotsExploded = []
        self.bSplineSurface = None
    def getPoles(self):           return self._poles
    def getUMultiplicities(self):
        nb_uknots = len(self._uknotsExploded)
        if (nb_uknots == 0):
            return []
        elif (nb_uknots == 1):
            return [1]
        else:
            ret = []
            mult = 1
            i = 1
            while i < nb_uknots:
                while (i < nb_uknots and isclose(self._uknotsExploded[i - 1], self._uknotsExploded[i])):
                    mult += 1
                    i += 1
                ret.append(mult)
                mult = 1
                i += 1
            return ret
    def getVMultiplicities(self):
        nb_vknots = len(self._vknotsExploded)
        if (nb_vknots == 0):
            return []
        elif (nb_vknots == 1):
            return [1]
        else:
            ret = []
            mult = 1
            i = 1
            while i < nb_vknots:
                while (i < nb_vknots and isclose(self._vknotsExploded[i - 1], self._vknotsExploded[i])):
                    mult += 1
                    i += 1
                ret.append(mult)
                mult = 1
                i += 1
            return ret
    def getUKnots(self):
        return self.UKnotSequence
    def getVKnots(self):
        return self.VKnotSequence
    def isURational(self):
        if (self.bSplineSurface is not None):
            return self.bSplineSurface.rational
        else:
            return self._weights is not None and len(self._weights) > 0
    def isVRational(self):
        if (self.bSplineSurface is not None):
            return self.bSplineSurface.rational
        else:
            return self._weights is not None and len(self._weights) > 0
    def NbUKnots(self):
        return len(self.UKnotSequence)
    def NbVKnots(self):
        return len(self.VKnotSequence)
    def NbUPoles(self):
        return len(self._poles)
    def NbVPoles(self):
        if (self.NbUPoles() > 0):
            return len(self._poles[0])
        else:
            return 0
    def NbPoles(self):
        return len(self._poles)
    def isUClosed(self):
        isClosed = True
        j = 0
        while (isClosed and j < self.NbVPoles()):
            isClosed = self._poles[0][j].distance(self._poles[-1][j]) <= EPS
            j += 1
        return isClosed
    def isVClosed(self):
        isClosed = True
        i = 0
        while (isClosed and i < self.NbUPoles()):
            isClosed = self._poles[i][0].distance(self._poles[i][-1]) <= EPS
            i += 1
        return isClosed
    def getWeights(self):
        return self._weights
    def buildFromPolesMultsKnots(self, poles, umults=(), vmults=(), uknots=(), vknots=(), uperiodic=False, vperiodic=False, udegree=1, vdegree=1, weights=None):
        lu            = len(poles)
        sum_of_umults = sum(umults)
        lv            = len(poles[0])
        sum_of_vmults = sum(vmults)
        for i in umults:
            for j in range(0, i):
                self._uknotsExploded.append(uknots[i])
        for i in vmults:
            for j in range(0, i):
                self._vknotsExploded.append(vknots[i])

        if ((weights is not None) and (lu != len(weights))): raise Exception("weights and poles rows-mismatch")
        if ((weights is not None) and (lv != len(weights[0]))): raise Exception("weights and poles cols-mismatch")

        if (len(uknots) != len(umults)): raise Exception("number of u-knots and u-mults mismatch")
        if (len(vknots) != len(vmults)): raise Exception("number of v-knots and v-mults mismatch")

        #if (PyObject_IsTrue(uperiodic) and (sum_of_umults - udegree - 1 != lu)): raise Exception("number of poles (%d) and sum of u-mults (%d) mismatch for uPeriodic = True" %(lu, sum_of_umults))
        #if (PyObject_IsTrue(vperiodic) and (sum_of_umults - udegree - 1 != lu)): raise Exception("number of poles (%d) and sum of v-mults (%d) mismatch for vPeriodic = True" %(lv, sum_of_vmults))
        if (PyObject_Not(uperiodic) and (sum_of_umults - udegree - 1 != lu)): raise Exception("number of poles (%d) and sum of u-mults (%d) mismatch for uPeriodic = False" %(lu, sum_of_umults))
        if (PyObject_Not(vperiodic) and (sum_of_vmults - vdegree - 1 != lv)): raise Exception("number of poles (%d) and sum of v-mults (%d) mismatch for vPeriodic = False" %(lv, sum_of_vmults))
        self._poles   = poles
        self.UKnotSequence  = uknots
        self.VKnotSequence  = vknots
        self.UDegree  = udegree
        self.VDegree  = vdegree
        self._weights = weights
        self.bSplineSurface = NURBSBSpline.Surface()
        self.bSplineCurve.weights = weights
        self.bSplineSurface.ctrlpoints = poles
        self.bSplineSurface.degree_u = udegree
        self.bSplineSurface.degree_v = vdegree
        self.bSplineSurface.knotvector_u = self._uknotsExploded
        self.bSplineSurface.knotvector_v = self._vknotsExploded
        return self
    def value(self, u, v):
        if (self.bSplineSurface):
            return Vector(self.bSplineSurface.evaluate(u, v))
        else:
            return None
    def interpolate(self, points, periodic=False):
        knotvector_u_len = len(points)
        knotvector_v_len =len(points[0])
        pointsConv = []
        for row in points:
            for col in row:
                pointsConv.append((col.x, col.y, col.z))        
        self.bSplineSurface = fitting.interpolate_surface(pointsConv, knotvector_u_len, knotvector_v_len, self.UDegree, self.VDegree)
        if (self.bSplineSurface.rational):
            self._weights = self.bSplineSurface.weights

        poles_tmp = reshape(self.bSplineSurface.ctrlpts, knotvector_v_len)
        self._poles = []
        for pole_row in poles_tmp:
            self._poles.append([])
            for pole_col in pole_row:
                self._poles[-1].append(Vector(pole_col[0], pole_col[1], pole_col[2]))

        self._uknotsExploded = self.bSplineSurface.knotvector_u
        self.UKnotSequence = []
        nbUKnots = len(self._uknotsExploded)
        if nbUKnots > 0:
            if nbUKnots == 1:
                self.UKnotSequence = self._uknotsExploded
            else:
                self.UKnotSequence.append(self._uknotsExploded[0])
                i = 1
                while i < nbUKnots:
                    while (i < nbUKnots and isclose(self._uknotsExploded[i - 1], self._uknotsExploded[i])):
                        i += 1
                    if (i < nbUKnots):
                        self.UKnotSequence.append(self._uknotsExploded[i])
                    i += 1
        self._vknotsExploded = self.bSplineSurface.knotvector_v
        self.VKnotSequence = []
        nbVKnots = len(self._vknotsExploded)
        if nbVKnots > 0:
            if nbVKnots == 1:
                self.VKnotSequence = self._vknotsExploded
            else:
                self.VKnotSequence.append(self._vknotsExploded[0])
                i = 1
                while i < nbVKnots:
                    while (i < nbVKnots and isclose(self._vknotsExploded[i - 1], self._vknotsExploded[i])):
                        i += 1
                    if (i < nbVKnots):
                        self.VKnotSequence.append(self._vknotsExploded[i])
                    i += 1
        #self._closed  = periodic
        # non si sa mai
        self.UDegree  = self.bSplineSurface.degree_u
        self.VDegree  = self.bSplineSurface.degree_v
jmplonka commented 1 year ago

@marcocecchiscmgroup : poles_tmp = reshape(self.bSplineSurface.ctrlpts, knotvector_v_len) <- Is reshape from numpy?

marcocecchiscmgroup commented 1 year ago

A couple of years ago I gave you this to remove the numpy dependency, maybe you wiped it out completely:

def reshape(v, size):
    if size == 1: return v
    return [[v[size * i + j] for j in range(size)] for i in range(len(v) // size)]
jmplonka commented 1 year ago

I was only too long away from this project.