Skip to content

Commit

Permalink
Fix for issue #70 - Face.build() failing to determine the correct face
Browse files Browse the repository at this point in the history
  • Loading branch information
jmplonka committed Apr 29, 2023
1 parent 3ff61c5 commit 1c916d9
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 54 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

_.png
0???.sat
.DS_Store
65 changes: 50 additions & 15 deletions Acis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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))):
Expand Down
47 changes: 19 additions & 28 deletions Acis2Step.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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):
Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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()
Expand All @@ -1777,7 +1767,6 @@ def export(filename, satHeader, satBodies):

_initExport()

global _scale
_scale = satHeader.scale

appPrtDef = APPLICATION_PROTOCOL_DEFINITION()
Expand Down Expand Up @@ -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
13 changes: 6 additions & 7 deletions importerDXF.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
1 change: 0 additions & 1 deletion importerSAT.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion metadata.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ 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

; 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.
Expand Down
4 changes: 2 additions & 2 deletions package.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
<name>InventorLoader</name>
<version>1.3</version>
<date>2022-01-31</date>
<version>1.4</version>
<date>2023-04-29</date>
<maintainer email="[email protected]">jmplonka</maintainer>
<license>LGPLv3</license>
<description>This plugin enables FreeCAD to import Inventor part files (*.IPT), ACIS files (*.SAT, *.SAB), 3D-Solids from DXF files and Fusion360 (*.f3d) files.</description>
Expand Down

1 comment on commit 1c916d9

@marcocecchiscmgroup
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<return None, acisFace.sense == 'forwared'

return None, acisFace.sense == 'forward'

Please sign in to comment.