Skip to content

Instantly share code, notes, and snippets.

@nicoguaro
Last active May 16, 2016 20:30
Show Gist options
  • Save nicoguaro/ec588f3b135f31e2eb88 to your computer and use it in GitHub Desktop.
Save nicoguaro/ec588f3b135f31e2eb88 to your computer and use it in GitHub Desktop.
Scripts for automation of FreeCAD entities.
from __future__ import division
import FreeCAD as FC
import Part
import numpy as np
def gen_fibers(nlines, nlayers, radius, hor_gap, vert_gap, theta, length):
"""Generate fibers forming the Herringbone pattern
Parameters
----------
nlines : int
Number of curves per layer.
nlayers : int
Number of layers.
hor_gap : float (positive)
Horizontal gap between fibers.
vert_gap : float (positive)
Vertical gap between fibers/layers.
theta : float
Angle (in degrees) between consecutive layers.
length : float (positive)
Length of the fibers in the un-rotated/un-deformed
configuration.
Returns
-------
fibers : FreeCAD Fuse instance
Part with the fibers forming the Bouligand pattern.
"""
cyl = Part.makeCylinder(length/2, 60)
cyl.translate(FC.Vector(0, 0, -radius))
cyl2 = cyl
y_vec = np.arange(-length/2, length/2, hor_gap)
z_vec = np.arange(0, vert_gap*nlayers, vert_gap)
fiber_list = []
for cont_z, z in enumerate(z_vec):
angle = theta*cont_z
for y in y_vec:
fiber = Part.makeCylinder(radius, 1.04*length)
fiber.translate(FC.Vector(0, 0, -1.04*length/2))
fiber.rotate(FC.Vector(0, 0, 0),
FC.Vector(0, 1, 0), 90)
fiber.translate(FC.Vector(0, y, z))
fiber.rotate(FC.Vector(0, 0, 0),
FC.Vector(0, 0, 1), angle)
fiber_list.append(fiber.common(cyl))
cyl2 = cyl2.cut(fiber)
bouligand = Part.makeCompound(fiber_list)
return bouligand, cyl2
if __name__ == "__main__":
print("="*5 + "Bouligand example" + "="*5)
doc = FC.newDocument("bouligand_fine")
length = 60
radius = 0.3
vert_gap = 1.2
nlines = 48
hor_gap = length/nlines
nlayers = 37
theta = 180/10
fibers, cyl = gen_fibers(nlines, nlayers, radius, hor_gap, vert_gap,
theta, length)
Part.show(fibers)
Part.show(cyl)
print("Successful example!!")
"""
BSpline definition in FreeCAD
Every points is part of a list of FreeCAD.Vector class.
The example uses a hypotrochoid
https://en.wikipedia.org/wiki/Hypotrochoid
"""
import FreeCAD as FC
from FreeCAD import Base
import Draft
import numpy as np
from numpy import sin, cos, pi
def param_curve(t, R, r, d):
"""Coordinates of a hypotrochoid for parameters t, R, r and d"""
x = (R - r)*cos(t) + d*cos((R - r)/r*t)
y = (R - r)*sin(t) - d*sin((R - r)/r*t)
z = 3*sin(t)
return x, y, z
#%%
## Hypotrochoid
npts = 200
R = 5.
r = 3.
d = 5.
t = np.linspace(0, 6*pi, npts)
x_vec, y_vec, z_vec = param_curve(t, R, r, d)
pts = [FC.Vector(x_vec[k], y_vec[k], z_vec[k]) for k in range(npts)]
path = Draft.makeBSpline(pts, closed=True, support=None)
## Circle perpendicular to the parametric curve
rad = 0.5
vec = pts[1] - pts[0]
ang = vec.getAngle(FC.Vector(vec.x, vec.y, 0))*np.sign(vec.z)
ang = ang + pi/2
axis = vec.cross(FC.Vector(vec.x, vec.y, 0))
place = FC.Placement()
# Quaternion in FreeCAD
# q1 = sin(angle/2)*axis_1
# q2 = sin(angle/2)*axis_2
# q3 = sin(angle/2)*axis_3
# q4 = cos(angle/2)
place.Rotation = (sin(ang/2)*axis[0],
sin(ang/2)*axis[1],
sin(ang/2)*axis[2],
cos(ang/2))
place.Base = pts[0]
circle = Draft.makeCircle(radius=rad, placement=place)
## Sweep
doc = App.ActiveDocument
tube = doc.addObject('Part::Sweep','Sweep')
tube.Sections = circle
tube.Spine = path
tube.Solid = True
tube.Frenet = False
doc.recompute()
from __future__ import division
import FreeCAD as FC
import Draft
import Part
import Mesh
import numpy as np
from numpy import array, sin, cos, pi
def surf_fun(x, y, z, amp, kx=1, ky=1):
r"""Eggshell-like function
.. math::
f = A\sin(k_x \pi x) \sin(k_y \pi y) \enspace .
Parameters
----------
x : array_like
x coordinate.
y : array_like
y coordinate.
z : array_like
z coordinate, vertical offset of the function.
kx : float (optional)
Spatial (semi)-frequency in `x`.
ky : float (optional)
Spatial (semi)-frequency in `y`.
Returns
-------
f : array_like
Function value `f(x, y, z)`.
"""
f = amp*sin(kx*pi*x)*sin(ky*pi*y) + z
return f
def create_curve_data(x_vec, y, z, theta, amp, kx=1, ky=1):
"""Points to generate the curve in space
The points form the curve generated from the intersection
between the function and a plane forming an angle theta
with respect to the plane xz.
Parameters
----------
x_vec : array_like
Values for x in the un-rotated frame.
y : float
Value for y in the un-rotated frame.
z : float
Value for z in the current layer.
theta : float
Angle with respect to x (in radians).
amp : float
Amplitude of the function.
Returns
-------
pts : array_like
Function evaluated along the rotated line.
"""
xnew = np.cos(theta)*x_vec - np.sin(theta)*y
ynew = np.cos(theta)*y + np.sin(theta)*x_vec
znew = surf_fun(xnew, ynew, z, amp, kx=kx, ky=ky)
npts = len(xnew)
pts = [FC.Vector(xnew[k], ynew[k], znew[k])
for k in range(npts)]
return pts
def make_circle(radius, theta, pts):
"""Circle perpendicular to the curve defined by a point list
Parameters
----------
radius : float (positive)
Radius of the circle.
theta : float
Angle with respect to x (in radians).
pts : array
Point list that defines the curve in the space.
Returns
-------
circle : FreeCAD circle instance
Circle to be swept.
"""
vec = pts[1] - pts[0]
ang = vec.getAngle(FC.Vector(cos(theta), sin(theta), 0))*np.sign(vec.z)
ang = ang + pi/2
axis = array([sin(theta), -cos(theta), 0])
place = FC.Placement()
# Quaternion in FreeCAD
# q1 = sin(angle/2)*axis_1
# q2 = sin(angle/2)*axis_2
# q3 = sin(angle/2)*axis_3
# q4 = cos(angle/2)
place.Rotation = (sin(ang/2)*axis[0],
sin(ang/2)*axis[1],
sin(ang/2)*axis[2],
cos(ang/2))
place.Base = pts[0]
circle = Draft.makeCircle(radius=radius, placement=place)
return circle
def gen_fibers(npts, nlines, nlayers, amp, radius, hor_gap, vert_gap, theta,
length, kx=1, ky=1):
"""Generate fibers forming the Herringbone pattern
Parameters
----------
npts : int
Number of points along each curve.
nlines : int
Number of curves per layer.
nlayers : int
Number of layers.
amp : float
Amplitude of the function.
hor_gap : float (positive)
Horizontal gap between fibers.
vert_gap : float (positive)
Vertical gap between fibers/layers.
theta : float
Angle (in radians) between consecutive layers.
length : float (positive)
Length of the fibers in the un-rotated/un-deformed
configuration.
Returns
-------
fibers : FreeCAD Fuse instance
Part with the fibers forming the Herringbone pattern.
"""
doc = FC.ActiveDocument
path_group = doc.addObject("App::DocumentObjectGroup","Paths")
circ_group = doc.addObject("App::DocumentObjectGroup","Circles")
fiber_group = doc.addObject("App::DocumentObjectGroup","Fibers")
x_vec = np.linspace(-length/2, length/2, npts)
y_vec = np.arange(-length/2, length/2, hor_gap)
z_vec = np.arange(0, vert_gap*nlayers, vert_gap)
fiber_list = []
for cont_z, z in enumerate(z_vec):
angle = theta*cont_z
for cont_y, y in enumerate(y_vec):
pts = create_curve_data(x_vec, y, z, angle, amp, kx=kx, ky=ky)
rms = np.mean([(z - pt.z)**2 for pt in pts])
if rms <= amp*1e-6:
curve = Draft.makeBSpline([pts[0], pts[-1]], closed=False,
support=None)
else:
curve = Draft.makeBSpline(pts, closed=False, support=None)
circle = make_circle(radius, angle, pts)
fiber = doc.addObject('Part::Sweep','Fiber')
fiber.Sections = [circle]
fiber.Spine = (curve)
fiber.Solid = True
fiber.Frenet = False
fiber_list.append(fiber)
# Add features to groups
fiber_group.addObject(fiber)
path_group.addObject(curve)
circ_group.addObject(circle)
herring = doc.addObject("Part::Compound","Compound")
herring.Links = fiber_list
return herring
if __name__ == "__main__":
print("="*5 + "Herringbone example" + "="*5)
doc = FC.newDocument("herring_fine")
npts = 41
length = 60
amp = 3.0
radius = 0.3
vert_gap = 1.2
nlines = 48
hor_gap = length/nlines
nlayers = 40
theta = pi/10
herring = gen_fibers(npts, nlines, nlayers, amp, radius, hor_gap, vert_gap,
theta, length, kx=0.1, ky=0.1)
doc.recompute()
# Save file
fname = "D:/workspace/vol_printing/examples/herrring_fine.FCStd"
doc.saveAs(fname)
print("Successful example!!")
from __future__ import division
import FreeCAD as FC
import Part
import numpy as np
object_name = "Shape"
vol = FC.ActiveDocument.getObject(object_name).Shape.Volume
print(np.round(vol,4))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment