|
|
|
|
import FreeCAD
|
|
|
|
|
import FreeCAD as App
|
|
|
|
|
import FreeCADGui as Gui
|
|
|
|
|
import numpy as np
|
|
|
|
|
import math
|
|
|
|
|
import Part
|
|
|
|
|
from shapely.geometry import Polygon, Point
|
|
|
|
|
from pyslm import hatching as hatching
|
|
|
|
|
from pyslm.geometry.geometry import LayerGeometryType
|
|
|
|
|
from typing import List
|
|
|
|
|
import rdp
|
|
|
|
|
from .utils import *
|
|
|
|
|
from freecad.LaserCladdingWorkbench.path import LaserPath
|
|
|
|
|
from pivy import coin
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LaserPad:
|
|
|
|
|
def __init__(self, obj, face):
|
|
|
|
|
obj.addProperty("App::PropertyFloatConstraint", "tool_width", "Parameters", "Width of the circular effect area of Laser in mm")
|
|
|
|
|
obj.tool_width = (2.3, 0.01, 20.0, 0.1)
|
|
|
|
|
|
|
|
|
|
obj.addProperty("App::PropertyFloatConstraint", "hatch_angle", "Hatching Parameters", "Hatch Angle")
|
|
|
|
|
obj.hatch_angle = (0.0, 0.0, 180.0, 1.0)
|
|
|
|
|
|
|
|
|
|
obj.addProperty("App::PropertyFloat", "hatch_volume_offset", "Parameters", "Offset between internal and external boundary")
|
|
|
|
|
obj.hatch_volume_offset = 2.3*0.5
|
|
|
|
|
|
|
|
|
|
obj.addProperty("App::PropertyFloat", "hatch_spot_compensation", "Parameters", "Additional offset to account for laser spot size")
|
|
|
|
|
obj.hatch_spot_compensation = 2.3*0.5
|
|
|
|
|
|
|
|
|
|
obj.addProperty("App::PropertyInteger", "hatch_inner_contours", "Parameters", "Inner Contours")
|
|
|
|
|
obj.hatch_inner_contours = 1
|
|
|
|
|
|
|
|
|
|
obj.addProperty("App::PropertyInteger", "hatch_outer_contours", "Parameters", "Outer Contours")
|
|
|
|
|
obj.hatch_outer_contours = 0
|
|
|
|
|
|
|
|
|
|
obj.addProperty("App::PropertyFloat", "hatch_contour_offset", "Parameters", "Contour Offset")
|
|
|
|
|
obj.hatch_contour_offset = 2.3
|
|
|
|
|
|
|
|
|
|
obj.addProperty("App::PropertyFloat", "z_offset", "Parameters", "Height (Z) Offset")
|
|
|
|
|
obj.z_offset = 0
|
|
|
|
|
|
|
|
|
|
obj.addProperty("App::PropertyLinkGlobal","ref_body","Reference","body")
|
|
|
|
|
obj.ref_body = face[0]
|
|
|
|
|
|
|
|
|
|
obj.addProperty("App::PropertyString","ref_surface","Reference","face")
|
|
|
|
|
obj.ref_surface = face[1]
|
|
|
|
|
|
|
|
|
|
obj.addExtension("App::GroupExtensionPython")
|
|
|
|
|
obj.Proxy = self
|
|
|
|
|
self._create_path(obj)
|
|
|
|
|
|
|
|
|
|
def _create_path(self, obj):
|
|
|
|
|
print("Create_path")
|
|
|
|
|
print("obj.ref_surface", obj.ref_surface)
|
|
|
|
|
# if len(obj.Group):
|
|
|
|
|
# obj.removeObjectsFromDocument()
|
|
|
|
|
face = obj.ref_body.getSubObject(obj.ref_surface)
|
|
|
|
|
myHatcher = hatching.Hatcher()
|
|
|
|
|
myHatcher.hatchDistance = obj.tool_width
|
|
|
|
|
myHatcher.hatchAngle = obj.hatch_angle
|
|
|
|
|
myHatcher.volumeOffsetHatch = obj.hatch_volume_offset
|
|
|
|
|
myHatcher.spotCompensation = obj.hatch_spot_compensation
|
|
|
|
|
myHatcher.numInnerContours = obj.hatch_inner_contours
|
|
|
|
|
myHatcher.numOuterContours = obj.hatch_outer_contours
|
|
|
|
|
myHatcher.contourOffset = obj.hatch_contour_offset
|
|
|
|
|
myHatcher.hatchSortMethod = hatching.AlternateSort()
|
|
|
|
|
|
|
|
|
|
polygon_coords = get_coords_from_shape(face)
|
|
|
|
|
print("===== coords from shape ======")
|
|
|
|
|
print("{}", polygon_coords)
|
|
|
|
|
print("===== ======")
|
|
|
|
|
|
|
|
|
|
## debug
|
|
|
|
|
# for poly in polygon_coords:
|
|
|
|
|
# c = Part.makeCompound([])
|
|
|
|
|
# vertex_list = [App.Vector(x,y,0) for x,y in poly]
|
|
|
|
|
# c.add(Part.makePolygon(vertex_list))
|
|
|
|
|
# Part.show(c)
|
|
|
|
|
|
|
|
|
|
layer = myHatcher.hatch(polygon_coords)
|
|
|
|
|
|
|
|
|
|
contours_geoms = layer.getContourGeometry()
|
|
|
|
|
hatch_geoms = layer.getHatchGeometry()
|
|
|
|
|
|
|
|
|
|
contours = create_contour_lines(contours_geoms)
|
|
|
|
|
hatchlines = create_hatch_lines(hatch_geoms)
|
|
|
|
|
|
|
|
|
|
proj_contours = project_to_face(contours, face)
|
|
|
|
|
proj_hatchlines = project_to_face(hatchlines, face)
|
|
|
|
|
# DEBUG Part.show(Part.makeCompound(hatchlines))
|
|
|
|
|
obj.purgeTouched()
|
|
|
|
|
|
|
|
|
|
for pc in proj_contours:
|
|
|
|
|
p = Part.makeCompound(pc)
|
|
|
|
|
contours_comp = obj.newObject("Part::Feature", "Contours")
|
|
|
|
|
LaserPath(contours_comp)
|
|
|
|
|
contours_comp.Shape = p
|
|
|
|
|
contours_comp.ViewObject.LineColor = (0.5, 0.8, 0.9)
|
|
|
|
|
contours_comp.Placement.Base.z = obj.z_offset
|
|
|
|
|
|
|
|
|
|
print("Creating Hatch Part")
|
|
|
|
|
hatches_comp = obj.newObject("Part::Feature", "Hatchlines")
|
|
|
|
|
p = Part.makeCompound([])
|
|
|
|
|
for pc in proj_hatchlines:
|
|
|
|
|
p.add(Part.makeCompound(pc))
|
|
|
|
|
|
|
|
|
|
LaserPath(hatches_comp)
|
|
|
|
|
hatches_comp.Shape = p
|
|
|
|
|
hatches_comp.ViewObject.LineColor = (0.9, 0.2, 0.2)
|
|
|
|
|
hatches_comp.Placement.Base.z = obj.z_offset
|
|
|
|
|
print("Group: ", obj.Group)
|
|
|
|
|
|
|
|
|
|
def onChanged(self, fp, prop):
|
|
|
|
|
'''Do something when a property has changed'''
|
|
|
|
|
# App.Console.PrintMessage("Change property: " + str(prop) + "\n")
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def execute(self, fp):
|
|
|
|
|
App.Console.PrintMessage("Recompute LaserPad\n")
|
|
|
|
|
# self._create_path(fp)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ViewProviderLaserPad:
|
|
|
|
|
def __init__(self, obj):
|
|
|
|
|
'''Set this object to the proxy object of the actual view provider'''
|
|
|
|
|
obj.addProperty("App::PropertyColor", "Color", "Box", "Color of the box").Color=(1.0, 0.0, 0.0)
|
|
|
|
|
obj.addExtension("Gui::ViewProviderGroupExtensionPython")
|
|
|
|
|
self.Object = obj.Object
|
|
|
|
|
obj.Proxy = self
|
|
|
|
|
|
|
|
|
|
def claimChildren(self):
|
|
|
|
|
"""Return objects that will be placed under it in the tree view."""
|
|
|
|
|
App.Console.PrintMessage("Reclaim children\n")
|
|
|
|
|
|
|
|
|
|
claimed = []
|
|
|
|
|
App.Console.PrintMessage(self.__dict__)
|
|
|
|
|
for o in self.Object.OutList:
|
|
|
|
|
if o.TypeId == 'Part::Feature':
|
|
|
|
|
claimed.append(o)
|
|
|
|
|
return claimed
|
|
|
|
|
|
|
|
|
|
def attach(self, vobj):
|
|
|
|
|
self.Object = vobj.Object
|
|
|
|
|
self.ViewObject = vobj
|
|
|
|
|
self.standard = coin.SoGroup()
|
|
|
|
|
vobj.addDisplayMode(self.standard,"Standard");
|
|
|
|
|
|
|
|
|
|
def getDisplayModes(self, obj):
|
|
|
|
|
"'''Return a list of display modes.'''"
|
|
|
|
|
return ["Standard"]
|
|
|
|
|
|
|
|
|
|
def getDefaultDisplayMode(self):
|
|
|
|
|
"'''Return the name of the default display mode. It must be defined in getDisplayModes.'''"
|
|
|
|
|
return "Standard"
|
|
|
|
|
|
|
|
|
|
def updateData(self, fp, prop):
|
|
|
|
|
'''If a property of the handled feature has changed we have the chance to handle this here'''
|
|
|
|
|
# fp is the handled feature, prop is the name of the property that has changed
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def setDisplayMode(self, mode):
|
|
|
|
|
'''Map the display mode defined in attach with those defined in getDisplayModes.\
|
|
|
|
|
Since they have the same names nothing needs to be done. This method is optional'''
|
|
|
|
|
return mode
|
|
|
|
|
|
|
|
|
|
def onChanged(self, vp, prop):
|
|
|
|
|
'''Here we can do something when a single property got changed'''
|
|
|
|
|
# App.Console.PrintMessage("Change property: " + str(prop) + "\n")
|
|
|
|
|
# if prop == "Color":
|
|
|
|
|
# c = vp.getPropertyByName("Color")
|
|
|
|
|
# self.color.rgb.setValue(c[0],c[1],c[2])
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def getIcon(self):
|
|
|
|
|
'''Return the icon in XPM format which will appear in the tree view. This method is\
|
|
|
|
|
optional and if not defined a default icon is shown.'''
|
|
|
|
|
return """
|
|
|
|
|
/* XPM */
|
|
|
|
|
static const char * ViewProviderBox_xpm[] = {
|
|
|
|
|
"16 16 6 1",
|
|
|
|
|
" c None",
|
|
|
|
|
". c #141010",
|
|
|
|
|
"+ c #615BD2",
|
|
|
|
|
"@ c #C39D55",
|
|
|
|
|
"# c #000000",
|
|
|
|
|
"$ c #57C355",
|
|
|
|
|
" ........",
|
|
|
|
|
" ......++..+..",
|
|
|
|
|
" .@@@@.++..++.",
|
|
|
|
|
" .@@@@.++..++.",
|
|
|
|
|
" .@@ .++++++.",
|
|
|
|
|
" ..@@ .++..++.",
|
|
|
|
|
"###@@@@ .++..++.",
|
|
|
|
|
"##$.@@$#.++++++.",
|
|
|
|
|
"#$#$.$$$........",
|
|
|
|
|
"#$$####### ",
|
|
|
|
|
"#$$#$$$$$# ",
|
|
|
|
|
"#$$#$$$$$# ",
|
|
|
|
|
"#$$#$$$$$# ",
|
|
|
|
|
" #$#$$$$$# ",
|
|
|
|
|
" ##$$$$$# ",
|
|
|
|
|
" ####### "};
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __getstate__(self):
|
|
|
|
|
'''When saving the document this object gets stored using Python's json module.\
|
|
|
|
|
Since we have some un-serializable parts here -- the Coin stuff -- we must define this method\
|
|
|
|
|
to return a tuple of all serializable objects or None.'''
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def __setstate__(self, state):
|
|
|
|
|
'''When restoring the serialized object from document we have the chance to set some internals here.\
|
|
|
|
|
Since no data were serialized nothing needs to be done here.'''
|
|
|
|
|
return None
|