Moving tag processing into Tag itself.

This commit is contained in:
Markus Lampert 2016-11-18 15:35:30 -08:00
parent fc55d7ae23
commit a9f246b466
3 changed files with 225 additions and 80 deletions

View File

@ -1,95 +1,95 @@
<RCC>
<qresource>
<file>icons/preferences-path.svg</file>
<file>icons/Path-Toolpath.svg</file>
<file>icons/Path-Compound.svg</file>
<file>icons/Path-Shape.svg</file>
<file>icons/Path-Profile.svg</file>
<file>icons/Path-Contour.svg</file>
<file>icons/Path-Pocket.svg</file>
<file>icons/Path-Drilling.svg</file>
<file>icons/Path-Job.svg</file>
<file>icons/Path-Dressup.svg</file>
<file>icons/Path-Hop.svg</file>
<file>icons/Path-Datums.svg</file>
<file>icons/Path-Copy.svg</file>
<file>icons/Path-ToolTable.svg</file>
<file>icons/Path-LengthOffset.svg</file>
<file>icons/Path-Axis.svg</file>
<file>icons/Path-Stock.svg</file>
<file>icons/Path-Plane.svg</file>
<file>icons/Path-Post.svg</file>
<file>icons/Path-LoadTool.svg</file>
<file>icons/Path-Comment.svg</file>
<file>icons/Path-Stop.svg</file>
<file>icons/Path-Machine.svg</file>
<file>icons/Path-Kurve.svg</file>
<file>icons/Path-FaceProfile.svg</file>
<file>icons/Path-FacePocket.svg</file>
<file>icons/Path-Array.svg</file>
<file>icons/Path-Custom.svg</file>
<file>icons/Path-Inspect.svg</file>
<file>icons/Path-ToolChange.svg</file>
<file>icons/Path-SimpleCopy.svg</file>
<file>icons/Path-Engrave.svg</file>
<file>icons/Path-Sanity.svg</file>
<file>icons/Path-3DSurface.svg</file>
<file>icons/Path-Speed.svg</file>
<file>icons/Path-Array.svg</file>
<file>icons/Path-Axis.svg</file>
<file>icons/Path-BaseGeometry.svg</file>
<file>icons/Path-Comment.svg</file>
<file>icons/Path-Compound.svg</file>
<file>icons/Path-Contour.svg</file>
<file>icons/Path-Copy.svg</file>
<file>icons/Path-Custom.svg</file>
<file>icons/Path-Datums.svg</file>
<file>icons/Path-Depths.svg</file>
<file>icons/Path-Dressup.svg</file>
<file>icons/Path-Drilling.svg</file>
<file>icons/Path-Engrave.svg</file>
<file>icons/Path-FacePocket.svg</file>
<file>icons/Path-FaceProfile.svg</file>
<file>icons/Path-Face.svg</file>
<file>icons/Path-Heights.svg</file>
<file>icons/Path-Hop.svg</file>
<file>icons/Path-Inspect.svg</file>
<file>icons/Path-Job.svg</file>
<file>icons/Path-Kurve.svg</file>
<file>icons/Path-LengthOffset.svg</file>
<file>icons/Path-LoadTool.svg</file>
<file>icons/Path-MachineLathe.svg</file>
<file>icons/Path-MachineMill.svg</file>
<file>icons/Path-Machine.svg</file>
<file>icons/Path-OperationA.svg</file>
<file>icons/Path-OperationB.svg</file>
<file>icons/Path-Plane.svg</file>
<file>icons/Path-Pocket.svg</file>
<file>icons/Path-Post.svg</file>
<file>icons/Path-Profile-Edges.svg</file>
<file>icons/Path-Profile-Face.svg</file>
<file>icons/Path-Profile.svg</file>
<file>icons/Path-Sanity.svg</file>
<file>icons/Path-SelectLoop.svg</file>
<file>icons/Path-Face.svg</file>
<file>translations/Path_de.qm</file>
<file>icons/Path-Shape.svg</file>
<file>icons/Path-SimpleCopy.svg</file>
<file>icons/Path-Speed.svg</file>
<file>icons/Path-Stock.svg</file>
<file>icons/Path-Stop.svg</file>
<file>icons/Path-ToolChange.svg</file>
<file>icons/Path-Toolpath.svg</file>
<file>icons/Path-ToolTable.svg</file>
<file>icons/preferences-path.svg</file>
<file>panels/ContourEdit.ui</file>
<file>panels/DlgJobChooser.ui</file>
<file>panels/DlgSelectPostProcessor.ui</file>
<file>panels/DlgToolCopy.ui</file>
<file>panels/DogboneEdit.ui</file>
<file>panels/DrillingEdit.ui</file>
<file>panels/EngraveEdit.ui</file>
<file>panels/HoldingTagsEdit.ui</file>
<file>panels/JobEdit.ui</file>
<file>panels/MillFaceEdit.ui</file>
<file>panels/PocketEdit.ui</file>
<file>panels/ProfileEdgesEdit.ui</file>
<file>panels/ProfileEdit.ui</file>
<file>panels/RemoteEdit.ui</file>
<file>panels/SurfaceEdit.ui</file>
<file>panels/ToolControl.ui</file>
<file>panels/ToolEdit.ui</file>
<file>panels/ToolLibraryEditor.ui</file>
<file>preferences/PathJob.ui</file>
<file>translations/Path_af.qm</file>
<file>translations/Path_zh-CN.qm</file>
<file>translations/Path_zh-TW.qm</file>
<file>translations/Path_hr.qm</file>
<file>translations/Path_cs.qm</file>
<file>translations/Path_nl.qm</file>
<file>translations/Path_de.qm</file>
<file>translations/Path_el.qm</file>
<file>translations/Path_es-ES.qm</file>
<file>translations/Path_fi.qm</file>
<file>translations/Path_fr.qm</file>
<file>translations/Path_hr.qm</file>
<file>translations/Path_hu.qm</file>
<file>translations/Path_it.qm</file>
<file>translations/Path_ja.qm</file>
<file>translations/Path_nl.qm</file>
<file>translations/Path_no.qm</file>
<file>translations/Path_pl.qm</file>
<file>translations/Path_pt-BR.qm</file>
<file>translations/Path_pt-PT.qm</file>
<file>translations/Path_ro.qm</file>
<file>translations/Path_ru.qm</file>
<file>translations/Path_sr.qm</file>
<file>translations/Path_es-ES.qm</file>
<file>translations/Path_sv-SE.qm</file>
<file>translations/Path_uk.qm</file>
<file>translations/Path_it.qm</file>
<file>translations/Path_pt-BR.qm</file>
<file>translations/Path_el.qm</file>
<file>translations/Path_sk.qm</file>
<file>translations/Path_tr.qm</file>
<file>translations/Path_sl.qm</file>
<file>panels/EngraveEdit.ui</file>
<file>panels/DrillingEdit.ui</file>
<file>panels/PocketEdit.ui</file>
<file>panels/ProfileEdit.ui</file>
<file>panels/SurfaceEdit.ui</file>
<file>panels/RemoteEdit.ui</file>
<file>panels/ToolControl.ui</file>
<file>panels/ToolLibraryEditor.ui</file>
<file>panels/JobEdit.ui</file>
<file>panels/DlgToolCopy.ui</file>
<file>panels/ToolEdit.ui</file>
<file>panels/DlgJobChooser.ui</file>
<file>panels/ContourEdit.ui</file>
<file>panels/MillFaceEdit.ui</file>
<file>panels/ProfileEdgesEdit.ui</file>
<file>panels/DogboneEdit.ui</file>
<file>panels/DlgSelectPostProcessor.ui</file>
<file>preferences/PathJob.ui</file>
<file>panels/HoldingTagsEdit.ui</file>
<file>translations/Path_sr.qm</file>
<file>translations/Path_sv-SE.qm</file>
<file>translations/Path_tr.qm</file>
<file>translations/Path_uk.qm</file>
<file>translations/Path_zh-CN.qm</file>
<file>translations/Path_zh-TW.qm</file>
</qresource>
</RCC>

