Square to round transition

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Square to round transition

Post by jfc4120 »

I am locked out, cannot type in placement or position for some reason.
Attachments
Screenshot 2022-07-19 220841.png
Screenshot 2022-07-19 220841.png (7.86 KiB) Viewed 1061 times
edwilliams16
Veteran
Posts: 3109
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Square to round transition

Post by edwilliams16 »

OK. You have it "attached" to the XY-Plane. Edit the attachment offset instead.

BTW, if you enclose the file there’s less guessing about the problem.
edwilliams16
Veteran
Posts: 3109
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Square to round transition

Post by edwilliams16 »

I tried using Part Design's loft to a point (FreeCAD 0.20+). At least the way that I did it, it was more complicated and the results were the same.
Part Design only creates solids, so after creating a suitable lofted solid:
Screen Shot 2022-07-20 at 8.37.05 AM.png
Screen Shot 2022-07-20 at 8.37.05 AM.png (20.65 KiB) Viewed 985 times
I did a Draft Downgrade to faces (which mysteriously gets rid of the body object somehow), selected the outside faces and Draft upgraded to a shell:
Screen Shot 2022-07-20 at 8.40.38 AM.png
Screen Shot 2022-07-20 at 8.40.38 AM.png (17.39 KiB) Viewed 985 times
Then, as before to flatten the shell, I had to go via creating a mesh and Mesh Design|Unwrap Mesh.
Screen Shot 2022-07-20 at 8.45.58 AM.png
Screen Shot 2022-07-20 at 8.45.58 AM.png (12.49 KiB) Viewed 985 times
This gives a perfectly good cutting template, but if you extract vertex coordinates from it, you get a large number, even along the straight edges, because of the intermediate mesh resolution. This might be OK for CNC...

So, depending on your exact needs, it might be better to use the low level approach of the original Basic code, translated to Python, than using a spreadsheet driven FreeCAD model of the object.
The technical stumbling block to a totally clean solution is the unwrapping of the ruled curved faces without going through a mesh.
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Square to round transition

Post by jfc4120 »

@edwilliams16 to practice I did a 1/4 transaction attached are some files with stages.
I had to to the 2 larges sections separate and the middle to faces the connect, then mesh, then unroll.

Note, this is why I prefer the above program that "auto" lays out a fitting flat to begin with, by just punching in a few parameters. It lay out in a blink of an eye. However I am trying to learn other techniques as well.

Also when I tried to unfold all from one mesh, it was way incorrect.

A big transition will need more segments, like 36, enclosed is one, and that is 48 inches, not mm.
Attachments
big_transition.png
big_transition.png (29.83 KiB) Viewed 960 times
ms.png
ms.png (31.68 KiB) Viewed 971 times
ms2.FCStd
(13.07 KiB) Downloaded 14 times
ms3.FCStd
(16.91 KiB) Downloaded 13 times
ms4.FCStd
(20.34 KiB) Downloaded 13 times
edwilliams16
Veteran
Posts: 3109
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Square to round transition

Post by edwilliams16 »

It seems when the faces were flat triangles, the mesh triangulation does no subdivision, so your route of approximating the circle with a polygon works well with the mesh unrolling.

Here's a simple script that transitions a square to an n-agon. I just did a quarter, because of the symmetry. If you want to generalize it to tilted or offset n-agons, or rectangles instead of squares, it's straightforward...

Just paste the (edited) script into the python console.

Code: Select all

import Part, Mesh, MeshPart, PartGui
import FreeCADGui as Gui
import math

def printVertices(vs):
    App.Console.PrintMessage('Template Vertices')
    for i, v in enumerate(vs):
        App.Console.PrintMessage(f'{i}:  ({v.x:.2f},{v.y:.2f})\n')

doc = App.ActiveDocument
#square on xy-plane, centered at origin: work in first quadrant
V3 = App.Vector
inchtomm = 25.4

####inputs
squareSide = 40 * inchtomm
height = 15 * inchtomm
radius = 15 * inchtomm
nSides = 8
####

sV = [squareSide/2*V3(*v) for v in [(1,0,0), (1, 1, 0), (0, 1, 0)]]
# sV[2]      sV[1]     
#
# O          sV[0]
# N-sided quarter circle
thetas = [(math.pi / 2 ) * j/nSides for j in range(nSides + 1)]
cV = [V3(radius * math.cos(t), radius * math.sin(t), height) for t in thetas]
firstFace = Part.Face(Part.makePolygon([sV[0], cV[0], sV[1], sV[0]]))
#Part.show(firstFace)
faceList = [firstFace]
lastFace = Part.Face(Part.makePolygon([sV[2], cV[-1], sV[1], sV[2]]))
#Part.show(lastFace)
for j in range(nSides):
    f = Part.Face(Part.makePolygon([sV[1], cV[j], cV[j+1], sV[1]]))
    #Part.show(f)
    faceList.append(f)

faceList.append(lastFace)
shell = Part.Shell(faceList)
Part.show(shell, "quarterShell")


