Identifying Intersecting edges of two fused objects

Need help, or want to share a macro? Post here!
User avatar
mnesarco
Posts: 107
Joined: Thu Mar 26, 2020 8:52 pm

Identifying Intersecting edges of two fused objects

Postby mnesarco » Thu Apr 16, 2020 1:03 am

Hi Friends,

I don't know if this is a basic thing or not, but after hours of search i cannot find any way to identify the edges of two fused objects like in the picture. My goal is to generate two shapes, then fuse them and then create a Fillet of the edges where the two objects connect each other.

Selection_061.png
Selection_061.png (11.05 KiB) Viewed 552 times
Thanks for your help.
FliegenderZirkus
Posts: 8
Joined: Thu Jan 23, 2020 6:49 am

Re: Identifying Intersecting edges of two fused objects

Postby FliegenderZirkus » Thu Apr 16, 2020 4:43 am

The only way I know is to find some special property of the edges which should get a fillet which distinguishes the from the rest. This can be for example their length, position, curvature or curve type. I'm not aware of a better way, but there may well be one!
In the example you posted one could look at the angle formed between the two faces meeting at each edge. It seems that the edges to get a fillet are the only ones where this angle changes along the length of the edge - for all others it is constant 90 deg. Below is a snippet I used to calculate angle between two faces along a common edge. Perhaps it can be modified for this purpose.
I'm looking for a more general approach to this problem as well.

Code: Select all

def max_angle_between_faces(face1, face2, edge):
	"""Find maximum angle between face1 and face2 along common edge"""
    
	max_angle = 0.0
	points = edge.discretize(Number=10) # edge is discretized into a number of points
	for pp in points:
		pt = App.Base.Vector(pp.x,pp.y,pp.z) 
		uv1 = face1.Surface.parameter(pt)
		u1 = uv1[0]
		v1 = uv1[1]
		normal1 = face1.normalAt(u1,v1)
		uv2 = face2.Surface.parameter(pt)
		u2 = uv2[0]
		v2 = uv2[1]
		normal2 = face2.normalAt(u2,v2)
		angle = math.degrees(normal1.getAngle(normal2))
		if angle > max_angle:
			max_angle = angle
	return max_angle
FliegenderZirkus
Posts: 8
Joined: Thu Jan 23, 2020 6:49 am

Re: Identifying Intersecting edges of two fused objects

Postby FliegenderZirkus » Thu Apr 16, 2020 6:53 am

Regarding the more genenral approach, I wonder if this could work..? We could loop over edges of the fused shape and look whether each edge is also part of either of the two base shapes. If yes, then it's an old edge and we don't want to apply a fillet. If not, then it's a new edge formed by the fusion. The catch would be how to write such a function for comparing edges. They won't always be identical, it's enough if a part of the edges overlaps. In your example the cylinder cuts one of the cube edges and splits it into two. The compare function would need to recognise that the two new shorter cube edges overlap with the original longer cube edge and thus is not a new entity. I suspect this approach would be rather ugly and would depend on discretisation and tolerances. But it's a thought.
chrisb
Posts: 25204
Joined: Tue Mar 17, 2015 9:14 am

Re: Identifying Intersecting edges of two fused objects

Postby chrisb » Thu Apr 16, 2020 7:53 am

This is probably very closely related to the toponaming, i.e. this may have to be recomputed on other changes as well. The behaviour may change when using realthunders toponaming changes, which still have to be merged.
onekk
Posts: 379
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: Identifying Intersecting edges of two fused objects

Postby onekk » Thu Apr 16, 2020 7:58 am

you can scan the edges and obtain some values to be checked against:

Code: Select all

    for idx, edge in enumerate(body.Shape.Edges):
        VCM = edge.CenterOfMass
        #print("edge {0}: {1} ".format(idx, VCM))
        if VCM[2] < (alt * 0.99):
            fillet_e.append((idx + 1, rag_ext, rag_ext)) 

In this case I was in need to determine the edges of the bottom face of a box (body in the code).

I've checked the edges CenterOfMass, roughly the half ot the edge and cheched them againt the max height (VCM[2] < (alt *0.99)) CenterOfMass return a Vector so VCM[2] is the Z component.

Then I've added the edge number, beware that for example a fille like in my case, they start from 1 and not from 0 as idx is starting, plus the data of the two radius (I don't know why are two radius, I suppose for two different axis, maybe XZ or XY)

There are attributes for an edge, this is the output of the Python Console:

['Area', 'BoundBox', 'CenterOfMass', 'Closed', 'CompSolids', 'Compounds', 'Content', 'Curve', 'Degenerated', 'Edges', 'Faces', 'FirstParameter', 'LastParameter', 'Length', 'Mass', 'Matrix', 'MatrixOfInertia', 'MemSize', 'Module', 'Orientation', 'ParameterRange', 'Placement', 'PrincipalProperties', 'ShapeType', 'Shells', 'Solids', 'StaticMoments', 'Tolerance', 'TypeId', 'Vertexes', 'Volume', 'Wires', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', 'ancestorsOfType', 'centerOfCurvatureAt', 'check', 'childShapes', 'cleaned', 'common', 'complement', 'copy', 'curvatureAt', 'cut', 'defeaturing', 'derivative1At', 'derivative2At', 'derivative3At', 'discretize', 'distToShape', 'dumpContent', 'dumpToString', 'exportBinary', 'exportBrep', 'exportBrepToString', 'exportIges', 'exportStep', 'exportStl', 'extrude', 'firstVertex', 'fix', 'fixTolerance', 'fuse', 'generalFuse', 'getAllDerivedFrom', 'getElement', 'getFacesFromSubelement', 'getParameterByLength', 'getTolerance', 'globalTolerance', 'hashCode', 'importBinary', 'importBrep', 'importBrepFromString', 'inTolerance', 'isClosed', 'isDerivedFrom', 'isEqual', 'isInside', 'isNull', 'isPartner', 'isSame', 'isSeam', 'isValid', 'lastVertex', 'limitTolerance', 'makeChamfer', 'makeFillet', 'makeOffset2D', 'makeOffsetShape', 'makeParallelProjection', 'makePerspectiveProjection', 'makeShapeFromMesh', 'makeThickness', 'mirror', 'multiFuse', 'normalAt', 'nullify', 'oldFuse', 'optimalBoundingBox', 'overTolerance', 'parameterAt', 'parameters', 'project', 'proximity', 'read', 'reflectLines', 'removeInternalWires', 'removeShape', 'removeSplitter', 'replaceShape', 'restoreContent', 'reverse', 'revolve', 'rotate', 'scale', 'section', 'sewShape', 'slice', 'slices', 'split', 'tangentAt', 'tessellate', 'toNurbs', 'transformGeometry', 'transformShape', 'translate', 'valueAt', 'writeInventor']

How to obtain this?

use this trick:

- create a solid in FreeCAD, say you have created a document named "test" with a Box, named "box", in the python console, type:

Code: Select all

obj = App.getDocument("test").getObject("box")
obj1 =obj.Shape.Edges[0]
dir(obj1)
suppose you want to analyze the methods exposed by say BounBox:

Code: Select all

dir(obj1.BoundBox)
['Center', 'DiagonalLength', 'XLength', 'XMax', 'XMin', 'YLength', 'YMax', 'YMin', 'ZLength', 'ZMax', 'ZMin', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'add', 'closestPoint', 'enlarge', 'getEdge', 'getIntersectionPoint', 'getPoint', 'intersect', 'intersected', 'isCutPlane', 'isInside', 'isValid', 'move', 'scale', 'setVoid', 'transformed', 'united']

and so on.

Code: Select all

bb1 = obj1.BounBox
print(bb1.__doc__)
will print this informations.

Bound box class
A bounding box is an orthographic cube which is a way to describe outer boundaries.
You get a bounding box from a lot of 3D types. It is often used to check if a 3D
entity lies in the range of another object. Checking for bounding interference first
can save a lot of computing time!

Constructor:
App.BoundBox([Xmin,Ymin,Zmin,Xmax,Ymax,Zmax])
App.BoundBox(Tuple, Tuple)
App.BoundBox(Vector, Vector)
App.BoundBox(BoundBox)


Hope it will help

Regards

Carlo D.
User avatar
mnesarco
Posts: 107
Joined: Thu Mar 26, 2020 8:52 pm

Re: Identifying Intersecting edges of two fused objects

Postby mnesarco » Thu Apr 16, 2020 3:44 pm

Hi Friends, thank you for your help. It looks very difficult to do, especially with irregular arbitrary solids. The cube and Cylinder was an oversimplified example.

What i am trying to archive is a general Fuse operation with an additional parameter: fillet radius. The idea is that all edges in the section have a fillet.
jbi
Posts: 57
Joined: Sun Apr 24, 2016 3:28 pm

Re: Identifying Intersecting edges of two fused objects

Postby jbi » Fri Apr 17, 2020 6:12 am

You could check the distance of all edges of the fused object with the distance to each individual edge of original objects. Best to use a midpoint of the edge, because the newly created edge(s) at the cube have also zero distance to the cylinder. Only midpoints which have zero distance to at least 2 original objects are common. This would be a geometrical solution which is slow.

Code: Select all


edgelist=fusedobj.Edges
#create the edgemidvertexes by walking edgelist

candidateidx=[]

for vtxnb,midvertex in enumerate(edgemidvertexes):
	count=0	
	for obj in sourceobjects: #should also work with more than 2 objects
		d=midvertex.distToShape(obj)[0] #the distance
		if d<1e-3:
			count=count+1
			if count==2:
				candidateidx.append(vtxnb) #this has corespondence to edgelist indexes,must be the common edges
				break

Just an Idea: Maybe it would be also possible to use hashCode() on original and fused object edge vertexes. Only if both vertex hashcodes of an fused object edge are not in the originals vertex hashcode list this edge is a common one. But maybe the vertex hashcodes change over the fuse operation (I do not know). This would be more of a logical solution which is faster than the geometrical one.
User avatar
microelly2
Posts: 4690
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Identifying Intersecting edges of two fused objects

Postby microelly2 » Fri Apr 17, 2020 7:02 am

To identify a subobject you need a key which is relative unique.
I use for faces this:

Code: Select all


def pkey2(c,pa,pb,r):
    return tuple([c,r]+list(np.round(pa,2))+list(np.round(pb,2)))

def getFaceKey(face):
    '''facekey for face type'''

    f=face
        
    typ=f.Surface.__class__.__name__
    if typ == 'Cone':
        pk=pkey2('cone',f.Surface.Apex,f.Surface.Axis,f.Surface.SemiAngle)
        k=geokey(f)
    elif typ == 'Plane':
        pk=pkey2('plane',f.Surface.Position,f.Surface.Axis,0)
    elif typ == 'Sphere':
        pk=pkey2('sphere',f.Surface.Center,f.Surface.Axis,f.Surface.Radius)
    elif typ == 'Cylinder':
        pk=pkey2('cyli',f.Surface.Center,f.Surface.Axis,f.Surface.Radius)
    else:
        raise Exception("unexpected surface type")
    return pk
   
        

The key for edges is still under testing, but it has a simiular structure.
I will post it when my tests are finished.
The results of face analysis you find in this video (its in German) but you can see the topogocigal structure of the faces in a workflow of unions and cuts. The process for edges will be the same. I think I can make the vidoe for edges till weekend.
https://www.youtube.com/watch?v=Dh43wd_3hrA
User avatar
microelly2
Posts: 4690
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Identifying Intersecting edges of two fused objects

Postby microelly2 » Fri Apr 17, 2020 8:31 am

Here the example code for edge classification

Code: Select all

def runEdges():  
    
    edgesA={}

    for i,e in enumerate(FreeCAD.ActiveDocument.Cut.Shape.Edges):
        t=e.Curve.__class__.__name__
        print (i,t)
        k=None
        if t =='Circle':
            k=np.round((list(e.ParameterRange)+ list( e.Curve.Location) + [e.Curve.Radius] +list(e.Curve.Axis)),2)
        if t =='Line':
            k=np.round((list(e.ParameterRange)+ list( e.Curve.Location) + [e.Length] +list(e.Curve.Direction)),2)
        if t =='Hyperbola':
            k=np.round((list(e.ParameterRange)+ list( e.Curve.Location) + [e.Curve.Eccentricity] +list(e.Curve.Axis)),2)
        if t =='BSplineCurve':
            k=list(np.round((list(e.ParameterRange)+ list( e.Curve.getPoles()[0]) + list(e.Curve.getPoles()[-1])),2))
            k += list(np.round(e.MatrixOfInertia.A,2))
        #print(k)
        assert k is not None
        edgesA[tuple(k)]=i

    print('\n'*4)
    col=[]
    col_new=[]
    for i,e in enumerate(FreeCAD.ActiveDocument.Cut001.Shape.Edges):
            t=e.Curve.__class__.__name__
            k=None
            if t =='Circle':
                k=np.round((list(e.ParameterRange)+ list( e.Curve.Location) + [e.Curve.Radius] +list(e.Curve.Axis)),2)
            if t =='Line':
                k=np.round((list(e.ParameterRange)+ list( e.Curve.Location) + [e.Length] +list(e.Curve.Direction)),2)
            if t =='Hyperbola':
                k=np.round((list(e.ParameterRange)+ list( e.Curve.Location) + [e.Curve.Eccentricity] +list(e.Curve.Axis)),2)
            if t =='BSplineCurve':
                k=list(np.round((list(e.ParameterRange)+ list( e.Curve.getPoles()[0]) + list(e.Curve.getPoles()[-1])),2))
                k += list(np.round(e.MatrixOfInertia.A,2))
            assert k is not None
            try:
                ii=edgesA[tuple(k)]
                print ("found ", i, ii)
                col += [e]
            except:
                print ("not found")
                col_new += [e]
            
    Part.show(Part.Compound(col))
    FreeCAD.ActiveDocument.ActiveObject.Label="reused Edges"
    FreeCAD.ActiveDocument.ActiveObject.ViewObject.LineColor=(0.,1.,0.)
    Part.show(Part.Compound(col_new))
    
    FreeCAD.ActiveDocument.ActiveObject.Label="new or modified Edges"
    FreeCAD.ActiveDocument.ActiveObject.ViewObject.LineColor=(1.,0.,0.)

User avatar
mnesarco
Posts: 107
Joined: Thu Mar 26, 2020 8:52 pm

Re: Identifying Intersecting edges of two fused objects

Postby mnesarco » Sat Apr 18, 2020 3:20 am

Interesting ideas, I will try to follow them...

Thank you Friends.