diff --git a/.gitignore b/.gitignore index 3865d7e..5f5eded 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ _.png 0???.sat +.DS_Store diff --git a/Acis.py b/Acis.py index 875412f..10ddd8e 100644 --- a/Acis.py +++ b/Acis.py @@ -1382,6 +1382,31 @@ def buildSurfaceCircle(self, r, min_V, max_V): helix.translate(self.posCenter) return helix.toShape() + def buildSurfaceLine(self, p1, p2): + min_U = self.radAngles.getLowerLimit() + max_U = self.radAngles.getUpperLimit() + r_maj = self.dirMajor.Length + r_min = self.dirMinor.Length + pitch = self.dirPitch.Length + steps_U = Helix.calcSteps(min_U, max_U, 8) + handed = 1 if (self.isLeftHanded()) else -1 + points = [] + + for u in steps_U: + c = Helix.calcPoint(u, min_U, self.facApex, r_maj, r_min, handed, pitch) + m = MAT(cos(u), sin(u), 0, 0, -sin(u), cos(u), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) + p_1 = m.multiply(p1) + c + p_2 = m.multiply(p2) + c + points.append((p_1, p_2)) + + # bring the helix into the correct position: + helix = Part.BSplineSurface() + helix.interpolate(points) + 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) + return helix.toShape() + class Range(object): def __init__(self, type, limit, scale = 1.0): self.type = type @@ -1779,7 +1804,12 @@ def getLoops(self): def getNext(self): return None if (self._next is None) else self._next.entity def getLoop(self): return None if (self._loop is None) else self._loop.entity def getParent(self): return None if (self._parent is None) else self._parent.entity - def getSurface(self): return None if (self._surface is None) else self._surface.entity + def getSurface(self): + if (self._surface): + if (self._surface.entity.subtype == 'ref'): + return self._surface.entity.getSurface() + return self._surface.entity + return None def buildCoEdges(self): edges = [] loop = self.getLoop() @@ -1790,10 +1820,9 @@ def buildCoEdges(self): def build(self): if (self.__ready_to_build__): self.__ready_to_build__ = False - self._surface = self.getSurface() edges = self.buildCoEdges() if (self._surface): - surface = self._surface.build() + surface = self.getSurface().build() if (surface): if (self.sense == 'reversed'): surface.reverse() @@ -2403,6 +2432,7 @@ def setHelix(self, chunks, index, inventor): self.shape = self.helix.build() if (not self.setProjectionSurface(s1, p1)): self.setProjectionSurface(s2, p2) + self.__ready_to_build__ = (self.shape is None) return i def setInt(self, chunks, index, inventor): i = self.setSurfaceCurve(chunks, index, inventor, 'int_int_cur') @@ -3352,21 +3382,20 @@ def setG2Blend(self, chunks, index, inventor): return i def setHelixCircle(self, chunks, index, inventor): self.subtype = 'helix_spl_circ' - angle, i = getInterval(chunks, index, MIN_PI, MAX_PI, 1.0) - dime1, i = getInterval(chunks, i, -MAX_LEN, MAX_LEN, getScale()) - length, i = getLength(chunks, i) # pi / 2 - curve = CurveInt(subtype="helix_int_cur") - i = curve.setHelix(chunks, i, inventor) - radius, i = getLength(chunks, i) # radius of the circle - self.shape = curve.helix.buildSurfaceCircle(radius, angle.getLowerLimit(), angle.getUpperLimit()) + self.angle, i = getInterval(chunks, index, MIN_PI, MAX_PI, 1.0) + self.dime1, i = getInterval(chunks, i, -MAX_LEN, MAX_LEN, getScale()) + self.length, i = getLength(chunks, i) # pi / 2 + self.path = CurveInt(subtype="helix_int_cur") + i = self.path.setHelix(chunks, i, inventor) + self.radius, i = getLength(chunks, i) # radius of the circle return i def setHelixLine(self, chunks, index, inventor): self.subtype = 'helix_spl_line' - angle, i = getInterval(chunks, index, MIN_PI, MAX_PI, 1.0) - dime1, i = getInterval(chunks, i, -MAX_LEN, MAX_LEN, 1.0) - curve = CurveInt(subtype="helix_int_cur") - i = curve.setHelix(chunks, i, inventor) - posCenter, i = getLocation(chunks, i) + self.angle, i = getInterval(chunks, index, MIN_PI, MAX_PI, 1.0) + self.dime1, i = getInterval(chunks, i, -MAX_LEN, MAX_LEN, 1.0) + self.path = CurveInt(subtype="helix_int_cur") + i = self.path.setHelix(chunks, i, inventor) + self.origin, i = getLocation(chunks, i) return i def setLoft(self, chunks, index, inventor): self.ls1, i = self._readLofSection(chunks, index, inventor) @@ -3778,6 +3807,7 @@ def setSubtype(self, chunks, index): if (self.record is None): self.record = Record('spline') self.record.index = self.index + self.record.entity = self i = self.setBulk(chunks, i + 1) assert (chunks[i].tag == TAG_SUBTYPE_CLOSE), u"-%s %s - pending chunks to read: %s" %(self.index, self.subtype, chunks[i:]) self.rangeU, i = getInterval(chunks, i + 1, MIN_INF, MAX_INF, getScale()) @@ -3872,6 +3902,11 @@ def build(self, face = None): path = self.path.build(None, None) if (path): self.shape = Part.makeSweepSurface(path, profile) + elif (self.subtype == 'helix_spl_circ'): + self.shape = self.path.helix.buildSurfaceCircle(self.radius, self.angle.getLowerLimit(), self.angle.getUpperLimit()) +# elif (self.subtype == 'helix_spl_line'): +# # TODO pt1 = ???, pt2 = ??? +# self.shape = self.path.helix.buildSurfaceLine(pt1, pt2) if (isinstance(self.surface, Surface)): self.shape = self.surface.build() if ((self.shape is not None) and (isinstance(self.shape.Surface, Part.SurfaceOfRevolution))): diff --git a/Acis2Step.py b/Acis2Step.py index d646a43..5e9e84d 100644 --- a/Acis2Step.py +++ b/Acis2Step.py @@ -11,7 +11,7 @@ from datetime import datetime from importerUtils import isEqual, getDumpFolder from FreeCAD import Vector as VEC, Rotation as ROT, Placement as PLC, Matrix as MAT -from importerUtils import logInfo, logWarning, logError, isEqual1D, getColorDefault, getDumpFolder, getAuthor, getDescription +from importerUtils import logInfo, logWarning, logError, logAlways, isEqual1D, getColorDefault, getDumpFolder, getAuthor, getDescription from importerConstants import CENTER, DIR_X, DIR_Y, DIR_Z, EPS if (sys.version_info.major > 2): @@ -486,7 +486,6 @@ def _createCoEdge(acisCoEdge): e = _createEdgeCurve(p1, p2, curve, (acisEdge.sense == 'forward')) oe.edge = e return oe - logError("Failed to create CoEdge for %s", acisCurve) return None def _createBoundaries(acisLoops): @@ -637,7 +636,7 @@ def _createSurfaceSpline(acisFace): return _createSurfaceRevolution(surface.profile, surface.center, surface.axis, acisFace.sense) if (isinstance(shape, Part.Toroid)): return _createSurfaceToroid(shape.MajorRadius, shape.MinorRadius, shape.Center, shape.Axis, acisFace.sense) - return None + return None, acisFace.sense == 'forwared' def _createSurfaceToroid(major, minor, center, axis, sense): torus = TOROIDAL_SURFACE('', None, major, math.fabs(minor)) @@ -667,13 +666,15 @@ def _convertFace(acisFace, parentColor, context): color = getColor(acisFace) if (color is None): color = parentColor - surface, sense = _createSurfaceFaceShape(acisFace) - if (surface): - face = ADVANCED_FACE('', surface, sense) - face.bounds = _createBoundaries(acisFace.getLoops()) - assignColor(color, face, context) - return face - + try: + surface, sense = _createSurfaceFaceShape(acisFace) + if (surface): + face = ADVANCED_FACE('', surface, sense) + face.bounds = _createBoundaries(acisFace.getLoops()) + assignColor(color, face, context) + return face + except: + logError('Fatal forr acisFace= %s', acisFace.getSurface().getSurface()) return None def _convertShell(acisShell, representation, parentColor): @@ -687,8 +688,7 @@ def _convertShell(acisShell, representation, parentColor): for acisFace in faces: face = _convertFace(acisFace, color, representation.context) - if (not shell.addFace(face)): - logError("Failed to create SAT face %s", acisFace) + shell.addFace(face) assignColor(color, shell, representation.context) return shell @@ -850,16 +850,6 @@ def _setExported(l, b): if (isinstance(l, ExportEntity)): l.has_been_exported = b -def _exportList(l): - step = u'' - if (type(l) == list): - d = l - else: - d = l.values() - for p in d: - step += p.exportSTEP() - return step - def _createGeometricRepresentationList(*entities): return (GEOMETRIC_REPRESENTATION_CONTEXT(len(entities)),) + entities @@ -915,9 +905,7 @@ def exportProperties(self): try: if (isinstance(a, ReferencedEntity)): step += a.exportSTEP() - elif (type(a) == list): - step += _exportInternalList_(a) - elif (type(a) == tuple): + elif (type(a) in (list, tuple)): step += _exportInternalList_(a) except: logError(traceback.format_exc()) @@ -1768,6 +1756,8 @@ def _getParameters(self): ############################################################# def export(filename, satHeader, satBodies): + global _scale, _entities + dt = datetime.now() # 2018-05-13T08:03:27-07:00 user = getAuthor() desc = getDescription() @@ -1777,7 +1767,6 @@ def export(filename, satHeader, satBodies): _initExport() - global _scale _scale = satHeader.scale appPrtDef = APPLICATION_PROTOCOL_DEFINITION() @@ -1810,15 +1799,17 @@ def export(filename, satHeader, satBodies): step += u"\n" step += u"DATA;\n" - step += _exportList(_entities) + for entity in _entities: + step += entity.exportSTEP() step += u"ENDSEC;\n" step += u"END-ISO-10303-21;" with io.open(stepfile, 'wt', encoding="UTF-8") as stepFile: stepFile.write(step) - logInfo(u"STEP file written to '%s'.", stepfile) _finalizeExport() + logInfo(u"STEP file written to '%s'.", stepfile) + return stepfile diff --git a/importerDXF.py b/importerDXF.py index 668ccba..68f0a55 100644 --- a/importerDXF.py +++ b/importerDXF.py @@ -45,13 +45,12 @@ def read(filename): setDumpFolder(filename) doc = readfile(filename) for entry in doc.entities: - if (isinstance(entry, Solid3d)): - if (__is_binary__(entry)): - __read_binary__(entry.sab, '%s_%d' %(name, i)) - i += 1 - elif (__is_text__(entry)): - __read_text__(entry.sat, '%s_%d' %(os.path.basename(os.path.splitext(filename)[0]), i)) - i += 1 + if (__is_binary__(entry)): + __read_binary__(entry.sab, '%s_%d' %(name, i)) + i += 1 + elif (__is_text__(entry)): + __read_text__(entry.sat, '%s_%d' %(os.path.basename(os.path.splitext(filename)[0]), i)) + i += 1 return True def create3dModel(group, doc): diff --git a/importerSAT.py b/importerSAT.py index cdf8ad3..aa09805 100644 --- a/importerSAT.py +++ b/importerSAT.py @@ -5,7 +5,6 @@ Collection of classes necessary to read and analyse Autodesk (R) Invetor (R) files. ''' -from msilib import CreateRecord import os, FreeCAD, Part, ImportGui, io from importerUtils import logInfo, logAlways, chooseImportStrategyAcis, STRATEGY_SAT, STRATEGY_NATIVE, STRATEGY_STEP, setDumpFolder, getDumpFolder from Acis2Step import export diff --git a/metadata.txt b/metadata.txt index 63c9888..cba30b6 100644 --- a/metadata.txt +++ b/metadata.txt @@ -9,7 +9,7 @@ author=jmplonka qgisMinimumVersion=2.0 description=This plugin enables FreeCAD to import Inventor part files (*.IPT), ACIS files (*.SAT, *.SAB), 3D-Solids from DXF files and Fusion360 (*.f3d) files. about= -version=version 1.3 +version=version 1.4 tracker=https://github.com/jmplonka/InventorLoader/issues repository=https://github.com/jmplonka/InventorLoader ; end of mandatory metadata @@ -17,6 +17,7 @@ repository=https://github.com/jmplonka/InventorLoader ; start of optional metadata category= changelog=The changelog lists the plugin versions and their changes: + 1.4 - Replaced dxfgrabber by ezdxf 1.3 - Added support for Fusion360 file format (only SAT) 1.2 - Added support for Inventor 2021 file format 1.1 - Added DXF 3D-solids import. diff --git a/package.xml b/package.xml index e32b8e8..8fb2e1a 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ InventorLoader -1.3 -2022-01-31 +1.4 +2023-04-29 jmplonka LGPLv3 This plugin enables FreeCAD to import Inventor part files (*.IPT), ACIS files (*.SAT, *.SAB), 3D-Solids from DXF files and Fusion360 (*.f3d) files.