mesh = doc.addObject('Mesh::Feature','Mesh')
mesh.Mesh=MeshPart.meshFromShape(Shape=shell, LinearDeflection=0.1, AngularDeflection=0.523599, Relative=False)
mesh.Label="Shape (Meshed)"
Gui.Selection.addSelection(doc.Name, 'Mesh')
Gui.runCommand('MeshPart_CreateFlatMesh',0)

template = doc.getObject('Shape')
template.Label = 'Template'
templateVertices = [v.Point for v in template.Shape.Vertexes]
printVertices(templateVertices)




Attachments
Screen Shot 2022-07-20 at 3.10.56 PM.png
Screen Shot 2022-07-20 at 3.10.56 PM.png (15.62 KiB) Viewed 908 times
edwilliams16
Veteran
Posts: 3109
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Square to round transition

Post by edwilliams16 »

Removed the Gui dependence of the script.

Code: Select all

import numpy as np
import Part, Mesh, MeshPart
import math
import numpy as np
import flatmesh

def printVertices(vs):
    App.Console.PrintMessage('Template Vertices')
    for i, v in enumerate(vs):
        App.Console.PrintMessage(f'{i}:  ({v.x:.2f},{v.y:.2f})\n')

def flattenMesh(obj):
    #https://github.com/FreeCAD/FreeCAD/blob/0d0bd1168bb96ded8dfe5ab3ca60588dec5d2949/src/Mod/MeshPart/Gui/MeshFlatteningCommand.py
    points = np.array([[i.x, i.y, i.z] for i in obj.Mesh.Points])
    faces = np.array([list(i) for i in  obj.Mesh.Topology[1]])
    flattener = flatmesh.FaceUnwrapper(points, faces)
    flattener.findFlatNodes(5, 0.95)
    boundaries = flattener.getFlatBoundaryNodes()
    wires = []
    for edge in boundaries:
        pi = Part.makePolygon([App.Vector(*node) for node in edge])
        wires.append(Part.Wire(pi))
    return wires

doc = App.ActiveDocument
#square on xy-plane, centered at origin: work in first quadrant
V3 = App.Vector
inchtomm = 25.4

####inputs
squareSide = 40 * inchtomm
height = 15 * inchtomm
radius = 15 * inchtomm
nSides = 8
####

sV = [squareSide/2*V3(*v) for v in [(1,0,0), (1, 1, 0), (0, 1, 0)]]
# sV[2]      sV[1]     
#
# O          sV[0]
# N-sided quarter circle
thetas = [(math.pi / 2 ) * j/nSides for j in range(nSides + 1)]
cV = [V3(radius * math.cos(t), radius * math.sin(t), height) for t in thetas]
firstFace = Part.Face(Part.makePolygon([sV[0], cV[0], sV[1], sV[0]]))
#Part.show(firstFace)
faceList = [firstFace]
lastFace = Part.Face(Part.makePolygon([sV[2], cV[-1], sV[1], sV[2]]))
#Part.show(lastFace)
for j in range(nSides):
    f = Part.Face(Part.makePolygon([sV[1], cV[j], cV[j+1], sV[1]]))
    #Part.show(f)
    faceList.append(f)

faceList.append(lastFace)
shell = Part.Shell(faceList)
Part.show(shell, "quarterShell")


mesh = doc.addObject('Mesh::Feature','Mesh')
mesh.Mesh=MeshPart.meshFromShape(Shape=shell, LinearDeflection=0.1, AngularDeflection=0.523599, Relative=False)
mesh.Label="QuarterShell (Meshed)"

