Discussion on placement

Have some feature requests, feedback, cool stuff to share, or want to know where FreeCAD is going? This is the place.
Forum rules
Be nice to others! Read the FreeCAD code of conduct!
edwilliams16
Veteran
Posts: 3111
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Discussion on placement

Post by edwilliams16 »

Code: Select all

from FreeCAD import Vector

def circumcenter(A, B, C):
    '''Return the circumcenter of three 3D vertices'''
    a2 = (B - C).Length**2
    b2 = (C - A).Length**2
    c2 = (A - B).Length**2
    if a2 * b2 * c2 == 0.0:
        print('Three vertices must be distinct')
        return
    #compute barycentric coordinates of circumcenter
    alpha = a2 * (b2 + c2 - a2)
    beta = b2 * (c2 + a2 - b2)
    gamma = c2 * (a2 + b2 - c2)
    return (alpha*A + beta * B + gamma * C)/(alpha + beta + gamma)
Here's some code to compute the circumcenter of three vertices if you want to play with it. The error checking may not be appropriate.
edwilliams16
Veteran
Posts: 3111
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Discussion on placement

Post by edwilliams16 »

Code: Select all

debug = True

def circumcenter(A, B, C):
    '''Return the circumcenter of three 3D vertices'''
    a2 = (B - C).Length**2
    b2 = (C - A).Length**2
    c2 = (A - B).Length**2
    if a2 * b2 * c2 == 0.0:
        print('Three vertices must be distinct')
        return
    alpha = a2 * (b2 + c2 - a2)
    beta = b2 * (c2 + a2 - b2)
    gamma = c2 * (a2 + b2 - c2)
    return (alpha*A + beta * B + gamma * C)/(alpha + beta + gamma)

selt = Gui.Selection.getSelectionEx()
if len(selt) != 1:
    print('Select one vertex, edge or object')
    shift = App.Vector(0, 0, 0)  # bail and do nothing  
else:
    sel = selt[0]
    if sel.HasSubObjects:
        vv = sel.SubObjects
        if len(vv) ==3 and vv[0].ShapeType == 'Vertex' and   vv[1].ShapeType == 'Vertex' and vv[2].ShapeType == 'Vertex':
            shift = circumcenter(vv[0].Point, vv[1].Point, vv[2].Point)
            if debug: print('Three Vertices')
        else: 
            selected = sel.SubObjects[0] 
            if selected.ShapeType == 'Vertex':
                shift = selected.Point
                if debug: print('One vertex')
            elif selected.ShapeType == 'Edge':
                if selected.Curve.TypeId == 'Part::GeomCircle':    #circles and arcs
                     shift = selected.Curve.Center
                     if debug: print('One arc')      
                elif selected.Curve.TypeId == 'Part::GeomLine':
                    shift = (selected.valueAt(selected.FirstParameter) + selected.valueAt(selected.LastParameter))/2 # mid-point
                    if debug: print('One line')
                else:
                    shift = App.Vector(0, 0, 0)  # bail and do nothing
            elif selected.ShapeType == 'Face':
                shift = selected.CenterOfMass #center of face
                if debug: print('One face')
            else:
                shift = App.Vector(0, 0, 0)  # bail and do nothing
    else:
        if debug: print(f'ObjectName is {sel.ObjectName}')
        #if sel.ObjectName[0:11] == 'BaseFeature':
        if hasattr(sel.Object, 'Shape'):
            if debug: print('One solid')
            if sel.Object.Shape.ShapeType == 'Solid':
                shift = sel.Object.Shape.CenterOfMass
            elif sel.Object.Shape.ShapeType == 'Compound':
                shift = sel.Object.Shape.SubShapes[0].CenterOfMass
            #more cases?
            else:
                shift = App.Vector(0, 0, 0)  # bail and do nothing                
        else:
            shift = App.Vector(0, 0, 0)  # bail and do nothing
if debug: print(f'Shift = {shift}')
base = App.ActiveDocument.getObject(sel.ObjectName).Placement
base.move(-shift)
App.ActiveDocument.recompute()
        
        
I added the three vertices -> move to center of circle option. The nested if's are getting a bit out of hand...

As before import and make solid, then make a BaseFeature of the solid in a body. Then either pick one vertex. one line, one arc or the whole solid, OR now three vertices. Then run the macro.

EDIT The macro worked more generally than expected. With an added tweak it now seems to work for STL imports that have been processed with Part|Create Shape from Mesh. It does not need to be put in a Body container or even converted to solid. Sorry so many "progress" posts.

Question. Are there more ShapeTypes to deal with other than Solid and Compound?
drmacro
Veteran
Posts: 8870
Joined: Sun Mar 02, 2014 4:35 pm

Re: Discussion on placement

Post by drmacro »

edwilliams16 wrote: Sun Jun 13, 2021 9:05 pm ...
I added the three vertices -> move to center of circle option. The nested if's are getting a bit out of hand...

As before import and make solid, then make a BaseFeature of the solid in a body. Then either pick one vertex. one line, one arc or the whole solid, OR now three vertices. Then run the macro.

EDIT The macro worked more generally than expected. With an added tweak it now seems to work for STL imports that have been processed with Part|Create Shape from Mesh. It does not need to be put in a Body container or even converted to solid. Sorry so many "progress" posts.

Question. Are there more ShapeTypes to deal with other than Solid and Compound?
Seems to work.

As for more types...good question. :)
Star Trek II: The Wrath of Khan: Spock: "...His pattern indicates two-dimensional thinking."
drmacro
Veteran
Posts: 8870
Joined: Sun Mar 02, 2014 4:35 pm

Re: Discussion on placement

Post by drmacro »

And...right after I sent the last post this from another post with essentially a list of them all ...maybe?

https://github.com/MariwanJ/Design456/b ... eak.py#L98

Note: not saying they all need to be processed, just I had no list before... :mrgreen:
Star Trek II: The Wrath of Khan: Spock: "...His pattern indicates two-dimensional thinking."
edwilliams16
Veteran
Posts: 3111
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Discussion on placement

Post by edwilliams16 »

Code: Select all

debug = True
'''Usage:
This macro will adjust the placement of a shape to place a selected feature at the origin.
One vertex:  - to origin
One line segment - midpoint to origin
One circle - center to origin
One arc of circle - center to origin
One face - CG of face to origin
Three vertices - center of circle through vertices to origin
Shape - CG of shape to origin

If the shape is a BaseFeature inside a body, the base feature's placement in the body is adjusted.
'''

def circumcenter(A, B, C):
    '''Return the circumcenter of three 3D vertices'''
    a2 = (B - C).Length**2
    b2 = (C - A).Length**2
    c2 = (A - B).Length**2
    if a2 * b2 * c2 == 0.0:
        print('Three vertices must be distinct')
        return
    alpha = a2 * (b2 + c2 - a2)
    beta = b2 * (c2 + a2 - b2)
    gamma = c2 * (a2 + b2 - c2)
    return (alpha*A + beta * B + gamma * C)/(alpha + beta + gamma)

def centerofmass(subshapes):
    mass =0.0
    moment = App.Vector(0, 0, 0)
    for sh in subshapes:
        mass += sh.Volume
        moment += sh.Volume * sh.CenterOfMass
    return moment/mass    

selt = Gui.Selection.getSelectionEx()
if len(selt) != 1:
    print('Select one vertex, edge or object\nor three vertices for center of circle')
    shift = App.Vector(0, 0, 0)  # bail and do nothing  