View File

@ -25,8 +25,7 @@ class PathWorkbench (Workbench):
"Path workbench"
def __init__(self):
self.__class__.Icon = FreeCAD.getResourceDir(
) + "Mod/Path/Resources/icons/PathWorkbench.svg"
self.__class__.Icon = FreeCAD.getResourceDir() + "Mod/Path/Resources/icons/PathWorkbench.svg"
self.__class__.MenuText = "Path"
self.__class__.ToolTip = "Path workbench"
@ -120,11 +119,6 @@ class PathWorkbench (Workbench):
# "Path", "Remote Operations")], remotecmdlist)
self.appendMenu([translate("Path", "&Path")], extracmdlist)
# Add preferences pages
import os
FreeCADGui.addPreferencePage(FreeCAD.getHomePath(
) + os.sep + "Mod" + os.sep + "Path" + os.sep + "PathScripts" + os.sep + "DlgSettingsPath.ui", "Path")
Log('Loading Path workbench... done\n')
def GetClassName(self):
@ -142,7 +136,7 @@ class PathWorkbench (Workbench):
if len(FreeCADGui.Selection.getSelection()) == 1:
if FreeCADGui.Selection.getSelection()[0].isDerivedFrom("Path::Feature"):
self.appendContextMenu("", ["Path_Inspect"])
if "Profile" in FreeCADGui.Selection.getSelection()[0].Name:
if "Profile" or "Contour" in FreeCADGui.Selection.getSelection()[0].Name:
self.appendContextMenu("", ["Add_Tag"])
self.appendContextMenu("", ["Set_StartPoint"])
self.appendContextMenu("", ["Set_EndPoint"])

