freecad-cam/Mod/Draft/draftgeoutils/geo_arrays.py
2026-02-01 01:59:24 +01:00

201 lines
6.4 KiB
Python

# ***************************************************************************
# * Copyright (c) 2020 three_d *
# * Copyright (c) 2020 Eliud Cabrera Castillo <e.cabrera-castillo@tum.de> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""Provides various functions to work with arrays.
One of the functions is used to create a `twisted array` object.
See `draftobjects/twistedarray.py`.
This array was developed in order to build a `twisted bridge` object.
See https://forum.freecad.org/viewtopic.php?f=23&t=49617
"""
## @package geo_arrays
# \ingroup draftgeoutils
# \brief Provides various functions to work with arrays.
import lazy_loader.lazy_loader as lz
import FreeCAD as App
from draftutils.messages import _msg
# Delay import of module until first use because it is heavy
Part = lz.LazyLoader("Part", globals(), "Part")
## \addtogroup draftgeoutils
# @{
def print_places(places, title="Places"):
"""Print a vector with a title."""
_msg(12*"-")
_msg(title)
for i in places:
_msg("{}".format(i))
def get_init_values(path, count=6):
"""Set values needed to create the array."""
norm = App.Vector(0, 0, 1)
# Currently this works with a sketch that has a single edge.
# Here we need a more general function to extract all edges from a shape,
# so that the array uses all of them.
edge = path.Shape.Edges[0]
edge_length = edge.Length
n = max((count - 1), 1)
step = edge_length / n
inc = 360 / n
return norm, edge, step, inc
def get_n_params(edge, number, step, norm):
"""Get the parameters needed in each iteration."""
parameter = edge.getParameterByLength(number * step)
v0 = edge.valueAt(parameter)
tan = edge.tangentAt(parameter).normalize()
binorm = tan.cross(norm).normalize()
rot = App.Rotation(binorm, tan, norm)
return v0, tan, rot
def get_twisted_placements(path, count=15, rot_factor=0.25):
"""Get the placements of the twisted array elements."""
count = max(count, 1)
(norm, edge,
step, inc) = get_init_values(path, count)
increment = 0
places = []
params = []
for number in range(count):
v0, tan, rot = get_n_params(edge, number, step, norm)
angle = increment * rot_factor
place = App.Placement(v0, tan, angle)
place.Rotation = place.Rotation * rot
places.append(place)
params.append((v0, tan, angle, rot))
increment += inc
return places, params
def get_twisted_array_shape(base, path, count=15, rot_factor=0.25):
"""Get the twisted array shape as a compound."""
places, _ = get_twisted_placements(path,
count=count,
rot_factor=rot_factor)
shapes, _ = create_frames(base, places)
shape = Part.makeCompound(shapes)
return shape
def get_twisted_bridge_shape(base, path, count=15, rot_factor=0.25,
width=100,
thickness=10):
"""Get the twisted bridge array shape as a compound."""
compound = list()
places, _ = get_twisted_placements(path,
count=count,
rot_factor=rot_factor)
# print_places(places)
shapes, profiles = create_frames(base, places)
compound.extend(shapes)
tunnel = make_tunnel(path, profiles)
compound.append(tunnel)
# size = base.Shape.Wires[-1].BoundBox.XLength * 0.9
# size = int(size)
# thickness = size/12.0
walkway = make_walkway(path, width, thickness)
compound.append(walkway)
shape = Part.makeCompound(compound)
return shape
def create_frames(obj, places):
"""Create the frames from the placements."""
len_wires = len(obj.Shape.Wires)
frames = list()
profiles = list()
for i in places:
frame = obj.Shape.copy()
frame.Placement = i
frames.append(frame)
profiles.append(frame.Wires[len_wires - 1])
return frames, profiles
def make_tunnel(path, profiles):
"""Create the tunnel shape."""
edge = path.Shape.Edges[0]
wire = Part.Wire(edge)
sweep = wire.makePipeShell(profiles)
return sweep
def make_walkway(path, width=100, thickness=10):
"""Construct the walkway of the twisted bridge array."""
spine = path.Shape.Edges[0]
half_size = width/2
offset_height = thickness
norm1 = App.Vector(0, 0, 1)
v1 = spine.valueAt(1)
tan1 = spine.tangentAt(1).normalize()
binorm1 = tan1.cross(norm1)
place = App.Placement()
place.Rotation = App.Rotation(binorm1, norm1, tan1)
place.move(v1 - App.Vector(0, 0, 3 * offset_height))
plane = Part.makePlane(width, thickness,
App.Vector(-half_size, -2 * offset_height, 0))
face = Part.Face(plane)
face.Placement = place.multiply(face.Placement)
wire = Part.Wire(spine)
sweep = wire.makePipe(face)
return sweep
## @}