else:
    sel = selt[0]
    if sel.HasSubObjects:
        vv = sel.SubObjects
        if len(vv) ==3 and vv[0].ShapeType == 'Vertex' and   vv[1].ShapeType == 'Vertex' and vv[2].ShapeType == 'Vertex':
            shift = circumcenter(vv[0].Point, vv[1].Point, vv[2].Point)
            if debug: print('Three Vertices')
        elif len(vv) == 1: 
            selected = sel.SubObjects[0]
            if selected.ShapeType == 'Vertex':
                shift = selected.Point
                if debug: print('One vertex')
            elif selected.ShapeType == 'Edge':
                if selected.Curve.TypeId == 'Part::GeomCircle':    #circles and arcs
                     shift = selected.Curve.Center
                     if debug: print('One arc')      
                elif selected.Curve.TypeId == 'Part::GeomLine':
                    shift = (selected.valueAt(selected.FirstParameter) + selected.valueAt(selected.LastParameter))/2 # mid-point
                    if debug: print('One line')
                else:
                    print(f'edge is {selected.Curve.TypeId} not handled')
                    shift = App.Vector(0, 0, 0)  # bail and do nothing
            elif selected.ShapeType == 'Face':
                shift = selected.CenterOfMass #center of face
                if debug: print('One face')
            else:
                print(f'ShapeType {selected.ShapeType} not handled')
                shift = App.Vector(0, 0, 0)  # bail and do nothing
        else:
            print('Wrong number of Selections')
            shift = App.Vector(0, 0, 0)  # bail and do nothing
    else:
        if debug: print(f'ObjectName is {sel.ObjectName}')
        if hasattr(sel.Object, 'Shape'):
            if debug: print('One solid')
            shape = sel.Object.Shape
            if shape.ShapeType == 'Solid' or shape.ShapeType == 'Shell':
                shift = shape.CenterOfMass
            elif shape.ShapeType == 'Compound' or shape.ShapeType == 'CompSolid':
                shift = centerofmass(shape.SubShapes)
            #more cases?
            else:
                print(f'Object ShapeType {sel.Object.Shape.ShapeType} not handled')
                shift = App.Vector(0, 0, 0)  # bail and do nothing                
        else:
            print(f'{sel.ObjectName} is not a shape')
            shift = App.Vector(0, 0, 0)  # bail and do nothing
if debug: print(f'Shift = {shift}')
base = App.ActiveDocument.getObject(sel.ObjectName).Placement
base.move(-shift)
App.ActiveDocument.recompute()
The macro now handles ShapeTypes: Solid, Shell, Compound and CompSolid, computing the CG of composite objects.
It will use a single vertex, the mid-point of a line, the center of a circle or arc or the CG of a face as the reference point to move to the origin.
It will do nothing with B-splines and Wires.

There's no Undo - but the restoring shift is printed to the report view. Undo would be better if I figured how to do it.

Maybe approaching prime-time with user testing? Should I link to new thread in Python Scripting and Macros?
drmacro
Veteran
Posts: 8870
Joined: Sun Mar 02, 2014 4:35 pm

Re: Discussion on placement

Post by drmacro »

Hmm...I have not attempted an Undo.

But I think there is call to get snapshot of the Undo stack, before you begin. Then another call to mark the end of the operation and it puts it on the stack...but I'm winging it based past non-FreeCAD experiences. ;)

As for posting in the scripting forum, more eyes are probably better.
Star Trek II: The Wrath of Khan: Spock: "...His pattern indicates two-dimensional thinking."
chrisb
Veteran
Posts: 53933
Joined: Tue Mar 17, 2015 9:14 am

Re: Discussion on placement

Post by chrisb »

I tested the macro: works great! It does more than I need.
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
User avatar
Pauvres_honteux
Posts: 728
Joined: Sun Feb 16, 2014 12:05 am
Location: Far side of the moon

Re: Discussion on placement

Post by Pauvres_honteux »

Any chance this can be made to "remember" the moved stuffs original position?
If so, this would in a wiff become the almighty Move-tool we so badly need.
drmacro
Veteran
Posts: 8870
Joined: Sun Mar 02, 2014 4:35 pm

Re: Discussion on placement

Post by drmacro »

Pauvres_honteux wrote: Thu Jun 17, 2021 9:34 am Any chance this can be made to "remember" the moved stuffs original position?
If so, this would in a wiff become the almighty Move-tool we so badly need.
Have you actually read the entirety of this thread?

The initial issue is that the original position is not forgotten when something is moved before it is moved into a body. :? :lol:
Star Trek II: The Wrath of Khan: Spock: "...His pattern indicates two-dimensional thinking."
User avatar
Pauvres_honteux
Posts: 728
Joined: Sun Feb 16, 2014 12:05 am
Location: Far side of the moon

Re: Discussion on placement

Post by Pauvres_honteux »

drmacro wrote: Thu Jun 17, 2021 10:09 am Have you actually read the entirety of this thread?
Okey, I see how my quest can be misread here.

Let us imagine I envision a broader scope so to say.
Expressing myself differently, this Move-tool I was whishing for would then be functional for anything, not only solids.
It est, it shall remember the original position for anything the user whish to move.
Post Reply