Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
marcocecchiscmgroup opened this issue Apr 28, 2023 · 11 comments
Assignees
Labels

Comments

@marcocecchiscmgroup
Copy link

marcocecchiscmgroup commented Apr 28, 2023

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
Copy link
Author

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
Copy link

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
Copy link
Owner

@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
Copy link
Owner

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
Copy link
Author

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
Copy link
Author

marcocecchiscmgroup commented May 2, 2023

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

  • bug fixes in STEP transformation

  • ** Online Autodesk dxf viewer didn't show the transformed elements at all (while FreeCAD, Rhino and ShareCAD did)

  • ** Translations only sometimes result in a (0, 0, 0) rotation axis, which cannot be normalized

  • Demo-Status-0.4.2.ipt works with native Part (no stubs)

  • Demo-Status-0.6.ipt works with native Part (no stubs)

  • Fixed syntax errors found compiling with Cython

Acis2Step.zip

@marcocecchiscmgroup
Copy link
Author

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
Copy link
Author

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
Copy link
Owner

jmplonka commented May 4, 2023

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

@marcocecchiscmgroup
Copy link
Author

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
Copy link
Owner

jmplonka commented May 4, 2023

I was only too long away from this project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants