Messages from Loft operation

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: 2764
Joined: Mon Feb 27, 2012 5:31 pm

Re: Messages from Loft operation

Post by keithsloan52 »

chrisb wrote: Wed Apr 14, 2021 7:17 am
keithsloan52 wrote: Wed Apr 14, 2021 6:48 am But as far as I know there is not a way to know when you have something like a cylinder or a cone what order the faces in Object.Faces are in.
You are right. That can be done only if you get the faces as input.
Using faces has attractions. Would for a given release of OCC that a standard cylinder top face would always be x and bottom face y?
Thinking if my code I code

Code: Select all

Cyl_top_face = 0
Cyl_bot_face = 3
Cone_top_face = 0
Cone_bot_face = 3  # If not a pointy Cone
etc
mario52
Veteran
Posts: 4692
Joined: Wed May 16, 2012 2:13 pm

Re: Messages from Loft operation

Post by mario52 »

Hi
keithsloan52 wrote: Tue Apr 13, 2021 6:58 pm

Code: Select all

19:46:53  1997.47 <App> Document.cpp(3527): two_cylinder#cylinder still touched after recompute
19:46:53  1997.47 <App> Document.cpp(3527): two_cylinder#cylinder001 still touched after recompute
What is it not happy about?
i have also this error and other, I don't know how I corrected

i upgrade my macro Copy a sliced of part ( Macro_D_Un_Jour_Raccord_2_Objects_By_Its_Faces ) the loft work very well with all faces

Image

mario
Maybe you need a special feature, go into Macros_recipes and Code_snippets, Topological_data_scripting.
My macros on Gist.github here complete macros Wiki and forum.
chrisb
Veteran
Posts: 54213
Joined: Tue Mar 17, 2015 9:14 am

Re: Messages from Loft operation

Post by chrisb »

keithsloan52 wrote: Wed Apr 14, 2021 8:23 am Using faces has attractions. Would for a given release of OCC that a standard cylinder top face would always be x and bottom face y?
I often use cylinders or other primitives to start a model. They are totally stable and are never affected by topological naming issues.
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
edwilliams16
Veteran
Posts: 3182
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Messages from Loft operation

Post by edwilliams16 »

Rather than lofting, I think a better algorithm to convex hull a collinear set of cylinders and cone frustra is by rotation around the common axis - the same way you did the pair of spheres. If you consider the cross-section with a plane containing the common axis, each cylinder looks like a rectangle and each cone a quadrilateral.
For instance, the convex hull of this collection is obtained by rotating the 2D convex hull of the three sketches Sketch, Sketch001 and Sketch002 - which is sketch003.
Screen Shot 2021-04-15 at 5.52.25 PM.png
Screen Shot 2021-04-15 at 5.52.25 PM.png (25.74 KiB) Viewed 634 times
gives
Screen Shot 2021-04-15 at 5.56.07 PM.png
Screen Shot 2021-04-15 at 5.56.07 PM.png (26.92 KiB) Viewed 634 times
So you end up just having to compute the 2D convex hull of the corners of the objects' cross-sections. It's just a few lines of code. The enclosed plots the hull of a random set of points - representing the corners of the cones and cylinders in the cross-section plane.

With a bit more complication, I think this could be generalized to an arbitrary collection of collinear spheres, cylinders and cones. The hull would consist of a set of circular arcs and straight lines, as it was in the two sphere case.

Code: Select all

# develop code to find convex hull of a line of circles and quadrilateral on an axis

def hulltopquad(coordList):
    points = sorted(coordList, key = lambda x: x[0]) # sort by x-coord
    top = [points[0], points[1]]
    for p in points[2:]:
        top.append(p)
        while len(top) > 2 and not _isConvex(*top[-3:]):
            del top[-2]
    return top

