REVISIT: Help with computing shared face.

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: Help with computing shared face.

Post by keithsloan52 »

edwilliams16 wrote: Wed Jun 15, 2022 9:57 pm
keithsloan52 wrote: Wed Jun 15, 2022 6:56 am Thanks - now understand.
You should check out the isSame() and isPartner() methods for the face pairs. Both return the correct matching faces in your test file.

https://forum.freecadweb.org/viewtopic. ... 57#p110957
Not sure what I am doing wrong but

Code: Select all

for face0 in shape0.Faces :
        for face1 in shape1.Faces :
            if face0.isPartner(face1):
            #if face0.isSame(face1):
               distance, points, info = face0.distToShape(face1)
               print(f'Distance {distance} points {points} info {info}')
               if distance == 0.0:
                  print(f'Distance {distance} points {points} info {info}')
Is not finding anything
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: Help with computing shared face.

Post by onekk »

could not be that matching for 0.0 is not returning a match due to the precision.

Some caveat about matching for equality for floating point number are around, for coders, but I admit that I sometime use this writing.

usually the suggestion is:

Code: Select all

if number <= epsilon or number >= epsilon * -1:
or similar if I have made a mess with signs, is advised with epsilon equal to the precision needed.

In some corner case it has avoided some head banging, even a simple print() statement sometimes is revealing what is going wrong.

see maybe "IEEE floating point error" if you don't know what I'm speaking about.

Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: Help with computing shared face.

Post by keithsloan52 »

onekk wrote: Thu Jun 16, 2022 1:39 pm could not be that matching for 0.0 is not returning a match due to the precision.

Some caveat about matching for equality for floating point number are around, for coders, but I admit that I sometime use this writing.

usually the suggestion is:

Code: Select all

if number <= epsilon or number >= epsilon * -1:
or similar if I have made a mess with signs, is advised with epsilon equal to the precision needed.

In some corner case it has avoided some head banging, even a simple print() statement sometimes is revealing what is going wrong.

see maybe "IEEE floating point error" if you don't know what I'm speaking about.

Regards

Carlo D.

Code: Select all

               print(f'Distance {distance} points {points} info {info}')
               if distance == 0.0:
                  print(f'Distance {distance} points {points} info {info}')
Do you mean this test for 0.0?

There is no print out before or after so it is the isPartner ( or isSame if I switch around the comments )
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: Help with computing shared face.

Post by onekk »

keithsloan52 wrote: Thu Jun 16, 2022 2:58 pm

Code: Select all

               print(f'Distance {distance} points {points} info {info}')
               if distance == 0.0:
                  print(f'Distance {distance} points {points} info {info}')
Do you mean this test for 0.0?

There is no print out before or after so it is the isPartner ( or isSame if I switch around the comments )
EDIT:

Sorry probably my post was not clear, a part of the IEEE precision, when I spoke about print statement was also to show the match of the isPartner() and so on.

So answer to your question above is:

Yes and No.

Yes if the problem is that a 0.0 is printed but not matched by the expression as Python sometimes will make some rounding when "showing data", if I don0t go wrong there is some truncating about a precise number of decimals if no format is specified.

Sorry for not being more precise but some time ago I have banged my head prior to find this info around in python documentation, but now having not noted down were it was, my memory is not very reliable. :oops:

No if the problem is that isPartner() or isSame() is not returning True. Are you aware of this:

*Author:* @wmayer:
*From:*: https://forum.freecadweb.org/viewtopic. ... 36#p586336

I have noted some time ago an noted down as it seems interesting.

Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Help with computing shared face.

Post by edwilliams16 »

I think I mislead myself on the applicability of isEqual() isSame() isPartner() methods. I think they apply to shared geometry in a single object.

I was under the impression that object names were required to be unique. https://wiki.freecadweb.org/Object_name
Both your "GDML_Box_det" objects claim to have the same "Valid Internal Name". At the very least this makes them difficult to differentiate in python references.

I tried

Code: Select all

Gui.Selection.clearSelection()
faces0 =[]
faces1 = []
for i in range(1,7):
	 Gui.Selection.addSelection('','World','Detector.GDMLBox_det.Face'+str(i))
	 sel = Gui.Selection.getSelectionEx('',0)
	 faces0.append(sel[0].SubObjects[0])
	 Gui.Selection.clearSelection()
	 Gui.Selection.addSelection('','World','Detector001.GDMLBox_det.Face'+str(i))
	 sel = Gui.Selection.getSelectionEx('',0)
	 faces1.append(sel[0].SubObjects[0])
	 Gui.Selection.clearSelection()

for i0, face0 in enumerate(faces0):
	for i1, face1 in enumerate(faces1):
		print(f'i0 {i0} i1 {i1} {face0.isPartner(face1)}')
since the Gui differentiates the objects - but it only identifies the corresponding faces ignoring the lateral shift.
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: Help with computing shared face.

Post by keithsloan52 »

edwilliams16 wrote: Thu Jun 16, 2022 10:27 pm I think I mislead myself on the applicability of isEqual() isSame() isPartner() methods. I think they apply to shared geometry in a single object.

I was under the impression that object names were required to be unique. https://wiki.freecadweb.org/Object_name
Both your "GDML_Box_det" objects claim to have the same "Valid Internal Name". At the very least this makes them difficult to differentiate in python references.

I tried

Code: Select all

