Another approach to assembly solver (A2plus)

Discussion about the development of the Assembly workbench.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
HubertTO
Posts: 2
Joined: Sun Jun 06, 2021 7:53 am

Re: Another approach to assembly solver (A2plus)

Post by HubertTO »

HubertTO wrote: Sun Jun 06, 2021 8:24 am ...
and recomputation works fine. As couple of tests are already implemented to check the selection compatibility with every constraint, I am asking myself if it would be possible to implement such 'modifyer' in the WB? The Flow could be something like:
- have the subelement label written in the constraint dialogbox and have an 'update' button on the right to change it
- if update button is pushed, get the result of 'highlight both part related' (maybe having 2 colors for the surfaces to identify which of the 2 surfaces we are editing, or highlight in red or other color the edges again to help remember what are we editing), then popup a dialog box to confirm selection
- let the user select the new element; validate, visibility is brought back to how it was before editing, then internally check if the new selection is still compatible with the constraint and keep doing the process like during creation
...
Hello everyone, I got some time to write fex lines on this topic. Please find the code below. I still have some issue with the transparency and color recovery after surface modification. The principle is the following:
- you select a link created by a2plus
- the run the macro
- it should hide every object except the 2 ones concerned by the link, make half transparent the parts and highlignt the selected surfaces which are linked.
- a window should pop-up and let you choose another surface. There is a warning if you try to work on the wrong part.
- if you close the window, it does not save the change, otherwise there is a button to close and exit.

if you have any feedback feel free to share.

Have a good weekend,
Hubert

Code: Select all


# -*- coding: utf-8 -*-

# Macro Begin
+++++++++++++++++++++++++++++++++++++++++++++++++
import FreeCAD as App
import FreeCADGui as Gui
import PySide
from PySide import QtCore, QtGui
+++++++++++++++++++++++++++++++++++++++++++++++++

##########################################
# Global function
##########################################

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

##########################################
# Gui definition
##########################################

class GuiSelectSubElemWindows(PySide.QtGui.QWidget):
    # automagically called when the window is created
    closed = QtCore.Signal()
    def __init__(self,app=None):
        super(GuiSelectSubElemWindows, self).__init__()
        self.setWindowFlags(PySide.QtCore.Qt.WindowStaysOnTopHint)
        self.setWindowTitle(_translate(
            "MainWindow", "SelectionWindows", None))
        self.app = app

        # UI
        self.setGeometry(14,200, 40, 50)
        grid = PySide.QtGui.QGridLayout()
        self.setLayout(grid)
        self.setLayout(grid)   
        self.setObjectName(_fromUtf8("SelectionWindows"))

        # Labels
        self.label_0 = QtGui.QLabel(self)
        self.label_0.setText("Constraint name:")
        self.UpdateConstraintName()
        self.label_0.setToolTip("Current constraint evaluated")
        self.label_0.adjustSize()
    
        self.label_1 = QtGui.QLabel(self)
        self.label_1.setText("Elem 1:")
        self.UpdateSubElem1Name()
        self.label_1.setToolTip("First Element of the constraint")
        self.label_1.adjustSize()
       
        self.label_2 = QtGui.QLabel(self)
        self.label_2.setText("Elem 2:")
        self.UpdateSubElem2Name()
        self.label_2.setToolTip("Second Element of the constraint")
        self.label_2.adjustSize()

        self.label_3 = QtGui.QLabel(self)
        self.label_3.setText("Status: ")
        self.label_3.adjustSize()
            
        # Update on request
        self.PushButton_1 = QtGui.QPushButton(self)
        self.PushButton_1.Label = 'Change_SubElem2'
        self.PushButton_1.setText("Change E1")
        self.PushButton_1.clicked.connect(self.UpdateSubElem1)
        self.PushButton_1.setToolTip("Validate First Sub Element")
        self.PushButton_1.adjustSize()

        self.PushButton_2 = QtGui.QPushButton(self)
        self.PushButton_2.Label = 'Change_SubElem2'
        self.PushButton_2.setText("Change E2")
        self.PushButton_2.clicked.connect(self.UpdateSubElem2)
        self.PushButton_2.setToolTip("Validate Second Sub Element")
        self.PushButton_2.adjustSize()


        self.PushButton_3 = QtGui.QPushButton(self)
        self.PushButton_3.Label = 'Save_and_Close'
        self.PushButton_3.setText("Save changes and Close")
        self.PushButton_3.clicked.connect(self.SaveModificationAndClose)
        self.PushButton_3.setToolTip("Save modifications and close macro")
        self.PushButton_3.adjustSize()

        grid.addWidget(self.label_0,0,0,1,2)
        grid.addWidget(self.label_1,1,0,1,1)        
        grid.addWidget(self.label_2,2,0,1,1)
        grid.addWidget(self.PushButton_1,1,1,1,1)
        grid.addWidget(self.PushButton_2,2,1,1,1)
        grid.addWidget(self.label_3,3,0,1,2)
        grid.addWidget(self.PushButton_3,4,0,1,2)

        self.show()

    def UpdateConstraintName(self):
        self.label_0.setText("Constraint name: %s" % self.app.a2pConstraintName)

    def UpdateSubElem1(self):
        self.app.UpdateSubElem1() # ??????
        self.UpdateSubElem1Name()
        self.label_3.setText('Status: %s' % self.app.status)

    def UpdateSubElem1Name(self):
        self.label_1.setText("Sub-Element 1: %s" % self.app.a2pSubElem1)

    def UpdateSubElem2(self):
        self.app.UpdateSubElem2() # ?????
        self.UpdateSubElem2Name()
        self.label_3.setText('Status: %s' % self.app.status)
        
    def refreshScreen(self):
        Gui.Selection.clearSelection()
        for obj in App.ActiveDocument.Objects:
            if obj.ViewObject.isVisible():
                Gui.Selection.addSelection(obj)
        Gui.Selection.clearSelection()

    def UpdateSubElem2Name(self):
        self.label_2.setText("Sub-Element 2: %s" % self.app.a2pSubElem2)

    def SaveModificationAndClose(self):
        self.app.Selection.SubElement1 = self.app.a2pSubElem1
        self.app.Selection.SubElement2 = self.app.a2pSubElem2
        self.close()

    def closeEvent(self, event):
        self.app.resetVisibility()
        self.refreshScreen()
        self.closed.emit()