def _isConvex(p, q, r):
    'return True if the vectors pq to qr is a right turn ie convex'
    return q[0]*r[1] + p[0]*q[1] + r[0]*p[1] - \
            (q[0]*p[1] + r[0]*q[1] + p[0]*r[1]) < 0


#test code
#if __name__ == __main__:
import numpy as np
import matplotlib.pyplot as plt
import random
def test(npts):
    pts =[]
    for i in range(npts):
         pts.append((random.random(),abs(random.random())))
    tophull =  hulltopquad(pts)
    #transpose list
    tt = list(zip(*tophull))
    x = np.array(tt[0])
    y = np.array(tt[1])
    #plot tophull
    plt.plot(x, y)
    tp = list(zip(*pts))
    xp = np.array(tp[0])
    yp= np.array(tp[1])
    #scatter plot orig points
    plt.scatter(xp ,yp, color = 'red')
    plt.show()

test(10)

Here's some output from the above code - showing the convex hull on the top of the points.
Screen Shot 2021-04-15 at 6.07.49 PM.png
Screen Shot 2021-04-15 at 6.07.49 PM.png (24.26 KiB) Viewed 634 times
Attachments
coaxialcylandcones.FCStd
(27.96 KiB) Downloaded 10 times
keithsloan52
Veteran
Posts: 2764
Joined: Mon Feb 27, 2012 5:31 pm

Re: Messages from Loft operation

Post by keithsloan52 »

edwilliams16 wrote: Fri Apr 16, 2021 4:10 am Rather than lofting, I think a better algorithm to convex hull a collinear set of cylinders and cone frustra is by rotation around the common axis - the same way you did the pair of spheres.
Some very good and helpful comments.

The reason I was thinking down the lofting line, was my intuition rather than any logic was wondering if lofting might also work when they are not quite collinear.
edwilliams16
Veteran
Posts: 3182
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Messages from Loft operation

Post by edwilliams16 »

Neither lofting nor revolving will obtain the convex hull if the objects aren't collinear. If they are parallel, but not collinear, lofting will produce a body that is not convex and is contained in the true hull. OTOH, rotation can make an axisymmetric convex body that contains the true hull. Take your pick!

From the coding point of view, using the loft (even in the two body case) still requires the logic to determine which pair of faces to loft, which depends on their location and radii. This logic is equivalent to finding the 2D convex hull and gets exponentially more complicated with an increasing number of bodies if you don't have a clear picture of what you are doing.
Once you have found the convex hull, it tells you which points to connect up to rotate, or which pairs of faces to loft, depending on how you proceed.

There's an endless list of successively more complicated configurations of primitives of which to find the convex hull. I assume you are trying to attack the use cases you are most concerned with?
keithsloan52
Veteran
Posts: 2764
Joined: Mon Feb 27, 2012 5:31 pm

Re: Messages from Loft operation

Post by keithsloan52 »

edwilliams16 wrote: Fri Apr 16, 2021 5:40 pm There's an endless list of successively more complicated configurations of primitives of which to find the convex hull. I assume you are trying to attack the use cases you are most concerned with?
Correct.

I have tried to implement based on your code. (Changed to sort for z-cord i.e. object heights)

But not quite working on my test file gets points i.e. what is passed as coordList
[Vector (0.0, 0.0, -2.0), Vector (0.0, 9.25, -2.0), Vector (0.0, 9.25, 0.0), Vector (0.0, 0.0, -40.0), Vector (0.0, 11.9, -40.0), Vector (0.0, 10.0, -4.0)]

top gets reduced to [Vector (0.0, 0.0, -40.0), Vector (0.0, 9.25, 0.0)] which is not correct

Code: Select all

def createFace(coordList) :
    points = sorted(coordList, key = lambda x: x[2]) # sort by z-coord
    top = [points[0], points[1]]
    for p in points[2:]:
        top.append(p)
        while len(top) > 2 and not _isConvex(*top[-3:]):
            del top[-2]
    print(top)
    #poly = Part.makePolygon(top)
    #face = Part.makeFace(poly)
    #face = Part.makeFace(top)
    #return face
    
