Geometric same solids

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
HalpPlease
Posts: 9
Joined: Tue Mar 14, 2023 10:26 am

Geometric same solids

Post by HalpPlease »

Since from my understanding, isSame, isPartner and isEqual are kinda useless to see if this is the case,
what else can I use to filter exactly the same solids ?

Basic example STP in attach.
I dont care where they are, i just want to filter identicals.

If there is no such thing, i can make a custom made filter, but im not sure on what i should compare.
Area + Volume + faces + edges ? Or more ?

Thanks in advance !
Attachments
TestDubbel.stp
(13.71 KiB) Downloaded 19 times
User avatar
onekk
Veteran
Posts: 6205
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: Geometric same solids

Post by onekk »

HalpPlease wrote: Tue Mar 14, 2023 10:37 am ...
Probably same should be explained.

Same for Python is an object that has the same "memory position", so it depends:

See maybe if these post that are dealing with the use of isSame.... will help you on some extent:

viewtopic.php?p=586336#p586336

viewtopic.php?p=614776#p614776

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/
HalpPlease
Posts: 9
Joined: Tue Mar 14, 2023 10:26 am

Re: Geometric same solids

Post by HalpPlease »

Yeah...
That didnt help me really, thanks for the effort anyway.
User avatar
onekk
Veteran
Posts: 6205
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: Geometric same solids

Post by onekk »

HalpPlease wrote: Tue Mar 14, 2023 1:01 pm Yeah...
That didnt help me really, thanks for the effort anyway.
Stp file is not a native FreeCAD format, so you must transform it in a FreeCAD solid.

So what you are trying to confront?

you posted only one stp file, so there are nothing to confront with.

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/
HalpPlease
Posts: 9
Joined: Tue Mar 14, 2023 10:26 am

Re: Geometric same solids

Post by HalpPlease »

Thanks in advance for ur effort.

The plan is to upload a stp file in a webapp, let a python script extract all
the usefull data in it, and make a sort of assembly of it.
Detecting bends, holes, ect...

At the moment I want to filter out the solids that are exactly the same.
The follow up question would be how would i detect if they are the same, but mirrored.


The current python script:

import sys
import FreeCAD
import Part
import Import
import json

class Test:
def __init__(self, shape,ShapeType, valid, area, volume, length, num_faces):
self.shape = shape
self.ShapeType = ShapeType
self.valid = valid
self.area = area
self.volume = volume
self.length = length
self.num_faces = num_faces
self.face_data = []

for face in self.shape.Faces:

#print(dir(face.Placement.Base))
face_dict = {
"valid": face.isValid(),
# "WhatBeInIt": dir(face),
"area": face.Area,
"volume":face.Volume,
# "isSame": face.isSame(),
# "center_of_mass": face.CenterOfMass,
# "normal": face.normalAt(0.5, 0.5),
# "isClosed": face.isClosed, An error occurred: Object of type 'builtin_function_or_method' is not JSON serializable
# "hashCode": face.hashCode, An error occurred: Object of type 'builtin_function_or_method' is not JSON serializable
"lengte": face.Length,
'X': face.Placement.Base.x,
"Y": face.Placement.Base.y,
"Z": face.Placement.Base.z,
"faces": len(face.Faces),
"num_wires": len(face.Wires),
"num_edges": len(face.Edges),
}
self.face_data.append(face_dict)


try:
data = Part.Shape()
# data.read("./assets/test/Weldment02-Staander_Midden.stp")
data.read("./assets/test/TestDubbel.stp")
# Read in the STP file and extract the desired lines of text
# with open("./assets/test/Weldment02-Staander_Midden.stp", "r") as f:
with open("./assets/test/TestDubbel.stp", "r") as f:
stp_text = f.readlines()

# Loop through each line in stp_text

product_lines = []
found_occurrence = False
occurrence_lines = []
for line in stp_text:
if "=NEXT_ASSEMBLY_USAGE_OCCURRENCE('" in line:
found_occurrence = True
occurrence_lines.append(line)
elif found_occurrence:
occurrence_lines.append(line)
if ";" in line:
found_occurrence = False
product_lines.append("".join(occurrence_lines))
occurrence_lines = []

ShapeType = data.ShapeType
valid = data.isValid()
area = data.Area
volume = data.Volume
length = data.Length
# faces = data.Faces
num_faces = len(data.Faces)
num_solids = len(data.Solids)

# Create a Test object with the extracted data

# print(dir(faceCollection), 'what be in it')
test = Test(data,ShapeType, valid, area, volume, length, num_faces)

# Create a list of dictionaries to hold the data for each object
object_list = []

# Append the data for the first object
object_dict = {
"stuknaam": product_lines[-1],
"ShapeType": test.ShapeType,
"valid_stuk": test.valid,
"oppervlakte": test.area,
"volume": test.volume,
"lengte": test.length,
"aantalFaces": test.num_faces,
# "isSame": test.isSame(),
# "faces": faceCollection,
"subparts": num_solids,
"face_data": test.face_data,
}
object_list.append(object_dict)

# Check if there are subparts and extract data from them
if num_solids > 0:
for i, solid in enumerate(data.Solids):
sub_valid = solid.isValid()
sub_ShapeType = solid.ShapeType
sub_area = solid.Area
sub_volume = solid.Volume
sub_length = solid.Length
sub_num_faces = len(solid.Faces)
sub_test = Test(solid,sub_ShapeType, sub_valid, sub_area, sub_volume, sub_length, sub_num_faces)
sub_object_dict = {
"stuknaam": product_lines,
"ShapeType": sub_test.ShapeType,
"valid_stuk": sub_test.valid,
"oppervlakte": sub_test.area,
"volume": sub_test.volume,
"lengte": sub_test.length,
"aantalFaces": sub_test.num_faces,
"subparts": len(sub_test.shape.Solids),
}
object_list.append(sub_object_dict)