##########################################
# Model definition
##########################################

class getRelatedConstraintObject():
    def __init__(self,Selection=None):
        self.ObjInView = []
        self.OriginalVisibilities = []
        self.OriginalTransparencies = []
        self.OriginalSubElemColors = [None,None]
        self.ProListCheck = ['Object1', 'Object2', 'Proxy', 'SubElement1', 'SubElement2', 'Suppressed', 'Toponame1', 'Toponame2', 'Type', 'Visibility']
        self.a2pObject1 = None
        self.a2pObject2= None
        self.a2pSubElem1 = None
        self.a2pSubElem2 = None
        self.a2pConstraintName= None
        self.Selection = Selection
        self.isSelectionValid = False
        self.status = ''
        self.checkSelection()
        if self.isSelectionValid:
            self.getObjectConstraintDetails()
            self.getObjInView()
            self.getOriginalVisibilities()
   
    def checkSelection(self):
        if self.Selection.TypeId == 'App::FeaturePython' and  all([item in self.Selection.PropertiesList for item in self.ProListCheck]):
            self.isSelectionValid = True
  
    def getObjInView(self):
        self.ObjInView.clear()
        for obj in App.ActiveDocument.Objects:
            try:
                obj.getPropertyByName("Shape")
                self.ObjInView.append(obj)
            except AttributeError:
                pass

    def getOriginalVisibilities(self):
        self.OriginalVisibilities.clear()
        for obj in self.ObjInView:
            vis =obj.ViewObject.Visibility
            transp = obj.ViewObject.Transparency
            self.OriginalVisibilities.append(vis)
            self.OriginalTransparencies.append(transp)
            if vis:
                if obj.TypeId != str("Drawing::FeatureViewPython") and not self.isRelatedObject(obj):
                    obj.ViewObject.Visibility = False
                elif self.isRelatedObject(obj):
                    pass
        self.setRelatedFacesHighlighted()
        

    def setRelatedFacesHighlighted(self):
        # Sub Elem 1
        self.OriginalSubElemColors[0] = self.setColorOnFace(self.a2pObject1, self.Selection.SubElement1)
        # Sub Elem 2
        self.OriginalSubElemColors[1] = self.setColorOnFace(self.a2pObject2, self.Selection.SubElement2)
      
    def getObjectConstraintDetails(self):
        self.a2pObject1 = self.Selection.Object1
        self.a2pObject2 = self.Selection.Object2
        self.a2pSubElem1 = self.Selection.SubElement1
        self.a2pSubElem2 = self.Selection.SubElement2
        self.a2pConstraintName = self.Selection.Label

    def UpdateSubElem1(self):
        try:
            SelectedObj = Gui.Selection.getSelectionEx()[0]
            SelectedObjName = SelectedObj.ObjectName
            if SelectedObjName == self.a2pObject1:
                self.a2pSubElem1 = SelectedObj.SubElementNames[0]
                SelectedObj.Object.ViewObject.DiffuseColor = self.OriginalSubElemColors[0]
                _ = self.setColorOnFace(self.a2pObject1, self.a2pSubElem1)
                self.status = 'SubElem1 selected successfully'
            else:
                self.status = 'Wrong part selected'
        except:
            pass
        
    def UpdateSubElem2(self):
        try:
            SelectedObj = Gui.Selection.getSelectionEx()[0]
            SelectedObjName = SelectedObj.ObjectName
            if SelectedObjName == self.a2pObject2:
                self.a2pSubElem2 = SelectedObj.SubElementNames[0]
                SelectedObj.Object.ViewObject.DiffuseColor = self.OriginalSubElemColors[1]
                _ = self.setColorOnFace(self.a2pObject2, self.a2pSubElem2)
                self.status = 'SubElem2 selected successfully'
            else:
                self.status = 'Wrong part selected'
        except:
            pass
        
    def isRelatedObject(self,CADObj):
        return CADObj.Name == self.a2pObject1 or CADObj.Name == self.a2pObject2
    
    def resetVisibility(self):
        obj =App.activeDocument().getObject(self.a2pObject1)
        obj.ViewObject.DiffuseColor = self.OriginalSubElemColors[0]
        obj =App.activeDocument().getObject(self.a2pObject2)
        obj.ViewObject.DiffuseColor = self.OriginalSubElemColors[1]
        for obj,vis,transp in zip(self.ObjInView,self.OriginalVisibilities,self.OriginalTransparencies):
            obj.ViewObject.Visibility = vis
            obj.ViewObject.Transparency = 1 #  workaroud for the display bug with reseting transparency
            obj.ViewObject.Transparency =  transp
        
        
    def ChangeTransparency(self,colors,value):
        colors = [(s[0],s[1],s[2],value) for s in colors]
        return colors

    def setColorOnFace(self, MyObject, MyFace,MyColor=None):
        obj =App.activeDocument().getObject(MyObject)
        NFaces = len(obj.Shape.Faces)
        if len(obj.ViewObject.DiffuseColor) == 1:
           colorlist = obj.ViewObject.DiffuseColor*NFaces
        else:
            colorlist = obj.ViewObject.DiffuseColor
        colorlist = self.ChangeTransparency(colorlist,0.7)
        SubElemIndex = int(MyFace[4:])-1
        if MyColor is None:
            c1, c2, c3, _ = colorlist[SubElemIndex]
            MyColor = (float((1.-c1)), float((1.-c2)), float((1.-c3)), 0.)
            # MyColor = (1.,0.,1.,0.)
        colorlist[SubElemIndex] = MyColor # (0.,1.,0.,0.)
        output = obj.ViewObject.DiffuseColor
        obj.ViewObject.DiffuseColor = colorlist
        return output
     