View File

@ -67,6 +67,29 @@ def getAngle(v):
return -a
return a
class Side:
Left = +1
Right = -1
Straight = 0
On = 0
@classmethod
def toString(cls, side):
if side == cls.Left:
return 'Left'
if side == cls.Right:
return 'Right'
return 'On'
@classmethod
def of(cls, ptRef, pt):
d = -ptRef.x*pt.y + ptRef.y*pt.x
if d < 0:
return cls.Left
if d > 0:
return cls.Right
return cls.Straight
def testPrintAngle(v):
print("(%+.2f, %+.2f, %+.2f): %+.2f" % (v.x, v.y, v.z, getAngle(v)/math.pi))
@ -81,18 +104,73 @@ def testAngle(x=1, y=1):
testPrintAngle(FreeCAD.Vector( 1*x,-1*y, 0))
def testPrintSide(pt1, pt2):
print('(%.2f, %.2f) - (%.2f, %.2f) -> %s' % (pt1.x, pt1.y, pt2.x, pt2.y, Side.toString(Side.of(pt1, pt2))))
def testSide():
testPrintSide(FreeCAD.Vector( 1, 0, 0), FreeCAD.Vector( 1, 0, 0))
testPrintSide(FreeCAD.Vector( 1, 0, 0), FreeCAD.Vector(-1, 0, 0))
testPrintSide(FreeCAD.Vector( 1, 0, 0), FreeCAD.Vector( 0, 1, 0))
testPrintSide(FreeCAD.Vector( 1, 0, 0), FreeCAD.Vector( 0,-1, 0))
def pathCommandForEdge(edge):
pt = edge.valueAt(edge.LastParameter)
params = {'X': pt.x, 'Y': pt.y, 'Z': pt.z}
if type(edge.Curve) == Part.Line:
return Part.Command('G1', params)
p1 = edge.valueAt(edge.FirstParameter)
p2 = edge.valueAt((edge.FirstParameter + edge.LastParameter)/2)
p3 = pt
if Side.Left == Side.of(p2 - p1, p3 - p2):
cmd = 'G3'
else:
cmd = 'G2'
offset = pt1 - edge.Curve.Center
params.update({'I': offset.x, 'J': offset.y, 'K': offset.z})
return Part.Command(cmd, params)
class Tag:
def __init__(self, x, y, width, height, angle, enabled):
self.x = x
self.y = y
self.width = width
self.height = height
self.angle = angle
self.width = math.fabs(width)
self.height = math.fabs(height)
self.angle = math.fabs(angle)
self.enabled = enabled
def toString(self):
return str((self.x, self.y, self.width, self.height, self.angle, self.enabled))
def originAt(self, z):
return FreeCAD.Vector(self.x, self.y, z)
def createSolidsAt(self, z):
r1 = self.width / 2
height = self.height
if self.angle == 90 and height > 0:
self.solid = Part.makeCylinder(r1, height)
self.core = self.solid
elif self.angle > 0.0 and height > 0.0:
tangens = math.tan(math.radians(self.angle))
dr = height / tangens
if dr < r1:
r2 = r1 - dr
self.core = Part.makeCylinder(r2, height)
else:
r2 = 0
height = r1 * tangens
self.core = None
self.solid = Part.makeCone(r1, r2, height)
else:
# degenerated case - no tag
self.solid = Part.makeSphere(r1 / 10000)
self.core = None
self.solid.translate(self.originAt(z))
if self.core:
self.core.translate(self.originAt(z))
@classmethod
def FromString(cls, string):
try:
@ -150,11 +228,25 @@ class PathData:
bottom = [e for e in edges if e.Vertexes[0].Point.z == minZ and e.Vertexes[1].Point.z == minZ]
wire = Part.Wire(bottom)
if wire.isClosed():
return wire
return Part.Wire(self.sortedBase(bottom))
# if we get here there are already holding tags, or we're not looking at a profile
# let's try and insert the missing pieces - another day
raise ValueError("Selected path doesn't seem to be a Profile operation.")
def sortedBase(self, base):
# first find the exit point, where base wire is closed
edges = [e for e in self.edges if e.valueAt(e.FirstParameter).z == self.minZ and e.valueAt(e.LastParameter).z != self.maxZ]
exit = sorted(edges, key=lambda e: -e.valueAt(e.LastParameter).z)[0]
pt = exit.valueAt(exit.FirstParameter)
# then find the first base edge, and sort them until done
ordered = []
while base:
edge = [e for e in base if e.valueAt(e.FirstParameter) == pt][0]
ordered.append(edge)
base.remove(edge)
pt = edge.valueAt(edge.LastParameter)
return ordered
def findZLimits(self, edges):
# not considering arcs and spheres in Z direction, find the highes and lowest Z values
@ -268,6 +360,18 @@ class PathData:
def pathLength(self):
return self.base.Length
def sortedTags(self, tags):
ordered = []
for edge in self.base.Edges:
ts = [t for t in tags if DraftGeomUtils.isPtOnEdge(t.originAt(self.minZ), edge)]
for t in sorted(ts, key=lambda t: (t.originAt(self.minZ) - edge.valueAt(edge.FirstParameter)).Length):
tags.remove(t)
ordered.append(t)
if tags:
raise ValueError("There's something really wrong here")
return ordered
class ObjectDressup:
def __init__(self, obj):
@ -286,6 +390,46 @@ class ObjectDressup:
def generateTags(self, obj, count=None, width=None, height=None, angle=90, spacing=None):
return self.pathData.generateTags(obj, count, width, height, angle, spacing)
def tagIntersection(self, face, edge):
p1 = edge.valueAt(edge.FirstParameter)
pts = edge.Curve.intersect(face.Surface)
if pts[0]:
closest = sorted(pts[0], key=lambda pt: (pt - p1).Length)[0]
return closest
return None
def createPath(self, edges, tagSolids):
commands = []
i = 0
while i != len(edges):
edge = edges[i]
while edge:
for solid in tagSolids:
for face in solid.Faces:
pt = self.tagIntersection(face, edge)
if pt:
if pt == edge.valueAt(edge.FirstParameter):
pt
elif pt != edge.valueAt(edge.LastParameter):
parameter = edge.Curve.parameter(pt)
wire = edge.split(parameter)
commands.append(pathCommandForEdge(wire.Edges[0]))
edge = wire.Edges[1]
break;
else:
commands.append(pathCommandForEdge(edge))
edge = None
i += 1
break
if not edge:
break
if edge:
commands.append(pathCommandForEdge(edge))
edge = None
return self.obj.Path
def execute(self, obj):
if not obj.Base:
return
@ -323,9 +467,16 @@ class ObjectDressup:
if tag.enabled:
#print("x=%s, y=%s, z=%s" % (tag.x, tag.y, pathData.minZ))
debugMarker(FreeCAD.Vector(tag.x, tag.y, pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5)
tags = pathData.sortedTags(tags)
for tag in tags:
tag.createSolidsAt(pathData.minZ)
self.fingerprint = [tag.toString() for tag in tags]
self.tags = tags
obj.Path = obj.Base.Path
#obj.Path = self.createPath(pathData.edges, tags)
obj.Path = self.Base.Path
def setTags(self, obj, tags):
obj.Tags = [tag.toString() for tag in tags]