#Gui.Selection.addSelection(doc.Name, 'Mesh')
#Gui.runCommand('MeshPart_CreateFlatMesh',0)
wires = flattenMesh(mesh)
template = Part.show(wires[0], "Template") #only one outline here
#template = doc.getObject('Template')
#template.Label = 'Template'
templateVertices = [v.Point for v in template.Shape.Vertexes]
printVertices(templateVertices

jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Square to round transition

Post by jfc4120 »

I will experiment around with both. I am not advanced enough to figure out how to do inputs, the older basicad program I did around 1997, been a while.
edwilliams16
Veteran
Posts: 3109
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Square to round transition

Post by edwilliams16 »

For now you can just edit

Code: Select all

####inputs
squareSide = 40 * inchtomm
height = 15 * inchtomm
radius = 15 * inchtomm
nSides = 8
####
One can always add some dialog boxes or a spreadsheet input later.
It was easy enough to allow the circular opening to be tilted and/or offset.
Screen Shot 2022-07-20 at 6.30.37 PM.png
Screen Shot 2022-07-20 at 6.30.37 PM.png (17.65 KiB) Viewed 847 times

Code: Select all

import numpy as np
import Part, Mesh, MeshPart
import math
import numpy as np
import flatmesh

def printVertices(vs):
    App.Console.PrintMessage('Template Vertices')
    for i, v in enumerate(vs):
        App.Console.PrintMessage(f'{i}:  ({v.x:.2f},{v.y:.2f})\n')

def flattenMesh(obj):
    #https://github.com/FreeCAD/FreeCAD/blob/0d0bd1168bb96ded8dfe5ab3ca60588dec5d2949/src/Mod/MeshPart/Gui/MeshFlatteningCommand.py
    points = np.array([[i.x, i.y, i.z] for i in obj.Mesh.Points])
    faces = np.array([list(i) for i in  obj.Mesh.Topology[1]])
    flattener = flatmesh.FaceUnwrapper(points, faces)
    flattener.findFlatNodes(5, 0.95)
    boundaries = flattener.getFlatBoundaryNodes()
    wires = []
    for edge in boundaries:
        pi = Part.makePolygon([App.Vector(*node) for node in edge])
        wires.append(Part.Wire(pi))
    return wires

doc = App.ActiveDocument
#square on xy-plane, centered at origin: 
V3 = App.Vector
inchtomm = 25.4

####inputs
squareSide = 40 * inchtomm

height = 15 * inchtomm  #circle center location
xoffset = 3 *inchtomm
yoffset = 4 * inchtomm

circleRotationAngle = 20 #degrees
circleRotationAxis = V3(1, 0, 0)
radius = 15 * inchtomm
nSides = 8
####

#sV = [squareSide/2*V3(*v) for v in [(1,0,0), (1, 1, 0), (0, 1, 0)]]
eps =1e-6
sV = [squareSide/2*V3(*v) for v in [(1,0,0), (1, 1, 0), (0, 1, 0),(-1,1,0), (-1, 0, 0), (-1, -1, 0), (0, -1, 0), (1,-1, 0), (1-eps,0,0)]]
# sv[3]      sV[2]      sV[1]     
#
# sv[4]      O          sV[0], sv[8] seam
#
# sv[5]     sV[6]       sV[7]
# 4N-sided quarter circle
thetas = [2 * math.pi * j/(4 * nSides) for j in range(4*nSides + 1)]
thetas[-1] -= eps #seam
#create and locate circle
cV2 = [V3(radius * math.cos(t), radius * math.sin(t), 0) for t in thetas]
pl = App.Placement(V3(xoffset, yoffset, height), App.Rotation(circleRotationAxis, circleRotationAngle))
cV = [pl.multVec(v) for v in cV2]

faceList = []
for k in range(4):
    face = Part.Face(Part.makePolygon([sV[2*k], cV[nSides*k], sV[2*k + 1], sV[2*k]]))
    #Part.show(face)
    faceList.append(face)
    face = Part.Face(Part.makePolygon([sV[2*k + 1], cV[nSides*(k + 1)], sV[2*k + 2], sV[2*k + 1]]))
    #Part.show(face)
    faceList.append(face)

for k in range(4):
    for j in range(nSides):
        f = Part.Face(Part.makePolygon([sV[2*k +1], cV[nSides*k +j], cV[nSides*k + j+1], sV[2*k +1]]))
        #Part.show(f)
        faceList.append(f)

shell = Part.Shell(faceList)
Part.show(shell, "QuarterShell")


mesh = doc.addObject('Mesh::Feature','Mesh')
mesh.Mesh=MeshPart.meshFromShape(Shape=shell, LinearDeflection=0.1, AngularDeflection=0.523599, Relative=False)
mesh.Label="QuarterShell (Meshed)"

#Gui.Selection.addSelection(doc.Name, 'Mesh')
#Gui.runCommand('MeshPart_CreateFlatMesh',0)
wires = flattenMesh(mesh)
template = Part.show(wires[0], "Template") #only one outline here
#template = doc.getObject('Template')
#template.Label = 'Template'
templateVertices = [v.Point for v in template.Shape.Vertexes]
printVertices(templateVertices)

The vertex coordinates are output to the Report window. They should probably be rotated and translated in some way for your application. ie where should the origin be and what alignment?
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Square to round transition

Post by jfc4120 »

@edwilliams16 Holy Molly that is great. Me being new, I would have never figured out that, lines like:
face = Part.Face(Part.makePolygon([sV[2*k], cV[nSides*k], sV[2*k + 1], sV[2*k]]))

abd

mesh.Mesh=MeshPart.meshFromShape(Shape=shell, LinearDeflection=0.1, AngularDeflection=0.523599, Relative=False)

I understand the geometry, but not the freecad python yet. You are real good at this.

I am hoping there is one more you and @johnwang might help with. Late now, I will post it tomorrow, please look for it.

Also, if you get a chance, please make the above a Macro anyone can use in the Freecad program, only if you have time.

The next program I think may even throw you fora loop.

Edit::

Would the above code still work if it was rectangular to round and not just Square?
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Square to round transition

Post by jfc4120 »

@edwilliams16 in case you missed edited answer, could you tweak the python program an have rectangular to round also?

Also I am uploading a file where I tried the same techniques of upgrading faces, but did not get expect results, pleas have a look.
Attachments
ms2_unexpected.FCStd
(11.22 KiB) Downloaded 14 times
ms2_newtry.FCStd
(13.11 KiB) Downloaded 14 times
Post Reply