for selection in Gui.Selection.getSelection():
    app = getRelatedConstraintObject(selection) # first in selection, no test)
    if app.isSelectionValid:
        view = GuiSelectSubElemWindows(app)
        loop = QtCore.QEventLoop()
        view.closed.connect(loop.quit)
        loop.exec_()


dan-miel
Posts: 391
Joined: Thu Sep 13, 2018 12:29 am
Location: Spokane WA. USA

Re: Another approach to assembly solver (A2plus)

Post by dan-miel »

HubertTO wrote: Sat Jun 19, 2021 8:04 am and recomputation works fine. As couple of tests are already implemented to check the selection compatibility with every constraint, I am asking myself if it would be possible to implement such 'modifyer' in the WB? The Flow could be something like:
- have the subelement label written in the constraint dialogbox and have an 'update' button on the right to change it
- if update button is pushed, get the result of 'highlight both part related' (maybe having 2 colors for the surfaces to identify which of the 2 surfaces we are editing, or highlight in red or other color the edges again to help remember what are we editing), then popup a dialog box to confirm selection
- let the user select the new element; validate, visibility is brought back to how it was before editing, then internally check if the new selection is still compatible with the constraint and keep doing the process like during creation
Hi HubertTO. It sounds like you want to view a constraint and then move the attachment surface to another surface if it is not the correct one. I wrote this workbench a while back but haven’t used FreeCAD since I finished building my house. Most of the workbench is out dated but the constraint viewer still seems to work. You can select a part and the constraints show in the dialog box. You can also select the constraints from the tree. You can then change the attachment surface.
I have attached some directions and an assembly that breaks the constraints when the assembly is updated.
Dan.
Attachments
A2plusmore.zip
(872.14 KiB) Downloaded 211 times
Constraint Viewer instructions.pdf
(64.85 KiB) Downloaded 216 times
Sample for updated part.zip
(141.63 KiB) Downloaded 204 times
dan-miel
Posts: 391
Joined: Thu Sep 13, 2018 12:29 am
Location: Spokane WA. USA

Re: Another approach to assembly solver (A2plus)

Post by dan-miel »

