Merge pull request #7506 from Roy-043/Draft-Fix-several-path-array-issues
Draft: Fix several path array issues
This commit is contained in:
@@ -336,35 +336,40 @@ class PathArray(DraftLink):
|
||||
self.show_and_hide(obj, prop)
|
||||
|
||||
def show_and_hide(self, obj, prop):
|
||||
"""Show and hide the properties depending on the touched property."""
|
||||
# The minus sign removes the Hidden property (show)
|
||||
if prop == "Align":
|
||||
"""Show and hide the properties depending on the touched property.
|
||||
|
||||
Note that when the array is created, some properties will change
|
||||
more than once in a seemingly random order.
|
||||
"""
|
||||
# The minus sign removes the Hidden property (show).
|
||||
if prop in ("Align", "AlignMode"):
|
||||
|
||||
# Check if all referenced properties are available:
|
||||
for pr in ("Align", "AlignMode", "ForceVertical",
|
||||
"VerticalVector", "TangentVector"):
|
||||
if not hasattr(obj, pr):
|
||||
return
|
||||
|
||||
if obj.Align:
|
||||
for pr in ("AlignMode", "ForceVertical", "VerticalVector",
|
||||
"TangentVector"):
|
||||
obj.setPropertyStatus(pr, "-Hidden")
|
||||
obj.setPropertyStatus("AlignMode", "-Hidden")
|
||||
|
||||
if obj.AlignMode == "Frenet":
|
||||
for pr in ("ForceVertical", "VerticalVector"):
|
||||
obj.setPropertyStatus(pr, "Hidden")
|
||||
else:
|
||||
for pr in ("ForceVertical", "VerticalVector"):
|
||||
obj.setPropertyStatus(pr, "-Hidden")
|
||||
|
||||
if obj.AlignMode == "Tangent":
|
||||
obj.setPropertyStatus("TangentVector", "-Hidden")
|
||||
else:
|
||||
obj.setPropertyStatus("TangentVector", "Hidden")
|
||||
|
||||
else:
|
||||
for pr in ("AlignMode", "ForceVertical", "VerticalVector",
|
||||
"TangentVector"):
|
||||
for pr in ("AlignMode", "ForceVertical",
|
||||
"VerticalVector", "TangentVector"):
|
||||
obj.setPropertyStatus(pr, "Hidden")
|
||||
|
||||
if prop == "AlignMode":
|
||||
if obj.AlignMode == "Original":
|
||||
for pr in ("ForceVertical", "VerticalVector"):
|
||||
obj.setPropertyStatus(pr, "-Hidden")
|
||||
|
||||
obj.setPropertyStatus("TangentVector", "Hidden")
|
||||
|
||||
elif obj.AlignMode == "Frenet":
|
||||
for pr in ("ForceVertical", "VerticalVector",
|
||||
"TangentVector"):
|
||||
obj.setPropertyStatus(pr, "Hidden")
|
||||
|
||||
elif obj.AlignMode == "Tangent":
|
||||
for pr in ("ForceVertical", "VerticalVector",
|
||||
"TangentVector"):
|
||||
obj.setPropertyStatus(pr, "-Hidden")
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
"""Execute code when the document is restored.
|
||||
|
||||
@@ -412,79 +417,55 @@ _PathArray = PathArray
|
||||
|
||||
|
||||
def placements_on_path(shapeRotation, pathwire, count, xlate, align,
|
||||
mode='Original', forceNormal=False,
|
||||
mode="Original", forceNormal=False,
|
||||
normalOverride=None):
|
||||
"""Calculate the placements of a shape along a given path.
|
||||
|
||||
Each copy will be distributed evenly.
|
||||
Copies will be distributed evenly.
|
||||
"""
|
||||
closedpath = DraftGeomUtils.isReallyClosed(pathwire)
|
||||
normal = DraftGeomUtils.get_normal(pathwire)
|
||||
# for backward compatibility with previous getNormal implementation
|
||||
if normal is None:
|
||||
normal = App.Vector(0, 0, 1)
|
||||
if mode == "Frenet":
|
||||
forceNormal = False
|
||||
|
||||
if forceNormal and normalOverride:
|
||||
normal = normalOverride
|
||||
else:
|
||||
normal = DraftGeomUtils.get_normal(pathwire)
|
||||
if normal is None:
|
||||
normal = App.Vector(0, 0, 1)
|
||||
|
||||
path = Part.__sortEdges__(pathwire.Edges)
|
||||
ends = []
|
||||
cdist = 0
|
||||
|
||||
for e in path: # find cumulative edge end distance
|
||||
# find cumulative edge end distance
|
||||
cdist = 0
|
||||
ends = []
|
||||
for e in path:
|
||||
cdist += e.Length
|
||||
ends.append(cdist)
|
||||
|
||||
step = cdist / (count if DraftGeomUtils.isReallyClosed(pathwire) else count - 1)
|
||||
remains = 0
|
||||
travel = 0
|
||||
placements = []
|
||||
|
||||
# place the start shape
|
||||
pt = path[0].Vertexes[0].Point
|
||||
_place = calculate_placement(shapeRotation,
|
||||
path[0], 0, pt, xlate, align, normal,
|
||||
mode, forceNormal)
|
||||
placements.append(_place)
|
||||
|
||||
# closed path doesn't need shape on last vertex
|
||||
if not closedpath:
|
||||
# place the end shape
|
||||
pt = path[-1].Vertexes[-1].Point
|
||||
_place = calculate_placement(shapeRotation,
|
||||
path[-1], path[-1].Length,
|
||||
pt, xlate, align, normal,
|
||||
mode, forceNormal)
|
||||
placements.append(_place)
|
||||
|
||||
if count < 3:
|
||||
return placements
|
||||
|
||||
# place the middle shapes
|
||||
if closedpath:
|
||||
stop = count
|
||||
else:
|
||||
stop = count - 1
|
||||
step = float(cdist) / stop
|
||||
remains = 0
|
||||
travel = step
|
||||
for i in range(1, stop):
|
||||
for i in range(0, count):
|
||||
# which edge in path should contain this shape?
|
||||
# avoids problems with float math travel > ends[-1]
|
||||
iend = len(ends) - 1
|
||||
|
||||
for j in range(0, len(ends)):
|
||||
if travel <= ends[j]:
|
||||
iend = j
|
||||
break
|
||||
else:
|
||||
# avoids problems with float math travel > ends[-1]
|
||||
iend = len(ends) - 1
|
||||
|
||||
# place shape at proper spot on proper edge
|
||||
remains = ends[iend] - travel
|
||||
offset = path[iend].Length - remains
|
||||
pt = path[iend].valueAt(get_parameter_from_v0(path[iend], offset))
|
||||
|
||||
_place = calculate_placement(shapeRotation,
|
||||
path[iend], offset,
|
||||
pt, xlate, align, normal,
|
||||
mode, forceNormal)
|
||||
placements.append(_place)
|
||||
place = calculate_placement(shapeRotation,
|
||||
path[iend], offset,
|
||||
pt, xlate, align, normal,
|
||||
mode, forceNormal)
|
||||
placements.append(place)
|
||||
|
||||
travel += step
|
||||
|
||||
@@ -495,86 +476,81 @@ calculatePlacementsOnPath = placements_on_path
|
||||
|
||||
|
||||
def calculate_placement(globalRotation,
|
||||
edge, offset, RefPt, xlate, align, normal=None,
|
||||
mode='Original', overrideNormal=False):
|
||||
"""Orient shape to a local coordinate system (tangent, normal, binormal).
|
||||
|
||||
Orient shape at parameter offset, normally length.
|
||||
edge, offset, RefPt, xlate, align,
|
||||
normal=App.Vector(0.0, 0.0, 1.0),
|
||||
mode="Original", overrideNormal=False):
|
||||
"""Orient shape in the local coordinate system at parameter offset.
|
||||
|
||||
http://en.wikipedia.org/wiki/Euler_angles (previous version)
|
||||
http://en.wikipedia.org/wiki/Quaternions
|
||||
"""
|
||||
# Start with a null Placement so the translation goes to the right place.
|
||||
# Then apply the global orientation.
|
||||
# Default Placement:
|
||||
placement = App.Placement()
|
||||
placement.Rotation = globalRotation
|
||||
placement.Base = RefPt + xlate
|
||||
|
||||
placement.move(RefPt + xlate)
|
||||
if not align:
|
||||
return placement
|
||||
|
||||
nullv = App.Vector(0, 0, 0)
|
||||
defNormal = App.Vector(0.0, 0.0, 1.0)
|
||||
if normal:
|
||||
defNormal = normal
|
||||
tol = 1e-6 # App.Rotation() tolerance is 1e-7. Shorter vectors are ignored.
|
||||
nullv = App.Vector()
|
||||
|
||||
try:
|
||||
t = edge.tangentAt(get_parameter_from_v0(edge, offset))
|
||||
t.normalize()
|
||||
except Exception:
|
||||
_wrn(translate("draft","Cannot calculate path tangent. Copy not aligned."))
|
||||
t = edge.tangentAt(get_parameter_from_v0(edge, offset))
|
||||
if t.isEqual(nullv, tol):
|
||||
_wrn(translate("draft", "Length of tangent vector is zero. Copy not aligned."))
|
||||
return placement
|
||||
|
||||
if mode in ('Original', 'Tangent'):
|
||||
if normal is None:
|
||||
n = defNormal
|
||||
else:
|
||||
n = normal
|
||||
n.normalize()
|
||||
# If the length of the normal is zero or if it is parallel to the tangent,
|
||||
# we make the vectors equal (n = t). The App.Rotation() algorithm will
|
||||
# then replace the normal with a default axis.
|
||||
# For the vector with the lowest App.Rotation() priority we use a null
|
||||
# vector. Calculating this binormal would not make sense in the mentioned
|
||||
# cases. And in all other cases calculating it is not necessary as
|
||||
# App.Rotation() will ignore it.
|
||||
|
||||
try:
|
||||
b = t.cross(n)
|
||||
b.normalize()
|
||||
except Exception:
|
||||
# weird special case, tangent and normal parallel
|
||||
b = nullv
|
||||
_wrn(translate("draft","Tangent and normal are parallel. Copy not aligned."))
|
||||
return placement
|
||||
if mode in ("Original", "Tangent"):
|
||||
n = normal
|
||||
if n.isEqual(nullv, tol):
|
||||
_wrn(translate("draft", "Length of normal vector is zero. Using a default axis instead."))
|
||||
n = t
|
||||
else:
|
||||
n_nor = n.normalize()
|
||||
t_nor = t.normalize()
|
||||
if n_nor.isEqual(t_nor, tol) or n_nor.isEqual(t_nor.negative(), tol):
|
||||
_wrn(translate("draft", "Tangent and normal vectors are parallel. Normal replaced by a default axis."))
|
||||
n = t
|
||||
|
||||
if overrideNormal:
|
||||
priority = "XZY"
|
||||
newRot = App.Rotation(t, b, n, priority) # t/x, b/y, n/z
|
||||
newRot = App.Rotation(t, nullv, n, "XZY") # priority = "XZY"
|
||||
else:
|
||||
# must follow X, try to follow Z, Y is what it is
|
||||
priority = "XZY"
|
||||
newRot = App.Rotation(t, n, b, priority)
|
||||
newRot = App.Rotation(t, n, nullv, "XYZ") # priority = "XYZ"
|
||||
|
||||
elif mode == 'Frenet':
|
||||
elif mode == "Frenet":
|
||||
try:
|
||||
n = edge.normalAt(get_parameter_from_v0(edge, offset))
|
||||
n.normalize()
|
||||
except App.Base.FreeCADError: # no/infinite normals here
|
||||
n = defNormal
|
||||
_msg(translate("draft","Cannot calculate path normal, using default."))
|
||||
except App.Base.FreeCADError: # no/infinite normals here
|
||||
_wrn(translate("draft", "Cannot calculate normal vector. Using the default normal instead."))
|
||||
n = normal
|
||||
|
||||
try:
|
||||
b = t.cross(n)
|
||||
b.normalize()
|
||||
except Exception:
|
||||
b = nullv
|
||||
_wrn(translate("draft","Cannot calculate path binormal. Copy not aligned."))
|
||||
return placement
|
||||
if n.isEqual(nullv, tol):
|
||||
_wrn(translate("draft", "Length of normal vector is zero. Using a default axis instead."))
|
||||
n = t
|
||||
else:
|
||||
n_nor = n.normalize()
|
||||
t_nor = t.normalize()
|
||||
if n_nor.isEqual(t_nor, tol) or n_nor.isEqual(t_nor.negative(), tol):
|
||||
_wrn(translate("draft", "Tangent and normal vectors are parallel. Normal replaced by a default axis."))
|
||||
n = t
|
||||
|
||||
newRot = App.Rotation(t, n, nullv, "XYZ") # priority = "XYZ"
|
||||
|
||||
priority = "XZY"
|
||||
newRot = App.Rotation(t, n, b, priority) # t/x, n/y, b/z
|
||||
else:
|
||||
_msg(translate("draft","AlignMode {} is not implemented").format(mode))
|
||||
_msg(translate("draft", "AlignMode {} is not implemented").format(mode))
|
||||
return placement
|
||||
|
||||
# Have valid tangent, normal, binormal
|
||||
newGRot = newRot.multiply(globalRotation)
|
||||
|
||||
placement.Rotation = newGRot
|
||||
placement.Base = RefPt + newRot.multVec(xlate)
|
||||
return placement
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user