826 lines
34 KiB
Python
826 lines
34 KiB
Python
import FreeCAD
|
|
|
|
from . import ICONPATH, TRANSLATIONSPATH
|
|
from .Tools import spawnClassCommand, placement2axisvec, describeSubObject, getPrimitiveInfo #, RobossemblerFreeCadExportScenario
|
|
from .pddl import freecad2pddl
|
|
from .BoMList import run_BoM_list
|
|
from .export_entities import export_coordinate_systems
|
|
from .utils.freecad_processor import process_file
|
|
from .project_validator import validate_project
|
|
from .usecases.asm4parser_usecase import Asm4StructureParseUseCase
|
|
from .constraints_operator import create_assembly_parameters, create_fastener_set, create_assembly_sequence, create_clearance_constraint, export_assembly_settings
|
|
from .geometric_feasibility_predicate.main import main as structure_analysis
|
|
from .asm_graph import main as asm_graph
|
|
# from .autodock_generator import main as asm_layers
|
|
from .assembly_graph_generation import main as structure_graph
|
|
|
|
if FreeCAD.GuiUp:
|
|
import FreeCADGui
|
|
from pivy import coin
|
|
from PySide import QtCore, QtGui, QtSvg
|
|
import Part
|
|
import os
|
|
|
|
############################################################
|
|
# Frame Objects
|
|
############################################################
|
|
class Frame(object):
|
|
"""Basic freestanding frame"""
|
|
def __init__(self, obj):
|
|
obj.addProperty("App::PropertyPlacement",
|
|
"Placement", "Base",
|
|
"Placement of the frame")
|
|
obj.Placement = FreeCAD.Placement()
|
|
obj.Proxy = self
|
|
self.obj = obj
|
|
self.additional_data = {}
|
|
|
|
def onChanged(self, fp, prop):
|
|
pass
|
|
|
|
def execute(self, obj):
|
|
pass
|
|
|
|
def __getstate__(self):
|
|
return None
|
|
|
|
def __setstate__(self, state):
|
|
return None
|
|
|
|
def getDict(self):
|
|
d = {}
|
|
d["label"] = str(self.obj.Label)
|
|
d["placement"] = placement2axisvec(self.obj.Placement)
|
|
d.update(self.additional_data)
|
|
return d
|
|
|
|
|
|
class PartFrame(Frame):
|
|
"""Frame rigidly attached to a part frame.
|
|
Inherits the base placement from the part's frame, and placement is
|
|
relative to the part frame.
|
|
"""
|
|
def __init__(self, obj, partobj):
|
|
Frame.__init__(self, obj)
|
|
obj.addProperty("App::PropertyPlacementLink",
|
|
"Part", "Parent",
|
|
"The part to attach to.")
|
|
obj.Part = partobj
|
|
obj.setEditorMode("Part", 1)
|
|
|
|
def execute(self, obj):
|
|
if FreeCAD.GuiUp:
|
|
obj.ViewObject.Proxy.updateData(obj, "Placement")
|
|
|
|
def getDict(self):
|
|
d = Frame.getDict(self)
|
|
d["part"] = str(self.obj.Part.Label)
|
|
return d
|
|
|
|
|
|
class FeatureFrame(PartFrame):
|
|
"""Frame rigidly attached to a feature.
|
|
The feature frame is attached to a feature on a part. It gives both the
|
|
placement of the feature w.r.t. the part, and the placement from the
|
|
feature."""
|
|
def __init__(self, obj, partobj, featurePlacement):
|
|
PartFrame.__init__(self, obj, partobj)
|
|
obj.addProperty("App::PropertyPlacement",
|
|
"FeaturePlacement", "Feature",
|
|
"The frame attached to the feature.")
|
|
obj.addProperty("App::PropertyString",
|
|
"PrimitiveType", "Feature",
|
|
"The primitive type of the feature.")
|
|
obj.addProperty("App::PropertyString",
|
|
"ShapeType", "Feature",
|
|
"The shape type of the feature.")
|
|
obj.addProperty("App::PropertyString",
|
|
"Positioning", "Feature",
|
|
"The type of positioning used during creation.")
|
|
obj.FeaturePlacement = featurePlacement
|
|
|
|
def getDict(self):
|
|
d = PartFrame.getDict(self)
|
|
d["featureplacement"] = placement2axisvec(self.obj.FeaturePlacement)
|
|
d["shapetype"] = str(self.obj.ShapeType)
|
|
d["positioning"] = str(self.obj.Positioning)
|
|
return d
|
|
|
|
|
|
############################################################
|
|
# ViewProvider to the frames
|
|
############################################################
|
|
class ViewProviderFrame(object):
|
|
"""ViewProvider for the basic frame.
|
|
Uses the SOAxiscrosskit to create axises with constant length regardless
|
|
of zoom. Updates position when placement is changed.
|
|
"""
|
|
def __init__(self, vobj):
|
|
vobj.addProperty("App::PropertyFloat", "Scale")
|
|
vobj.Scale = 0.12
|
|
vobj.addProperty("App::PropertyFloat", "HeadSize")
|
|
vobj.HeadSize = 3.0
|
|
vobj.addProperty("App::PropertyFloat", "LineWidth")
|
|
vobj.LineWidth = 2.0
|
|
vobj.Proxy = self
|
|
|
|
def attach(self, vobj):
|
|
# We only have a shaded visual group
|
|
self.shaded = coin.SoGroup()
|
|
|
|
# Takes heavily from SoAxisCrosskit.h,
|
|
# and Toggle_DH_Frames by galou_breizh on the forums
|
|
self.vframe = coin.SoType.fromName("SoShapeScale").createInstance()
|
|
self.vframe.setPart("shape", coin.SoType.fromName("SoAxisCrossKit").createInstance())
|
|
self.vframe.scaleFactor.setValue(0.2)
|
|
ax = self.vframe.getPart("shape", 0)
|
|
cone = ax.getPart("xHead.shape", 0)
|
|
cone.bottomRadius.setValue(vobj.HeadSize)
|
|
cone = ax.getPart("yHead.shape", 0)
|
|
cone.bottomRadius.setValue(vobj.HeadSize)
|
|
cone = ax.getPart("zHead.shape", 0)
|
|
cone.bottomRadius.setValue(vobj.HeadSize)
|
|
lwstring = "lineWidth {0}".format(vobj.LineWidth)
|
|
ax.set("xAxis.appearance.drawStyle", lwstring)
|
|
ax.set("yAxis.appearance.drawStyle", lwstring)
|
|
ax.set("zAxis.appearance.drawStyle", lwstring)
|
|
ax.set("xAxis.pickStyle", "style SHAPE")
|
|
ax.set("yAxis.pickStyle", "style SHAPE")
|
|
ax.set("zAxis.pickStyle", "style SHAPE")
|
|
|
|
# Then remember to make it selectable in the viewer
|
|
selectionNode = coin.SoType.fromName("SoFCSelection").createInstance()
|
|
selectionNode.documentName.setValue(FreeCAD.ActiveDocument.Name)
|
|
selectionNode.objectName.setValue(vobj.Object.Name)
|
|
selectionNode.subElementName.setValue("Frame")
|
|
selectionNode.addChild(self.vframe)
|
|
|
|
# We would like to place it where we want
|
|
self.transform = coin.SoTransform()
|
|
self.shaded.addChild(self.transform)
|
|
self.shaded.addChild(self.vframe)
|
|
self.shaded.addChild(selectionNode)
|
|
vobj.addDisplayMode(self.shaded, "Shaded")
|
|
|
|
def updateData(self, fp, prop):
|
|
if prop == "Placement":
|
|
pl = fp.getPropertyByName("Placement")
|
|
self.transform.translation = (pl.Base.x,
|
|
pl.Base.y,
|
|
pl.Base.z)
|
|
self.transform.rotation = pl.Rotation.Q
|
|
|
|
def getDisplayModes(self, vobj):
|
|
modes = ["Shaded"]
|
|
return modes
|
|
|
|
def getDefaultDisplayMode(self):
|
|
return "Shaded"
|
|
|
|
def onChanged(self, vp, prop):
|
|
if prop == "Scale":
|
|
s = vp.getPropertyByName("Scale")
|
|
self.vframe.scaleFactor.setValue(float(s))
|
|
elif prop == "HeadSize":
|
|
hs = vp.getPropertyByName("HeadSize")
|
|
xcone = self.vframe.getPart("shape", 0).getPart("xHead.shape", 0)
|
|
xcone.bottomRadius.setValue(float(hs))
|
|
ycone = self.vframe.getPart("shape", 0).getPart("yHead.shape", 0)
|
|
ycone.bottomRadius.setValue(float(hs))
|
|
zcone = self.vframe.getPart("shape", 0).getPart("zHead.shape", 0)
|
|
zcone.bottomRadius.setValue(float(hs))
|
|
elif prop == "LineWidth":
|
|
lw = vp.getPropertyByName("LineWidth")
|
|
lwstring = "lineWidth {0}".format(lw)
|
|
ax = self.vframe.getPart("shape", 0)
|
|
ax.set("xAxis.appearance.drawStyle", lwstring)
|
|
ax.set("yAxis.appearance.drawStyle", lwstring)
|
|
ax.set("zAxis.appearance.drawStyle", lwstring)
|
|
|
|
def __getstate__(self):
|
|
return None
|
|
|
|
def __setstate__(self, state):
|
|
pass
|
|
|
|
|
|
class ViewProviderPartFrame(ViewProviderFrame):
|
|
"""View provider to the part frame."""
|
|
def updateData(self, fp, prop):
|
|
if prop == "Placement":
|
|
parentpl = fp.getPropertyByName("Part").Placement
|
|
localpl = fp.Placement
|
|
pl = parentpl.multiply(localpl)
|
|
self.transform.translation = (pl.Base.x,
|
|
pl.Base.y,
|
|
pl.Base.z)
|
|
self.transform.rotation = pl.Rotation.Q
|
|
|
|
|
|
class ViewProviderFeatureFrame(ViewProviderFrame):
|
|
"""View provider to the feature frames."""
|
|
def updateData(self, fp, prop):
|
|
if prop == "Placement":
|
|
parentpl = fp.getPropertyByName("Part").Placement
|
|
featurepl = fp.getPropertyByName("FeaturePlacement")
|
|
localpl = fp.Placement
|
|
pl = parentpl.multiply(featurepl.multiply(localpl))
|
|
self.transform.translation = (pl.Base.x,
|
|
pl.Base.y,
|
|
pl.Base.z)
|
|
self.transform.rotation = pl.Rotation.Q
|
|
|
|
|
|
###################################################################
|
|
# Base functions
|
|
###################################################################
|
|
|
|
def makeFrame(placement=FreeCAD.Placement()):
|
|
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "Frame")
|
|
Frame(obj)
|
|
if FreeCAD.GuiUp:
|
|
ViewProviderFrame(obj.ViewObject)
|
|
return obj
|
|
|
|
|
|
def makePartFrame(part):
|
|
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "PartFrame")
|
|
PartFrame(obj, part)
|
|
#if int(FreeCAD.Version()[1]) > 16:
|
|
# geo_feature_group = part.getParentGeoFeatureGroup()
|
|
# geo_feature_group.addObject(obj)
|
|
if FreeCAD.GuiUp:
|
|
ViewProviderPartFrame(obj.ViewObject)
|
|
return obj
|
|
|
|
|
|
def makeFeatureFrame(part, featurepl):
|
|
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",
|
|
"FeatureFrame")
|
|
FeatureFrame(obj, part, featurepl)
|
|
# If we're >0.16, add the feature frame to the assembly
|
|
#if int(FreeCAD.Version()[1]) > 16:
|
|
# geo_feature_group = part.getParentGeoFeatureGroup()
|
|
# geo_feature_group.addObject(obj)
|
|
if FreeCAD.GuiUp:
|
|
ViewProviderFeatureFrame(obj.ViewObject)
|
|
return obj
|
|
|
|
def makeSelectedPartFrames():
|
|
for part in FreeCADGui.Selection.getSelection():
|
|
if isinstance(part, Part.Feature):
|
|
pf = makePartFrame(part)
|
|
pf.Label = "Frame"+str(part.Label)
|
|
|
|
def makeAllPartFrames():
|
|
dc = FreeCAD.activeDocument()
|
|
for part in dc.Objects:
|
|
if isinstance(part, Part.Feature):
|
|
pf = makePartFrame(part)
|
|
pf.Label = "Frame"+str(part.Label)
|
|
|
|
|
|
def spawnFeatureFrameCreator():
|
|
ffpanel = FeatureFramePanel()
|
|
FreeCADGui.Control.showDialog(ffpanel)
|
|
|
|
|
|
|
|
###################################################################
|
|
# GUI Related
|
|
###################################################################
|
|
|
|
spawnClassCommand("BoMGeneration",
|
|
run_BoM_list,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "BoMList.svg")),
|
|
"MenuText": "Generate Bill of Materials",
|
|
"ToolTip": "Press the button to create big BoM"})
|
|
spawnClassCommand("FrameCommand",
|
|
makeFrame,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "frame.svg")),
|
|
"MenuText": "Make a free frame",
|
|
"ToolTip": "Make a freestanding reference frame."})
|
|
|
|
|
|
spawnClassCommand("SelectedPartFrameCommand",
|
|
makeSelectedPartFrames,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "partframe.svg")),
|
|
"MenuText": "selected parts frames",
|
|
"ToolTip": "Make selected parts frames."})
|
|
|
|
spawnClassCommand("AllPartFramesCommand",
|
|
makeAllPartFrames,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "allpartframes.svg")),
|
|
"MenuText": "All parts frames",
|
|
"ToolTip": "Make all parts frames."})
|
|
spawnClassCommand("FeatureFrameCommand",
|
|
spawnFeatureFrameCreator,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "featureframecreator.svg")),
|
|
"MenuText": "frame on selected primitive",
|
|
"ToolTip": "Create a frame on selected primitive."})
|
|
|
|
spawnClassCommand("Export_Entities",
|
|
export_coordinate_systems,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "BoMList.svg")),
|
|
"MenuText": "ExportLCS",
|
|
"ToolTip": "Export all the markups"})
|
|
|
|
spawnClassCommand("Validate_Project",
|
|
validate_project,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "")),
|
|
"MenuText": "Validate Project",
|
|
"ToolTip": "Check errors in project file"})
|
|
spawnClassCommand("Publish_Project",
|
|
process_file,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "publish.svg")),
|
|
"MenuText": "Publish Project",
|
|
"ToolTip": "Save and export project files"})
|
|
spawnClassCommand("Create Assembly Parameters",
|
|
create_assembly_parameters,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "galternatives.ico")),
|
|
"MenuText": "Create Assembly Parameters",
|
|
"ToolTip": "Create Assembly Parameters"})
|
|
spawnClassCommand("Create Fastener Set",
|
|
create_fastener_set,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "screw26.ico")),
|
|
"MenuText": "Create Fastener Set",
|
|
"ToolTip": "Create Fastener Set"})
|
|
spawnClassCommand("Compute Assembly Sequence",
|
|
structure_analysis,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "free-icon-software-5741483.png")),
|
|
"MenuText": "Compute Assembly Sequence",
|
|
"ToolTip": "Compute Assembly Sequence"})
|
|
spawnClassCommand("Create Assembly Sequence",
|
|
create_assembly_sequence,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "assembly.ico")),
|
|
"MenuText": "Create Assembly Sequence",
|
|
"ToolTip": "Create Assembly Sequence"})
|
|
spawnClassCommand("Export Assembly Settings",
|
|
export_assembly_settings,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "free-icon-export-724827.ico")),
|
|
"MenuText": "Export Assembly Settings",
|
|
"ToolTip": "Export Assembly Settings"})
|
|
# spawnClassCommand("Create Assembly Layers",
|
|
# asm_layers,
|
|
# {"Pixmap": str(os.path.join(ICONPATH, ".svg")),
|
|
# "MenuText": "Create Assembly Layers",
|
|
# "ToolTip": "Create Assembly Layers"})
|
|
spawnClassCommand("Create Structure Graph",
|
|
structure_graph,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "pngwing.com.ico")),
|
|
"MenuText": "Create Structure Graph",
|
|
"ToolTip": "Create Structure Graph"})
|
|
spawnClassCommand("Create Assembly Graph",
|
|
asm_graph,
|
|
{"Pixmap": str(os.path.join(ICONPATH, "iteration_3316689.ico")),
|
|
"MenuText": "Create Assembly Graph",
|
|
"ToolTip": "Create Assembly Graph"})
|
|
|
|
|
|
|
|
###################################################################
|
|
# GUI buttons
|
|
###################################################################
|
|
class FeatureFramePanel:
|
|
"""Spawn panel choices for a feature."""
|
|
def __init__(self):
|
|
selected = FreeCADGui.Selection.getSelectionEx()
|
|
# Check selection
|
|
if len(selected) == 1:
|
|
selected = selected[0]
|
|
self.selected = selected
|
|
else:
|
|
FreeCAD.Console.PrintError("Multipart selection not available.")
|
|
self.reject()
|
|
|
|
if not selected.HasSubObjects:
|
|
FreeCAD.Console.PrintError("Part selected not feature.")
|
|
self.reject()
|
|
elif not len(selected.SubObjects) == 1:
|
|
FreeCAD.Console.PrintError("Multifeature selection not available")
|
|
self.reject()
|
|
|
|
# Choices related to selection
|
|
so_desc = describeSubObject(selected.SubObjects[0])
|
|
self.so_desc = so_desc
|
|
shape_choices = {
|
|
"Vertex": [],
|
|
"Edge": ["PointOnEdge"],
|
|
"Face": ["PointOnSurface"]
|
|
}
|
|
prim_choices = {
|
|
"ArcOfCircle": ["Center"],
|
|
"ArcOfEllipse": ["Center"],
|
|
"ArcOfHyperBola": ["Center"],
|
|
"ArcOfParabola": ["Center"],
|
|
"BSplineCurve": ["Center"],
|
|
"BezierCurve": ["Center"],
|
|
"Circle": ["Center"],
|
|
"Ellipse": ["Center"],
|
|
"Hyperbola": ["Center"],
|
|
"Parabola": ["Center"],
|
|
"Line": [],
|
|
"BSplineSurface": ["Center"],
|
|
"BezierSurface": ["Center"],
|
|
"Cylinder": ["PointOnCenterline"],
|
|
"Plane": ["Center"],
|
|
"Sphere": ["Center"],
|
|
"Toroid": ["Center"],
|
|
"Cone": ["PointOnCenterline"]
|
|
}
|
|
self.choices = ["PickedPoint"]
|
|
self.choices = self.choices + shape_choices[so_desc[1]]
|
|
self.choices = self.choices + prim_choices[so_desc[0]]
|
|
# Setting up QT form
|
|
uiform_path = os.path.join(UIPATH, "FeatureFrameCreator.ui")
|
|
self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
|
|
self.form.ChoicesBox.addItems(self.choices)
|
|
self.form.PickedTypeLabel.setText(so_desc[0])
|
|
QtCore.QObject.connect(self.form.ChoicesBox,
|
|
QtCore.SIGNAL("currentIndexChanged(QString)"),
|
|
self.choiceChanged)
|
|
self.scenes = {}
|
|
for choice in self.choices:
|
|
sc = QtGui.QGraphicsScene()
|
|
icon = str(os.path.join(ICONPATH, choice+".svg"))
|
|
sc.addItem(QtSvg.QGraphicsSvgItem(icon))
|
|
self.scenes[choice] = sc
|
|
self.choiceChanged(self.form.ChoicesBox.currentText())
|
|
|
|
def choiceChanged(self, choice):
|
|
if choice in self.scenes.keys():
|
|
self.form.Preview.setScene(self.scenes[choice])
|
|
|
|
def accept(self):
|
|
sel_choice = self.form.ChoicesBox.currentText()
|
|
paneldict = {"PickedPoint": PickedPointPanel,
|
|
"PointOnEdge": PointOnEdgePanel,
|
|
"PointOnSurface": PointOnSurfacePanel,
|
|
"Center": CenterPanel,
|
|
"PointOnCenterline": PointOnCenterlinePanel}
|
|
pan = paneldict[sel_choice](self.selected, self.so_desc)
|
|
FreeCADGui.Control.closeDialog()
|
|
# The dialog is actually closed after the accept function has
|
|
# completed. So we need to use a delayed task to open the new dialog:
|
|
QtCore.QTimer.singleShot(0,
|
|
lambda: FreeCADGui.Control.showDialog(pan))
|
|
|
|
def reject(self):
|
|
FreeCADGui.Control.closeDialog()
|
|
|
|
|
|
class BaseFeaturePanel(object):
|
|
"""Base feature panel to be inherited from."""
|
|
def __init__(self, selected, so_desc):
|
|
# Handle selected and FF placement
|
|
self.selected = selected
|
|
self.so_desc = so_desc
|
|
# Connect offset to spinboxes
|
|
QtCore.QObject.connect(self.form.XBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.offsetChanged)
|
|
QtCore.QObject.connect(self.form.YBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.offsetChanged)
|
|
QtCore.QObject.connect(self.form.ZBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.offsetChanged)
|
|
QtCore.QObject.connect(self.form.RollBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.offsetChanged)
|
|
QtCore.QObject.connect(self.form.PitchBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.offsetChanged)
|
|
QtCore.QObject.connect(self.form.YawBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.offsetChanged)
|
|
QtCore.QObject.connect(self.form.ScaleBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.scaleChanged)
|
|
|
|
def createFrame(self):
|
|
self.fframe = makeFeatureFrame(self.selected.Object, self.local_ffpl)
|
|
self.fframe.PrimitiveType = self.so_desc[0]
|
|
self.fframe.ShapeType = self.so_desc[1]
|
|
ad = getPrimitiveInfo(self.so_desc[0],
|
|
self.selected.SubObjects[0])
|
|
self.fframe.Proxy.additional_data.update(ad)
|
|
|
|
def scaleChanged(self):
|
|
scale = self.form.ScaleBox.value()
|
|
self.fframe.ViewObject.Scale = scale
|
|
|
|
def offsetChanged(self):
|
|
disp = FreeCAD.Vector(self.form.XBox.value(),
|
|
self.form.YBox.value(),
|
|
self.form.ZBox.value())
|
|
rot = FreeCAD.Rotation(self.form.YawBox.value(),
|
|
self.form.PitchBox.value(),
|
|
self.form.RollBox.value())
|
|
offset = FreeCAD.Placement(disp, rot)
|
|
self.fframe.Placement = offset
|
|
|
|
def accept(self):
|
|
framelabel = self.form.FrameLabelField.toPlainText()
|
|
if not len(framelabel) == 0:
|
|
self.fframe.Label = framelabel
|
|
FreeCADGui.Control.closeDialog()
|
|
|
|
def reject(self):
|
|
FreeCAD.activeDocument().removeObject(self.fframe.Name)
|
|
FreeCADGui.Control.closeDialog()
|
|
|
|
|
|
class PickedPointPanel(BaseFeaturePanel):
|
|
"""Create a feature frame at the picked point."""
|
|
# Not very clever. It just places the frame with default rotation.
|
|
def __init__(self, selected, so_desc):
|
|
uiform_path = os.path.join(UIPATH, "FramePlacer.ui")
|
|
self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
|
|
BaseFeaturePanel.__init__(self, selected, so_desc)
|
|
parent_pl = selected.Object.Placement
|
|
abs_pl = FreeCAD.Placement(selected.PickedPoints[0],
|
|
FreeCAD.Rotation())
|
|
self.local_ffpl = parent_pl.inverse().multiply(abs_pl)
|
|
self.createFrame()
|
|
self.fframe.Positioning = "PickedPoint"
|
|
|
|
|
|
class PointOnEdgePanel(BaseFeaturePanel):
|
|
"""Create a feature frame on an edge."""
|
|
def __init__(self, selected, so_desc):
|
|
uiform_path = os.path.join(UIPATH, "FramePlacer.ui")
|
|
self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
|
|
# Enable the first parameter
|
|
self.form.VLabel.setEnabled(True)
|
|
self.form.VLabel.setVisible(True)
|
|
self.form.VLabel.setText("u")
|
|
self.form.VBox.setEnabled(True)
|
|
self.form.VBox.setVisible(True)
|
|
QtCore.QObject.connect(self.form.VBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.parameterChanged)
|
|
|
|
# Enable percentage or param selection
|
|
self.form.OptionsLabel.setEnabled(True)
|
|
self.form.OptionsLabel.setVisible(True)
|
|
self.form.OptionsLabel.setText("Arc param.")
|
|
self.form.OptionsBox.setEnabled(True)
|
|
self.form.OptionsBox.setVisible(True)
|
|
self.form.OptionsBox.addItems(["mm", "%"])
|
|
QtCore.QObject.connect(self.form.OptionsBox,
|
|
QtCore.SIGNAL("currentIndexChanged(QString)"),
|
|
self.choiceChanged)
|
|
BaseFeaturePanel.__init__(self, selected, so_desc)
|
|
|
|
# Place the frame wherever the values are atm
|
|
self.local_ffpl = FreeCAD.Placement()
|
|
self.createFrame()
|
|
self.fframe.Positioning = "PointOnEdge"
|
|
self.choiceChanged(self.form.OptionsBox.currentText())
|
|
self.parameterChanged()
|
|
|
|
def parameterChanged(self):
|
|
value = self.form.VBox.value()
|
|
if self.form.OptionsBox.currentText() == "%":
|
|
value = self.p2mm(value)
|
|
edge = self.selected.SubObjects[0]
|
|
point = edge.valueAt(value)
|
|
tangentdir = edge.tangentAt(value)
|
|
rot = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0),
|
|
tangentdir)
|
|
abs_ffpl = FreeCAD.Placement(point, rot)
|
|
parent_pl = self.selected.Object.Placement
|
|
self.local_ffpl = parent_pl.inverse().multiply(abs_ffpl)
|
|
self.fframe.FeaturePlacement = self.local_ffpl
|
|
# force recompute of placement?
|
|
self.fframe.Placement = self.fframe.Placement
|
|
|
|
def choiceChanged(self, choice):
|
|
value = self.form.VBox.value()
|
|
if choice == "mm":
|
|
value = self.p2mm(value)
|
|
self.form.VBox.setSuffix("mm")
|
|
parameter_range = self.selected.SubObjects[0].ParameterRange
|
|
self.form.VBox.setRange(*parameter_range)
|
|
self.form.VBox.setSingleStep(0.1)
|
|
elif choice == "%":
|
|
value = self.mm2p(value)
|
|
self.form.VBox.setSuffix("%")
|
|
self.form.VBox.setRange(0, 100.0)
|
|
self.form.VBox.setSingleStep(1.0)
|
|
self.form.VBox.setValue(value)
|
|
|
|
def p2mm(self, value):
|
|
parameter_range = self.selected.SubObjects[0].ParameterRange
|
|
delta = parameter_range[1] - parameter_range[0]
|
|
return 0.01*value*delta + parameter_range[0]
|
|
|
|
def mm2p(self, value):
|
|
parameter_range = self.selected.SubObjects[0].ParameterRange
|
|
delta = parameter_range[1] - parameter_range[0]
|
|
return 100.0*(value - parameter_range[0])/delta
|
|
|
|
|
|
class PointOnSurfacePanel(BaseFeaturePanel):
|
|
"""Create a feature on a surface."""
|
|
def __init__(self, selected, so_desc):
|
|
uiform_path = os.path.join(UIPATH, "FramePlacer.ui")
|
|
self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
|
|
# Enable both parameters
|
|
self.form.ULabel.setVisible(True)
|
|
self.form.VLabel.setVisible(True)
|
|
self.form.UBox.setVisible(True)
|
|
self.form.VBox.setVisible(True)
|
|
QtCore.QObject.connect(self.form.VBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.parameterChanged)
|
|
QtCore.QObject.connect(self.form.UBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.parameterChanged)
|
|
# Enable percentage or param selection
|
|
self.form.OptionsLabel.setEnabled(True)
|
|
self.form.OptionsLabel.setVisible(True)
|
|
self.form.OptionsLabel.setText("Surf. param.")
|
|
self.form.OptionsBox.setEnabled(True)
|
|
self.form.OptionsBox.setVisible(True)
|
|
self.form.OptionsBox.addItems(["mm", "%"])
|
|
QtCore.QObject.connect(self.form.OptionsBox,
|
|
QtCore.SIGNAL("currentIndexChanged(QString)"),
|
|
self.choiceChanged)
|
|
BaseFeaturePanel.__init__(self, selected, so_desc)
|
|
|
|
# Place the frame wherever the values are atm
|
|
self.local_ffpl = FreeCAD.Placement()
|
|
self.createFrame()
|
|
self.fframe.Positioning = "PointOnSurface"
|
|
self.choiceChanged(self.form.OptionsBox.currentText())
|
|
self.parameterChanged()
|
|
|
|
def parameterChanged(self):
|
|
value = (self.form.UBox.value(), self.form.VBox.value())
|
|
if self.form.OptionsBox.currentText() == "%":
|
|
value = self.p2mm(value)
|
|
face = self.selected.SubObjects[0]
|
|
point = face.valueAt(*value)
|
|
normaldir = face.normalAt(*value)
|
|
rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1),
|
|
normaldir)
|
|
abs_ffpl = FreeCAD.Placement(point, rotation)
|
|
parent_pl = self.selected.Object.Placement
|
|
self.local_ffpl = parent_pl.inverse().multiply(abs_ffpl)
|
|
self.fframe.FeaturePlacement = self.local_ffpl
|
|
# Force recompute of placement
|
|
self.fframe.Placement = self.fframe.Placement
|
|
|
|
def choiceChanged(self, choice):
|
|
value = (self.form.UBox.value(), self.form.VBox.value())
|
|
if choice == "mm":
|
|
value = self.p2mm(value)
|
|
parameter_range = self.selected.SubObjects[0].ParameterRange
|
|
self.form.UBox.setRange(parameter_range[0], parameter_range[1])
|
|
self.form.UBox.setSuffix("mm")
|
|
self.form.UBox.setSingleStep(0.1)
|
|
self.form.VBox.setRange(parameter_range[2], parameter_range[3])
|
|
self.form.VBox.setSuffix("mm")
|
|
self.form.VBox.setSingleStep(0.1)
|
|
elif choice == "%":
|
|
value = self.mm2p(value)
|
|
self.form.UBox.setRange(0.0, 100.0)
|
|
self.form.UBox.setSuffix("%")
|
|
self.form.VBox.setRange(0.0, 100.0)
|
|
self.form.UBox.setSingleStep(1.0)
|
|
self.form.VBox.setSuffix("%")
|
|
self.form.VBox.setSingleStep(1.0)
|
|
self.form.UBox.setValue(value[0])
|
|
self.form.VBox.setValue(value[1])
|
|
|
|
def p2mm(self, value):
|
|
parameter_range = self.selected.SubObjects[0].ParameterRange
|
|
delta = [parameter_range[1] - parameter_range[0],
|
|
parameter_range[3] - parameter_range[2]]
|
|
u = 0.01*value[0]*delta[0] + parameter_range[0]
|
|
v = 0.01*value[1]*delta[1] + parameter_range[2]
|
|
return (u, v)
|
|
|
|
def mm2p(self, value):
|
|
parameter_range = self.selected.SubObjects[0].ParameterRange
|
|
delta = [parameter_range[1] - parameter_range[0],
|
|
parameter_range[3] - parameter_range[2]]
|
|
u = 100.0*(value[0] - parameter_range[0])/delta[0]
|
|
v = 100.0*(value[1] - parameter_range[2])/delta[1]
|
|
return (u, v)
|
|
|
|
|
|
class CenterPanel(BaseFeaturePanel):
|
|
"""Create a feature frame on center."""
|
|
def __init__(self, selected, so_desc):
|
|
uiform_path = os.path.join(UIPATH, "FramePlacer.ui")
|
|
self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
|
|
BaseFeaturePanel.__init__(self, selected, so_desc)
|
|
edge_curve_list = ["ArcOfCircle",
|
|
"ArcOfEllipse",
|
|
"ArcOfHyperbola",
|
|
"ArcOfParabola",
|
|
"Circle",
|
|
"Ellipse",
|
|
"Hyperbola",
|
|
"Parabola"]
|
|
face_surf_list = ["Sphere",
|
|
"Toroid"]
|
|
if so_desc[0] in edge_curve_list:
|
|
edge = selected.SubObjects[0]
|
|
axis = edge.Curve.Axis
|
|
rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1),
|
|
axis)
|
|
center_point = edge.Curve.Center
|
|
elif so_desc[0] in face_surf_list:
|
|
face = selected.SubObjects[0]
|
|
axis = face.Surface.Axis
|
|
rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1),
|
|
axis)
|
|
center_point = face.Surface.Center
|
|
else:
|
|
rotation = FreeCAD.Rotation()
|
|
center_point = selected.SubObjects[0].CenterOfMass
|
|
parent_pl = selected.Object.Placement
|
|
abs_pl = FreeCAD.Placement(center_point,
|
|
rotation)
|
|
self.local_ffpl = parent_pl.inverse().multiply(abs_pl)
|
|
self.createFrame()
|
|
self.fframe.Positioning = "Center"
|
|
|
|
|
|
class PointOnCenterlinePanel(BaseFeaturePanel):
|
|
"""Create a point on centerline of primitive."""
|
|
def __init__(self, selected, so_desc):
|
|
uiform_path = os.path.join(UIPATH, "FramePlacer.ui")
|
|
self.form = FreeCADGui.PySideUic.loadUi(uiform_path)
|
|
BaseFeaturePanel.__init__(self, selected, so_desc)
|
|
# Enable the along line parameter
|
|
self.form.VLabel.setVisible(True)
|
|
self.form.VLabel.setText("u")
|
|
self.form.VBox.setVisible(True)
|
|
QtCore.QObject.connect(self.form.VBox,
|
|
QtCore.SIGNAL("valueChanged(double)"),
|
|
self.parameterChanged)
|
|
# Enable percentage of param selection
|
|
self.form.OptionsLabel.setVisible(True)
|
|
self.form.OptionsLabel.setText("Line param.")
|
|
self.form.OptionsBox.setVisible(True)
|
|
self.form.OptionsBox.addItems(["mm", "%"])
|
|
QtCore.QObject.connect(self.form.OptionsBox,
|
|
QtCore.SIGNAL("currentIndexChanged(QString)"),
|
|
self.choiceChanged)
|
|
# Place the frame wherever the values are atm
|
|
self.local_ffpl = FreeCAD.Placement()
|
|
self.createFrame()
|
|
self.fframe.Positioning = "PointOnCenterline"
|
|
self.parameterChanged()
|
|
|
|
def parameterChanged(self):
|
|
value = self.form.VBox.value()
|
|
if self.form.OptionsBox.currentText() == "%":
|
|
value = self.p2mm(value)
|
|
displacement_pl = FreeCAD.Placement(FreeCAD.Vector(0, 0, value),
|
|
FreeCAD.Rotation())
|
|
# Find the center
|
|
axis = self.selected.SubObjects[0].Surface.Axis
|
|
rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1),
|
|
axis)
|
|
center_point = self.selected.SubObjects[0].Surface.Center
|
|
center_pl = FreeCAD.Placement(center_point, rotation)
|
|
abs_ffpl = center_pl.multiply(displacement_pl)
|
|
parent_pl = self.selected.Object.Placement
|
|
self.local_ffpl = parent_pl.inverse().multiply(abs_ffpl)
|
|
self.fframe.FeaturePlacement = self.local_ffpl
|
|
# force recompute of placement
|
|
self.fframe.Placement = self.fframe.Placement
|
|
|
|
def choiceChanged(self, choice):
|
|
FreeCAD.Console.PrintMessage("choiceChanged\n")
|
|
value = self.form.VBox.value()
|
|
FreeCAD.Console.PrintMessage("preval:"+str(value)+"\n")
|
|
if choice == "mm":
|
|
value = self.p2mm(value)
|
|
self.form.VBox.setSuffix("mm")
|
|
parameter_range = self.selected.SubObjects[0].ParameterRange[2:]
|
|
self.form.VBox.setRange(*parameter_range)
|
|
self.form.VBox.setSingleStep(0.1)
|
|
elif choice == "%":
|
|
value = self.mm2p(value)
|
|
self.form.VBox.setSuffix("%")
|
|
self.form.VBox.setRange(0, 100)
|
|
self.form.VBox.setSingleStep(1.0)
|
|
self.form.VBox.setValue(value)
|
|
FreeCAD.Console.PrintMessage("postval:"+str(value)+"\n")
|
|
|
|
def p2mm(self, value):
|
|
parameter_range = self.selected.SubObjects[0].ParameterRange[2:]
|
|
delta = parameter_range[1] - parameter_range[0]
|
|
return 0.01*value*delta + parameter_range[0]
|
|
|
|
def mm2p(self, value):
|
|
parameter_range = self.selected.SubObjects[0].ParameterRange[2:]
|
|
delta = parameter_range[1] - parameter_range[0]
|
|
return 100.0*(value - parameter_range[0])/delta
|