dan-miel wrote: Sat Jun 26, 2021 11:41 pm I wrote this workbench a while back but haven’t used FreeCAD since I finished building my house.
I took a fast look at the part if this workbench that should find broken constraints. A2plus has been rewritten since I wrote this. The broken constraint finder is working on the one file I tried it on so I’m putting it up here without checking it very well. When the program is ran, it finds broken constraints in the a2plus assembly and creates a list of them. You can then open the constraint viewer to see what is broken and perhaps fix them.
I hope someone has success with one of these programs.
Dan
Attachments
A2plusmore Ver12.zip
(970 KiB) Downloaded 231 times
Directions for finding broken constraints.pdf
(48.84 KiB) Downloaded 224 times
Assy with broken constraints.zip
(8.71 KiB) Downloaded 178 times
dan-miel
Posts: 391
Joined: Thu Sep 13, 2018 12:29 am
Location: Spokane WA. USA

Re: Another approach to assembly solver (A2plus)

Post by dan-miel »

dan-miel wrote: Mon Jul 05, 2021 12:24 am
dan-miel wrote: Sat Jun 26, 2021 11:41 pm I wrote this workbench a while back but haven’t used FreeCAD since I finished building my house.
You can then open the constraint viewer to see what is broken and perhaps fix them.
I hope someone has success with one of these programs.
Dan
I see that this workbench. "A2plusmore", from the previous message has been downloaded over 50 times. Has anyone had success using it or have a suggestion to make it more useful?
Dan
MRx
Posts: 319
Joined: Wed Jul 08, 2020 5:59 am
Location: Tainan / Taiwan

Re: Another approach to assembly solver (A2plus)

Post by MRx »

Hi,

when clicking onto edit a part imported by a2plus, it will open the part separately.
So far so good.

My object is a little bit bigger, now when clicking it it will stall the popup menu on the screen for a minute.
Could you refactor this option to close the popup menu first and then load the object into freecad?
User avatar
uwestoehr
Veteran
Posts: 4961
Joined: Sun Jan 27, 2019 3:21 am
Location: Germany
Contact:

Re: Another approach to assembly solver (A2plus)

Post by uwestoehr »

dan-miel wrote: Mon Jul 05, 2021 12:24 am You can then open the constraint viewer to see what is broken and perhaps fix them.
Hello Dan, many thanks for your great contribution!
In order that users can easily install your addon, can you please make your addon available via GitHub so that one can install it using the Addon manager?
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: Another approach to assembly solver (A2plus)

Post by Kunda1 »

Wouldn't it be better as a PR to A2+ instead?
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
dan-miel
Posts: 391
Joined: Thu Sep 13, 2018 12:29 am
Location: Spokane WA. USA

Re: Another approach to assembly solver (A2plus)

Post by dan-miel »

Kunda1 wrote: Sun Mar 13, 2022 4:22 pm Wouldn't it be better as a PR to A2+ instead?
To all:
This post is about a program I called Constraint viewer. It is the program above, A2plusmore, which I stripped down to two icons, the viewer and updater, which are really diagnostic tools for constraints. Many of the other functions are broken since the last major update of A2plus.

Uwestoehr and Kunda1:
I put the A2Plusmore program above to see if I could get some feedback on whether or not I should spend any time on separating out the viewer program. Why there are over one hundred downloads, I don’t know. I’m still not sure if the program runs on any machine but mine, but I separated it anyway. Is Klaus is working on A2plus at the moment?
On another note, as a newbie I could use a link on how to submit it as an addon or PR. Plus it needs a lot of cleanup on the code and how to documentation.
Dan
Attachments
A2pConstraintViewer ver2.zip
(933.86 KiB) Downloaded 112 times
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: Another approach to assembly solver (A2plus)

Post by Kunda1 »

dan-miel wrote: Mon Mar 14, 2022 12:24 pm Uwestoehr and Kunda1:
I put the A2Plusmore program above to see if I could get some feedback on whether or not I should spend any time on separating out the viewer program. Why there are over one hundred downloads, I don’t know. I’m still not sure if the program runs on any machine but mine, but I separated it anyway. Is Klaus is working on A2plus at the moment?
On another note, as a newbie I could use a link on how to submit it as an addon or PR. Plus it needs a lot of cleanup on the code and how to documentation.
Open a new thread to discuss this so this thread doesn't get derailed is the first place to start. Make a clear ask on said thread and we'll get the ball rolling on this ;)
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
dan-miel
Posts: 391
Joined: Thu Sep 13, 2018 12:29 am
Location: Spokane WA. USA

Re: Another approach to assembly solver (A2plus)

Post by dan-miel »

Kunda1 wrote: Mon Mar 14, 2022 3:13 pm Open a new thread to discuss this so this thread doesn't get derailed is the first place to start. Make a clear ask on said thread and we'll get the ball rolling on this
Done. I renamed ConstraintViewer to ConstraintDiagnostics but it is the same workbench.
Thanks.
Dan
Post Reply