Gui.Selection.clearSelection()
faces0 =[]
faces1 = []
for i in range(1,7):
	 Gui.Selection.addSelection('','World','Detector.GDMLBox_det.Face'+str(i))
	 sel = Gui.Selection.getSelectionEx('',0)
	 faces0.append(sel[0].SubObjects[0])
	 Gui.Selection.clearSelection()
	 Gui.Selection.addSelection('','World','Detector001.GDMLBox_det.Face'+str(i))
	 sel = Gui.Selection.getSelectionEx('',0)
	 faces1.append(sel[0].SubObjects[0])
	 Gui.Selection.clearSelection()

for i0, face0 in enumerate(faces0):
	for i1, face1 in enumerate(faces1):
		print(f'i0 {i0} i1 {i1} {face0.isPartner(face1)}')
since the Gui differentiates the objects - but it only identifies the corresponding faces ignoring the lateral shift.
I see your point, going to have to get my head around this.

Detector is a Part that contains GDMLBox_det.

Detector_001 is a Link Part and the Link is to Detector which contains GDMLBox_det, so yes the GDMLBox_det is the same object.

Now Detector_001 has a different Placement from Detector i.e. 2000,0,0 as opposed to 0,0,0 and hence the box is displayed with this Placement.

Now what I am after is what is the Common Face of the displayed boxes.

Now I thought that creating a new copy of the Shape(TopoShape) in Detector_001 and performing a translate would give me a suitable Shapes (TopoShapes) to compare,
hence

Code: Select all

def adjustShape(part):
    print("Adjust Shape")
    print(part.Name)
    print(part.Label)
    print(part.OutList)
    print(f'Before Placement Base {part.Placement.Base}')
    beforeBase = part.Placement.Base
    if hasattr(part,'LinkedObject'):
       print(f'Linked Object {part.LinkedObject}')
       part = part.getLinkedObject()
       print(part.Name)
       print(part.Label)
       print(part.OutList)
    obj = getGDMLObject(part.OutList)
    obj.recompute()
    # Shape is immutable so have to copy
    shape = getShape(obj)
    print(f'Shape Valid {shape.isValid()}')
    print(dir(shape))
    #return shape
    print(f'After Placement Base {part.Placement.Base}')
    #return translate(part, part.Placement.Base)
    return shape.translate(beforeBase)
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Help with computing shared face.

Post by edwilliams16 »

Couldn't find a definition of:

Code: Select all

obj = getGDMLObject(part.OutList)
Making copies in the global coordinate system to compare, seems like a good way to start.
I tried using Part.Common to see if would return a common face - but possibly due to numerics, it didn't.
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: Help with computing shared face.

Post by keithsloan52 »

edwilliams16 wrote: Fri Jun 17, 2022 9:44 pm Couldn't find a definition of:

Code: Select all

obj = getGDMLObject(part.OutList)
Making copies in the global coordinate system to compare, seems like a good way to start.
I tried using Part.Common to see if would return a common face - but possibly due to numerics, it didn't.

Code: Select all

def getGDMLObject(list):
    print('getGDMLObject')
    print(list[0].TypeId)
    if list[0].TypeId == "App::Origin":
       print(f'Vertex {list[1].Shape.Vertexes}')
       return list[1]
    else:
       print(f'Vertex {list[0].Shape.Vertexes}')
       return list[0]
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Help with computing shared face.

Post by edwilliams16 »

Code: Select all

def shapeGlobal(part):
	placement = part.Placement # in world coords
	if hasattr(part,'LinkedObject'):
		part = part.getLinkedObject()
	gdobjs = [obj for obj in part.OutList if 'GDML' in obj.Name]
	gdobj = gdobjs[0] # only one we assume
	return gdobj.Shape.copy().transformShape(placement.Matrix)

def drawFace(vs):
    wire = Part.makePolygon(vs)
    face = Part.Face(wire)
    return face

doc = App.ActiveDocument
obj = doc.getObject("Detector")
shp1 = shapeGlobal(obj)
obj = doc.getObject("Detector001")
shp2 = shapeGlobal(obj)
v1 = [v.Point for v in shp1.Vertexes]
v2 = [v.Point for v in shp2.Vertexes]
vcommon = [v for v in v1 if any([w.isEqual(v, 1e-7) for w in v2])]
if len(vcommon) ==4:
	Part.show(drawFace(vcommon))
Here's a hack, just comparing vertices.
realthunder
Veteran
Posts: 2190
Joined: Tue Jan 03, 2017 10:55 am

Re: Help with computing shared face.

Post by realthunder »

In current upstream, comparing vertex is also what I would recommend you to use. Maybe also compare the bound box center of the face.

In my branch, there is a new API TopoShape.searchSubShape() that will make the task a lot simpler. It performs not only vertex comparing under the hood, but also geometry comparing for both edge and face.

To search the sub shape, and find out the face index, use the following code

Code: Select all

# Get transformed shape in global coordinate space
shape1 = App.ActiveDocument.World.getSubObject('Detector.GDMLBox_det.')
shape2 = App.ActiveDocument.World.getSubObject('Detector001.GDMLBox_det.')
for i, face in enumerate(shape1.Faces):
	# searchSubShape() returns a list of tuple(name, subshape)
	found = shape2.searchSubShape(face, needName=True)
	if found:
		print(f'Face{i+1} -> {found}')
In case any one requesting for a PR, please note that the code is not trivial, and will be part of the upcoming topo naming branch.
Try Assembly3 with my custom build of FreeCAD at here.
And if you'd like to show your support, you can donate through patreon, liberapay, or paypal
Post Reply