def _isConvex(p, q, r):
    'return True if the vectors pq to qr is a right turn ie convex'
    return q[0]*r[1] + p[0]*q[1] + r[0]*p[1] - \
            (q[0]*p[1] + r[0]*q[1] + p[0]*r[1]) < 0
Full code is at https://github.com/KeithSloan/OpenSCAD_Alt_Import
branch parallel

Test file
From_Finger.csg
(334 Bytes) Downloaded 8 times
edwilliams16
Veteran
Posts: 3182
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Messages from Loft operation

Post by edwilliams16 »

The algorithm works in 2D. Whatever it does given 3d vectors wont be right. The x, y coordinates are
X = distance of the face along the symmetry axis
Y = radius of face.

They are the coordinates as if you had drawn a sketch in the xy plane and revolved it around the x-axis as in my illustration freecad file.
The argument coordList is a list of 2d tuples [(x0, y0), (x1, y1) ...]

X you get by dotting obj.Placement.Base with the unit vector along the symmetry axis, which is obtained by applying the obj.Placement.Rotation to Vector(0,0,1) and adding obj.Height for the other end.

More detail later if required.
keithsloan52
Veteran
Posts: 2764
Joined: Mon Feb 27, 2012 5:31 pm

Re: Messages from Loft operation

Post by keithsloan52 »

Well I have to create a Part.Polygon to revolve and that takes FreeCAD.Vectors.
Okay so I made your x,y values FreeCAD.Vector(x,y,0) and it creates a correct polygon
but it is on the x-y plane and cylinders/cone cones are on the z axis. see screenshot
Tried creating Vectors FreeCAD.Vector(0,x,y) and adjusting your code but something is going wrong

Code: Select all

def createRevolveHull(coordlist) :
    points = sorted(coordlist, key = lambda x: x[1]) # sort by - tried 1 and 2 not working
    top = [points[0], points[1]]
    for p in points[2:]:
        top.append(p)
        while len(top) > 2 and not _isConvex(*top[-3:]):
            del top[-2]
    print(top)
    # close polygon
    top.append(top[0])
    poly = Part.makePolygon(top)
    revHull = poly.revolve(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),360)   
    return revHull

def _isConvex(p, q, r):
    'return True if the vectors pq to qr is a right turn ie convex'
    return q[1]*r[2] + p[1]*q[2] + r[1]*p[2] - \
            (q[1]*p[2] + r[1]*q[2] + p[1]*r[2]) < 0
Attachments
F01649AA-49FC-4AE1-9E0F-DECFDF9124C3.jpeg
F01649AA-49FC-4AE1-9E0F-DECFDF9124C3.jpeg (50.6 KiB) Viewed 544 times
keithsloan52
Veteran
Posts: 2764
Joined: Mon Feb 27, 2012 5:31 pm

Re: Messages from Loft operation

Post by keithsloan52 »

Okay change the code to

Code: Select all

                 ax1 = obj.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1))
                 print('ax1 : '+str(ax1))
                 ax = obj.Placement.Base.dot(ax1)
                 print('ax  : '+str(ax))
                 bx = ax + h
                 pointLst.append(FreeCAD.Vector(0,0,ax))
                 pointLst.append(FreeCAD.Vector(0,r1,ax))
                 pointLst.append(FreeCAD.Vector(0,r2,bx))
                 pointLst.append(FreeCAD.Vector(0,0,bx))
02F6A8D2-E00B-441A-A9AA-A00AD0E0E833.jpeg
02F6A8D2-E00B-441A-A9AA-A00AD0E0E833.jpeg (41.99 KiB) Viewed 542 times
And it works - Many Thanks
Just have to find out how to get rid of the message

Code: Select all

22:31:01  2.7e-08 <App> Document.cpp(3527): From_Finger#cylinder still touched after recompute
Post Reply