print(data.Solids[1].Area == data.Solids[0].Area)
print(data.Solids[1].Volume == data.Solids[0].Volume)
print(dir(data.Solids[1]) == dir(data.Solids[0]))
print(data.Solids[1].Area == data.Solids[0].Area)
# print(data.isPartner(data.Solids[0]))



# Print the list of dictionaries, met json.dumps, anders krijgen we gewoon een vlakke string.
# print(object_list)
print(json.dumps(object_list))


except Exception as e:
print("An error occurred:", e)
sys.exit(1)
User avatar
onekk
Veteran
Posts: 6205
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: Geometric same solids

Post by onekk »

Please use the </> when posting code if not is impossible to load it properly in an editor.

Some considerations it will be possible to use FreeCAD in a Webapp but it is out of my knowledge as it involve to use "FreeCAD as a library".

Let's see if some more expert of this sort of things will step in and gave you some useful hints.

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/
User avatar
Roy_043
Veteran
Posts: 8550
Joined: Thu Dec 27, 2018 12:28 pm

Re: Geometric same solids

Post by Roy_043 »

Why do you say isPartner is useless? It seems to work for the shapes in your example file.

Code: Select all

doc = App.ActiveDocument
obj1 = doc.getObject("Part__Feature")
obj2 = doc.getObject("Part__Feature001")
obj1.Shape.isPartner(obj2.Shape)
User avatar
dunaden
Posts: 7
Joined: Wed Nov 18, 2020 2:43 pm

Re: Geometric same solids

Post by dunaden »

My solution to this problem is the function snippet below. Note however that this implementation requires the pose (FreeCAD.Placement) to be identical (or at least very similar) already. I personally use this as a method of validating test outputs against solids in pre-approved reference files.

Also this is obviously very computationally expensive for a comparison.

Code: Select all

def equate_solids_by_volume(
    solid_1: Part.Solid | Part.Shape,
    solid_2: Part.Solid | Part.Shape,
    max_volume_tolerance: float = 0.001,
    max_proportional_tolerance: float = 0.05,
    proportional: bool = False,
) -> bool:
    """Checks if 2 Solids are approximately equivalent by how their volumes overlap.

    NOTE:   This requires the placements of the 2 shapes/solids to be equal already, it
            does not account for identical shapes in different positions or rotations.

    Args:
        solid_1 (Part.Solid | Part.Shape): First input Solid to be tested for equality.
        solid_2 (Part.Solid | Part.Shape): Second input Solid to be tested for equality.
        max_volume_tolerance (float): The maximum volume of the difference between
            solids for equality if proportional == False
        max_proportional_tolerance (float): The maximum proportional difference in
            volume if proportional == True
        proportional (bool): if false, volumes are directly checked against
            max_volume_tolerance, else they are compared to max_proportional_tolerance
            standardized against solid_1.Volume

    Returns:
        bool: True if the Solids are approximately equivalent by checking the
            difference in volume of the fusion and solid_1.
    """
    # This is needed to make sure that the inputs are pre-fused together themselves.
    # If they aren't, overlapping solids will have their volumes considered separately.
    fused_solid_1 = Part.Shape()  # type: ignore
    for solid in solid_1.Solids:
        fused_solid_1 = fused_solid_1.fuse(solid)

    fused_solid_2 = Part.Shape()  # type: ignore
    for solid in solid_2.Solids:
        fused_solid_2 = fused_solid_2.fuse(solid)

    solid_1 = fused_solid_1
    solid_2 = fused_solid_2

    # If the difference in volume values is too much, already done - no need to compare
    # with fuses.
    volume_diff = abs(solid_1.Volume - solid_2.Volume)

    if proportional and ((volume_diff / solid_1.Volume) > max_proportional_tolerance):
        return False

    if not proportional and volume_diff > max_volume_tolerance:
        return False

    # If not, we need to determine the volume change when they are fused together.
    fuse_volume = solid_1.fuse(solid_2).Volume
    diff_volume = abs(fuse_volume - solid_1.Volume)

    if proportional:
        return bool((diff_volume / solid_1.Volume) < max_proportional_tolerance)

    return bool(diff_volume < max_volume_tolerance)
HalpPlease
Posts: 9
Joined: Tue Mar 14, 2023 10:26 am

Re: Geometric same solids

Post by HalpPlease »

Well thanks for the effort anyway.

I found a solution in this form:

Code: Select all

  for i, solid in enumerate(data.Solids):
        properties = (round(solid.Area, 2), round(solid.Volume, 2), round(solid.Length, 2), len(solid.Faces), len(solid.Edges))
        if properties not in properties_list:

            face_collection = []
            for face in solid.Faces:
                area = face.Area
                face_collection.append(area)

            max_area = max(face_collection)

            count = face_collection.count(max_area)

            properties_dict = {
                'count': 1,
                'valid': solid.isValid(),
                'ShapeType': solid.ShapeType,
                'area': solid.Area,
                'volume': solid.Volume,
                'length': solid.Length,
                'num_faces': len(solid.Faces),
                'num_edges': len(solid.Edges),
                'highestFaceArea': max_area,
                'highestFaceAreaCount': count
            }
            properties_list.append(properties)
            properties_dict['properties'] = properties
            properties_dict['sub_objects'] = []
            properties_dict['sub_objects'].append(i)

            object_list.append(properties_dict)

        else:
            index = properties_list.index(properties)
            properties_dict = object_list[index+1]
            properties_dict['count'] += 1
            properties_dict['sub_objects'].append(i)

    print(json.dumps(object_list))
Post Reply