Improve make_sketch and geometric related functions

This commit is contained in:
marioalexis
2020-10-09 23:13:46 -03:00
committed by Yorik van Havre
parent 675fdd7c39
commit aa88f3ded0
8 changed files with 435 additions and 194 deletions

View File

@@ -68,11 +68,16 @@ from draftgeoutils.general import (precision,
from draftgeoutils.geometry import (findPerpendicular,
findDistance,
getSplineNormal,
get_spline_normal,
getNormal,
get_normal,
getRotation,
isPlanar,
is_planar,
calculatePlacement,
mirror)
mirror,
are_coplanar,
is_straight_line)
from draftgeoutils.edges import (findEdge,
orientEdge,

View File

@@ -111,15 +111,27 @@ def isSameLine(e1, e2):
return False
def isLine(bspline):
def is_line(bspline):
"""Return True if the given BSpline curve is a straight line."""
step = bspline.LastParameter/10
b = bspline.tangent(0)
for i in range(10):
if bspline.tangent(i * step) != b:
return False
return True
# previous implementation may fail for a multipole striaght spline due
# a second order error in tolerance, wich introduce a difference of 1e-14
# in the values of the tangents. Also, may fail on a periodic spline.
# step = bspline.LastParameter/10
# b = bspline.tangent(0)
#
# for i in range(10):
# if bspline.tangent(i * step) != b:
# return False
start_point = bspline.StartPoint
end_point = bspline.EndPoint
dist_start_end = end_point.distanceToPoint(start_point)
if (dist_start_end == bspline.length()) < 1e-7:
return True
return False
def invert(shape):
@@ -209,4 +221,8 @@ def getTangent(edge, from_point=None):
return None
# compatibility layer
isLine = is_line
## @}

View File

@@ -196,70 +196,57 @@ def findDistance(point, edge, strict=False):
return None
def getSplineNormal(edge):
def get_spline_normal(edge, tol=-1):
"""Find the normal of a BSpline edge."""
startPoint = edge.valueAt(edge.FirstParameter)
endPoint = edge.valueAt(edge.LastParameter)
midParameter = (edge.FirstParameter
+ (edge.LastParameter - edge.FirstParameter) / 2)
midPoint = edge.valueAt(midParameter)
v1 = midPoint - startPoint
v2 = midPoint - endPoint
n = v1.cross(v2)
n.normalize()
return n
def getNormal(shape):
"""Find the normal of a shape or list of points, if possible."""
if isinstance(shape, (list, tuple)):
if len(shape) >= 3:
v1 = shape[1].sub(shape[0])
v2 = shape[2].sub(shape[0])
n = v2.cross(v1)
if n.Length:
return n
if is_straight_line(shape, tol):
return None
n = App.Vector(0, 0, 1)
if shape.isNull():
return n
if (shape.ShapeType == "Face") and hasattr(shape, "normalAt"):
n = shape.copy().normalAt(0.5, 0.5)
elif shape.ShapeType == "Edge":
if geomType(shape.Edges[0]) in ["Circle", "Ellipse"]:
n = shape.Edges[0].Curve.Axis
elif (geomType(shape.Edges[0]) == "BSplineCurve"
or geomType(shape.Edges[0]) == "BezierCurve"):
n = getSplineNormal(shape.Edges[0])
plane = edge.findPlane(tol)
if plane:
normal = plane.Axis
return normal
else:
for e in shape.Edges:
if geomType(e) in ["Circle", "Ellipse"]:
n = e.Curve.Axis
break
elif (geomType(e) == "BSplineCurve"
or geomType(e) == "BezierCurve"):
n = getSplineNormal(e)
break
return None
e1 = vec(shape.Edges[0])
for i in range(1, len(shape.Edges)):
e2 = vec(shape.Edges[i])
if 0.1 < abs(e1.getAngle(e2)) < 3.14:
n = e1.cross(e2).normalize()
break
def get_normal(shape, tol=-1):
"""Find the normal of a shape or list of points, if possible."""
# for points
if isinstance(shape, (list, tuple)):
if len(shape) <= 2:
return None
else:
poly = Part.makePolygon(shape)
if is_straight_line(poly, tol):
return None
plane = poly.findPlane(tol)
if plane:
normal = plane.Axis
return normal
else:
return None
# for shapes
if is_straight_line(shape, tol):
return None
else:
plane = find_plane(shape, tol)
if plane:
normal = plane.Axis
else:
return None
# Check the 3D view to flip the normal if the GUI is available
if App.GuiUp:
vdir = gui_utils.get_3d_view().getViewDirection()
if n.getAngle(vdir) < 0.78:
n = n.negative()
v_dir = gui_utils.get_3d_view().getViewDirection()
if normal.getAngle(v_dir) < 0.78:
normal = normal.negative()
if not n.Length:
return None
return n
return normal
def getRotation(v1, v2=App.Vector(0, 0, 1)):
@@ -275,34 +262,196 @@ def getRotation(v1, v2=App.Vector(0, 0, 1)):
return App.Rotation(axis, angle)
def isPlanar(shape):
def is_planar(shape, tol=-1):
"""Return True if the given shape or list of points is planar."""
n = getNormal(shape)
if not n:
return False
# for points
if isinstance(shape, list):
if len(shape) <= 3:
return True
else:
for v in shape[3:]:
pv = v.sub(shape[0])
rv = DraftVecUtils.project(pv, n)
if not DraftVecUtils.isNull(rv):
return False
else:
if len(shape.Vertexes) <= 3:
return True
poly = Part.makePolygon(shape)
if is_straight_line(poly, tol):
return True
plane = poly.findPlane(tol)
if plane:
return True
else:
return False
for p in shape.Vertexes[1:]:
pv = p.Point.sub(shape.Vertexes[0].Point)
rv = DraftVecUtils.project(pv, n)
if not DraftVecUtils.isNull(rv):
# for shapes
# because Part.Shape.findPlane return None for Vertex and straight edges
if shape.ShapeType == "Vertex":
return True
if is_straight_line(shape, tol):
return True
plane = find_plane(shape, tol)
if plane:
return True
else:
return False
def is_straight_line(shape, tol=-1):
"""Return True if shape is a straight line.
function used in other methods because Part.Shape.findPlane assign a
plane and normal to straight wires creating priviliged directions
and to deal with straight wires with overlapped edges."""
if len(shape.Faces) != 0:
return False
if len(shape.Edges) == 0:
return False
if len(shape.Edges) >= 1:
start_edge = shape.Edges[0]
dir_start_edge = start_edge.tangentAt(start_edge.FirstParameter)
#set tolerance
if tol <=0:
err = shape.globalTolerance(tol)
else:
err = tol
for edge in shape.Edges:
first_point = edge.firstVertex().Point
last_point = edge.lastVertex().Point
dir_edge = edge.tangentAt(edge.FirstParameter)
# chek if edge is curve or no parallel to start_edge
# because sin(x) = x + O(x**3), for small angular deflection it's
# enough use the cross product of directions (or dot with a normal)
if (abs(edge.Length - first_point.distanceToPoint(last_point)) > err
or dir_start_edge.cross(dir_edge).Length > err):
return False
return True
def are_coplanar(shape_a, shape_b, tol=-1):
"""Return True if exist a plane containing both shapes."""
if not is_planar(shape_a, tol) or not is_planar(shape_b, tol):
return False
if shape_a.isEqual(shape_b):
return True
plane_a = find_plane(shape_a, tol)
plane_b = find_plane(shape_b, tol)
#set tolerance
if tol <=0:
err = 1e-7
else:
err = tol
if plane_a and plane_b:
normal_a = plane_a.Axis
normal_b = plane_b.Axis
proj = plane_a.projectPoint(plane_b.Position)
if (normal_a.cross(normal_b).Length > err
or plane_b.Position.sub(proj).Length > err):
return False
else:
return True
elif plane_a and not plane_b:
normal_a = plane_a.Axis
for vertex in shape_b.Vertexes:
dir_ver_b = vertex.Point.sub(plane_a.Position).normalize()
if abs(normal_a.dot(dir_ver_b)) > err:
proj = plane_a.projectPoint(vertex.Point)
if vertex.Point.sub(proj).Length > err:
return False
return True
elif plane_b and not plane_a:
normal_b = plane_b.Axis
for vertex in shape_a.Vertexes:
dir_ver_a = vertex.Point.sub(plane_b.Position).normalize()
if abs(normal_b.dot(dir_ver_a)) > err:
proj = plane_b.projectPoint(vertex.Point)
if vertex.Point.sub(proj).Length > err:
return False
return True
# not normal_a and not normal_b:
else:
points_a = [vertex.Point for vertex in shape_a.Vertexes]
points_b = [vertex.Point for vertex in shape_b.Vertexes]
poly = Part.makePolygon(points_a + points_b)
if is_planar(poly, tol):
return True
else:
return False
def get_spline_surface_normal(shape, tol=-1):
"""Check if shape formed by BSpline surfaces is planar and get normal.
If shape is not planar return None."""
if len(shape.Faces) == 0:
return None
#set tolerance
if tol <=0:
err = shape.globalTolerance(tol)
else:
err = tol
first_surf = shape.Faces[0].Surface
if not first_surf.isPlanar(tol):
return None
# find bounds of first_surf
u0, u1, v0, v1 = first_surf.bounds()
u = (u0 + u1)/2
v = (v0 + v1)/2
first_normal = first_surf.normal(u, v)
# chek if all faces are planar and parallel
for face in shape.Faces:
surf = face.Surface
if not surf.isPlanar(tol):
return None
u0, u1, v0, v1 = surf.bounds()
u = (u0 + u1)/2
v = (v0 + v1)/2
surf_normal = surf.normal(u, v)
if first_normal.cross(surf_normal).Length > err:
return None
normal = first_normal
return normal
def find_plane(shape, tol=-1):
"""Find the plane containing the shape if possible.
Use this function as a workaround due Part.Shape.findPlane
fail to find plane on BSpline surfaces."""
if shape.ShapeType == "Vertex":
return None
if is_straight_line(shape, tol):
return None
plane = shape.findPlane(tol)
if plane:
return plane
elif len(shape.Faces) >= 1:
# in case shape have BSpline surfaces
normal = get_spline_surface_normal(shape, tol)
if normal:
position = shape.CenterOfMass
return Part.Plane(position, normal)
else:
return None
else:
return None
def calculatePlacement(shape):
"""Return a placement located in the center of gravity of the shape.
@@ -310,11 +459,14 @@ def calculatePlacement(shape):
of gravity of the shape, and oriented towards the shape's normal.
Otherwise, it returns a null placement.
"""
if not isPlanar(shape):
if not is_planar(shape):
return App.Placement()
pos = shape.BoundBox.Center
norm = getNormal(shape)
norm = get_normal(shape)
# for backward compatibility with previous getNormal implementation
if norm == None:
norm = App.Vector(0, 0, 1)
pla = App.Placement()
pla.Base = pos
r = getRotation(norm)
@@ -337,4 +489,14 @@ def mirror(point, edge):
else:
return None
#compatibility layer
getSplineNormal = get_spline_normal
getNormal = get_normal
isPlanar = is_planar
## @}

View File

@@ -32,7 +32,7 @@ import FreeCAD as App
import DraftVecUtils
from draftgeoutils.general import geomType, vec
from draftgeoutils.geometry import getNormal
from draftgeoutils.geometry import get_normal
from draftgeoutils.wires import isReallyClosed
from draftgeoutils.intersections import wiresIntersect, connect
@@ -223,7 +223,10 @@ def offsetWire(wire, dvec, bind=False, occ=False,
if normal:
norm = normal
else:
norm = getNormal(wire) # norm = Vector(0, 0, 1)
norm = get_normal(wire) # norm = Vector(0, 0, 1)
# for backward compatibility with previous getNormal implementation
if norm == None:
norm = App.Vector(0, 0, 1)
closed = isReallyClosed(wire)
nedges = []

View File

@@ -29,11 +29,12 @@
import math
import lazy_loader.lazy_loader as lz
import FreeCAD as App
import DraftVecUtils
import WorkingPlane
from draftgeoutils.general import geomType, vec, precision
from draftgeoutils.geometry import getNormal
from draftgeoutils.geometry import get_normal
from draftgeoutils.edges import findMidpoint, isLine
# Delay import of module until first use because it is heavy
@@ -157,9 +158,10 @@ def findWiresOld(edges):
def flattenWire(wire):
"""Force a wire to get completely flat along its normal."""
n = getNormal(wire)
if not n:
return
n = get_normal(wire)
# for backward compatibility with previous getNormal implementation
if n == None:
n = App.Vector(0, 0, 1)
o = wire.Vertexes[0].Point
plane = WorkingPlane.plane()

View File

@@ -36,7 +36,6 @@ into several individual Draft objects.
## \addtogroup draftguitools
# @{
from PySide.QtCore import QT_TRANSLATE_NOOP
import FreeCADGui as Gui
import Draft_rc
import draftguitools.gui_base_original as gui_base_original
@@ -90,7 +89,8 @@ class Draft2Sketch(gui_base_original.Modifier):
for obj in sel:
if obj.isDerivedFrom("Sketcher::SketchObject"):
allDraft = False
elif obj.isDerivedFrom("Part::Part2DObjectPython"):
elif (obj.isDerivedFrom("Part::Part2DObjectPython")
or obj.isDerivedFrom("Part::Feature")):
allSketches = False
else:
allDraft = False
@@ -141,12 +141,13 @@ class Draft2Sketch(gui_base_original.Modifier):
if obj.isDerivedFrom("Sketcher::SketchObject"):
_cmd_list.append("obj" + str(n) + " = " + _cmd_df)
elif obj.isDerivedFrom("Part::Part2DObjectPython"):
_cmd_list.append("obj" + str(n) + " = " + _cmd_sk)
elif obj.isDerivedFrom("Part::Feature"):
# if (len(obj.Shape.Wires) == 1
# or len(obj.Shape.Edges) == 1):
elif (obj.isDerivedFrom("Part::Part2DObjectPython")
or obj.isDerivedFrom("Part::Feature")):
_cmd_list.append("obj" + str(n) + " = " + _cmd_sk)
#elif obj.isDerivedFrom("Part::Feature"):
# # if (len(obj.Shape.Wires) == 1
# # or len(obj.Shape.Edges) == 1):
# _cmd_list.append("obj" + str(n) + " = " + _cmd_sk)
n += 1
_cmd_list.append('FreeCAD.ActiveDocument.recompute()')
self.commit(translate("draft", "Convert Draft/Sketch"),

View File

@@ -38,15 +38,16 @@ import draftutils.gui_utils as gui_utils
from draftutils.translate import translate
def make_sketch(objectslist, autoconstraints=False, addTo=None,
delete=False, name="Sketch", radiusPrecision=-1):
"""makeSketch(objectslist,[autoconstraints],[addTo],[delete],[name],[radiusPrecision])
def make_sketch(objects_list, autoconstraints=False, addTo=None,
delete=False, name="Sketch", radiusPrecision=-1, tol=1e-3):
"""makeSketch(objects_list,[autoconstraints],[addTo],[delete],
[name],[radiusPrecision],[tol])
Makes a Sketch objectslist with the given Draft objects.
Makes a Sketch objects_list with the given Draft objects.
Parameters
----------
objectlist: can be single or list of objects of Draft type objects,
objects_list: can be single or list of objects of Draft type objects,
Part::Feature, Part.Shape, or mix of them.
autoconstraints(False): if True, constraints will be automatically added to
@@ -57,14 +58,17 @@ def make_sketch(objectslist, autoconstraints=False, addTo=None,
delete(False): if True, the original object will be deleted.
If set to a string 'all' the object and all its linked object will be
deleted
deleted.
name('Sketch'): the name for the new sketch object
name('Sketch'): the name for the new sketch object.
radiusPrecision(-1): If <0, disable radius constraint. If =0, add indiviaul
radiusPrecision(-1): If <0, disable radius constraint. If =0, add individual
radius constraint. If >0, the radius will be rounded according to this
precision, and 'Equal' constraint will be added to curve with equal
radius within precision.
tol(1e-3): Tolerance used to check if the shapes are planar and coplanar.
Consider change to tol=-1 for a more accurate analisis.
"""
if not App.ActiveDocument:
@@ -75,24 +79,72 @@ def make_sketch(objectslist, autoconstraints=False, addTo=None,
from Sketcher import Constraint
import Sketcher
StartPoint = 1
EndPoint = 2
MiddlePoint = 3
start_point = 1
end_point = 2
middle_point = 3
deletable = None
if not isinstance(objectslist,(list,tuple)):
objectslist = [objectslist]
for obj in objectslist:
if App.GuiUp:
v_dir = gui_utils.get_3d_view().getViewDirection()
else:
v_dir = App.Base.Vector(0,0,-1)
# lists to accumulate shapes with defined normal and undefined normal
shape_norm_yes = list()
shape_norm_no = list()
if not isinstance(objects_list,(list,tuple)):
objects_list = [objects_list]
for obj in objects_list:
if isinstance(obj,Part.Shape):
shape = obj
elif not hasattr(obj,'Shape'):
App.Console.PrintError(translate("draft","not shape found"))
App.Console.PrintError(translate("draft","No shape found\n"))
return None
else:
shape = obj.Shape
if not DraftGeomUtils.isPlanar(shape):
App.Console.PrintError(translate("draft","All Shapes must be co-planar"))
if not DraftGeomUtils.is_planar(shape, tol):
App.Console.PrintError(translate("draft","All Shapes must be planar\n"))
return None
if DraftGeomUtils.get_normal(shape, tol):
shape_norm_yes.append(shape)
else:
shape_norm_no.append(shape)
shapes_list = shape_norm_yes + shape_norm_no
# test if all shapes are coplanar
if len(shape_norm_yes) >= 1:
for shape in shapes_list[1:]:
if not DraftGeomUtils.are_coplanar(shapes_list[0], shape, tol):
App.Console.PrintError(translate("draft","All Shapes must be coplanar\n"))
return None
# define sketch normal
normal = DraftGeomUtils.get_normal(shapes_list[0], tol)
else:
# supose all geometries are straight lines or points
points = [vertex.Point for shape in shapes_list for vertex in shape.Vertexes]
if len(points) >= 2:
poly = Part.makePolygon(points)
if not DraftGeomUtils.is_planar(poly, tol):
App.Console.PrintError(translate("draft","All Shapes must be coplanar\n"))
return None
normal = DraftGeomUtils.get_normal(poly, tol)
if not normal:
# all points aligned
poly_dir = poly.Edges[0].Curve.Direction
normal = (v_dir - v_dir.dot(poly_dir)*poly_dir).normalize()
normal = normal.negative()
else:
# only one point
normal = v_dir.negative()
if addTo:
nobj = addTo
else:
@@ -128,24 +180,23 @@ def make_sketch(objectslist, autoconstraints=False, addTo=None,
else:
return(edge)
rotation = None
for obj in objectslist:
axis = App.Vector(0,0,1).cross(normal)
angle = DraftVecUtils.angle(normal, App.Vector(0,0,1))*App.Units.Radian
rotation = App.Rotation(axis, angle)
for obj in objects_list:
ok = False
tp = utils.get_type(obj)
if tp in ["Circle","Ellipse"]:
if obj.Shape.Edges:
if rotation is None:
rotation = obj.Placement.Rotation
edge = obj.Shape.Edges[0]
if len(edge.Vertexes) == 1:
newEdge = DraftGeomUtils.orientEdge(edge)
nobj.addGeometry(newEdge)
newedge = DraftGeomUtils.orientEdge(edge, normal)
nobj.addGeometry(newedge)
else:
# make new ArcOfCircle
circle = DraftGeomUtils.orientEdge(edge)
angle = edge.Placement.Rotation.Angle
axis = edge.Placement.Rotation.Axis
circle.Center = DraftVecUtils.rotate(edge.Curve.Center, -angle, axis)
circle = DraftGeomUtils.orientEdge(edge, normal)
first = math.radians(obj.FirstAngle)
last = math.radians(obj.LastAngle)
arc = Part.ArcOfCircle(circle, first, last)
@@ -153,24 +204,36 @@ def make_sketch(objectslist, autoconstraints=False, addTo=None,
addRadiusConstraint(edge)
ok = True
elif tp == "Rectangle":
if rotation is None:
rotation = obj.Placement.Rotation
if obj.FilletRadius.Value == 0:
for edge in obj.Shape.Edges:
nobj.addGeometry(DraftGeomUtils.orientEdge(edge))
nobj.addGeometry(DraftGeomUtils.orientEdge(edge, normal))
# TODO: the previous implementation for autoconstraints fails in front
# and side view. So the autoconstraints for wires is used. This need
# more checking
if autoconstraints:
last = nobj.GeometryCount - 1
segs = [last-3,last-2,last-1,last]
if obj.Placement.Rotation.Q == (0,0,0,1):
constraints.append(Constraint("Coincident",last-3,EndPoint,last-2,StartPoint))
constraints.append(Constraint("Coincident",last-2,EndPoint,last-1,StartPoint))
constraints.append(Constraint("Coincident",last-1,EndPoint,last,StartPoint))
constraints.append(Constraint("Coincident",last,EndPoint,last-3,StartPoint))
constraints.append(Constraint("Horizontal",last-3))
constraints.append(Constraint("Vertical",last-2))
constraints.append(Constraint("Horizontal",last-1))
constraints.append(Constraint("Vertical",last))
last = nobj.GeometryCount
segs = list(range(last-len(obj.Shape.Edges),last-1))
for seg in segs:
constraints.append(Constraint("Coincident",seg,end_point,seg+1,start_point))
if DraftGeomUtils.isAligned(nobj.Geometry[seg],"x"):
constraints.append(Constraint("Vertical",seg))
elif DraftGeomUtils.isAligned(nobj.Geometry[seg],"y"):
constraints.append(Constraint("Horizontal",seg))
constraints.append(Constraint("Coincident",last-1,end_point,segs[0],start_point))
ok = True
# if autoconstraints:
# last = nobj.GeometryCount - 1
# segs = [last-3,last-2,last-1,last]
# if obj.Placement.Rotation.Q == (0,0,0,1):
# constraints.append(Constraint("Coincident",last-3,end_point,last-2,start_point))
# constraints.append(Constraint("Coincident",last-2,end_point,last-1,start_point))
# constraints.append(Constraint("Coincident",last-1,end_point,last,start_point))
# constraints.append(Constraint("Coincident",last,end_point,last-3,start_point))
# constraints.append(Constraint("Horizontal",last-3))
# constraints.append(Constraint("Vertical",last-2))
# constraints.append(Constraint("Horizontal",last-1))
# constraints.append(Constraint("Vertical",last))
# ok = True
elif tp in ["Wire","Polygon"]:
if obj.FilletRadius.Value == 0:
closed = False
@@ -180,71 +243,58 @@ def make_sketch(objectslist, autoconstraints=False, addTo=None,
closed = obj.Closed
if obj.Shape.Edges:
if (len(obj.Shape.Vertexes) < 3):
e = obj.Shape.Edges[0]
nobj.addGeometry(Part.LineSegment(e.Curve,e.FirstParameter,e.LastParameter))
else:
# Use the first three points to make a working plane. We've already
# checked to make sure everything is coplanar
plane = Part.Plane(*[i.Point for i in obj.Shape.Vertexes[:3]])
normal = plane.Axis
if rotation is None:
axis = App.Vector(0,0,1).cross(normal)
angle = DraftVecUtils.angle(normal, App.Vector(0,0,1)) * App.Units.Radian
rotation = App.Rotation(axis, angle)
for edge in obj.Shape.Edges:
# edge.rotate(App.Vector(0,0,0), rotAxis, rotAngle)
edge = DraftGeomUtils.orientEdge(edge, normal)
nobj.addGeometry(edge)
if autoconstraints:
last = nobj.GeometryCount
segs = list(range(last-len(obj.Shape.Edges),last-1))
for seg in segs:
constraints.append(Constraint("Coincident",seg,EndPoint,seg+1,StartPoint))
if DraftGeomUtils.isAligned(nobj.Geometry[seg],"x"):
constraints.append(Constraint("Vertical",seg))
elif DraftGeomUtils.isAligned(nobj.Geometry[seg],"y"):
constraints.append(Constraint("Horizontal",seg))
if closed:
constraints.append(Constraint("Coincident",last-1,EndPoint,segs[0],StartPoint))
for edge in obj.Shape.Edges:
edge = DraftGeomUtils.orientEdge(edge, normal)
nobj.addGeometry(edge)
if autoconstraints:
last = nobj.GeometryCount
segs = list(range(last-len(obj.Shape.Edges),last-1))
for seg in segs:
constraints.append(Constraint("Coincident",seg,end_point,seg+1,start_point))
if DraftGeomUtils.isAligned(nobj.Geometry[seg],"x"):
constraints.append(Constraint("Vertical",seg))
elif DraftGeomUtils.isAligned(nobj.Geometry[seg],"y"):
constraints.append(Constraint("Horizontal",seg))
if closed:
constraints.append(Constraint("Coincident",last-1,end_point,segs[0],start_point))
ok = True
elif tp == "BSpline":
if obj.Shape.Edges:
nobj.addGeometry(obj.Shape.Edges[0].Curve)
edge = DraftGeomUtils.orientEdge(obj.Shape.Edges[0], normal)
nobj.addGeometry(edge)
nobj.exposeInternalGeometry(nobj.GeometryCount-1)
ok = True
elif tp == "BezCurve":
if obj.Shape.Edges:
bez = obj.Shape.Edges[0].Curve
bsp = bez.toBSpline(bez.FirstParameter,bez.LastParameter)
nobj.addGeometry(bsp)
nobj.exposeInternalGeometry(nobj.GeometryCount-1)
for piece in obj.Shape.Edges:
bez = piece.Curve
bsp = bez.toBSpline(bez.FirstParameter,bez.LastParameter).toShape()
edge = DraftGeomUtils.orientEdge(bsp.Edges[0], normal)
nobj.addGeometry(edge)
nobj.exposeInternalGeometry(nobj.GeometryCount-1)
ok = True
# TODO: set coincident constraint for vertexes in multi-edge bezier curve
elif tp == "Point":
shape = obj.Shape.copy()
if angle:
shape.rotate(App.Base.Vector(0,0,0), axis, -1*angle)
point = Part.Point(shape.Point)
nobj.addGeometry(point)
ok = True
elif tp == 'Shape' or hasattr(obj,'Shape'):
shape = obj if tp == 'Shape' else obj.Shape
if not DraftGeomUtils.isPlanar(shape):
App.Console.PrintError(translate("draft","The given object is not planar and cannot be converted into a sketch."))
return None
if rotation is None:
#rotation = obj.Placement.Rotation
norm = DraftGeomUtils.getNormal(shape)
if norm:
rotation = App.Rotation(App.Vector(0,0,1),norm)
else:
App.Console.PrintWarning(translate("draft","Unable to guess the normal direction of this object"))
rotation = App.Rotation()
norm = obj.Placement.Rotation.Axis
if not shape.Wires:
for e in shape.Edges:
# unconnected edges
newedge = convertBezier(e)
nobj.addGeometry(DraftGeomUtils.orientEdge(newedge,norm,make_arc=True))
nobj.addGeometry(DraftGeomUtils.orientEdge(
newedge, normal, make_arc=True))
addRadiusConstraint(newedge)
# if not addTo:
# nobj.Placement.Rotation = DraftGeomUtils.calculatePlacement(shape).Rotation
if autoconstraints:
for wire in shape.Wires:
last_count = nobj.GeometryCount
@@ -252,7 +302,7 @@ def make_sketch(objectslist, autoconstraints=False, addTo=None,
for edge in edges:
newedge = convertBezier(edge)
nobj.addGeometry(DraftGeomUtils.orientEdge(
newedge,norm,make_arc=True))
newedge, normal, make_arc=True))
addRadiusConstraint(newedge)
for i,g in enumerate(nobj.Geometry[last_count:]):
if edges[i].Closed:
@@ -277,25 +327,25 @@ def make_sketch(objectslist, autoconstraints=False, addTo=None,
start2 = g2.value(g2.FirstParameter)
if DraftVecUtils.equals(end1,start2) :
constraints.append(Constraint(
"Coincident",seg,EndPoint,seg2,StartPoint))
"Coincident",seg,end_point,seg2,start_point))
continue
end2 = g2.value(g2.LastParameter)
start1 = g.value(g.FirstParameter)
if DraftVecUtils.equals(end2,start1):
constraints.append(Constraint(
"Coincident",seg,StartPoint,seg2,EndPoint))
"Coincident",seg,start_point,seg2,end_point))
elif DraftVecUtils.equals(start1,start2):
constraints.append(Constraint(
"Coincident",seg,StartPoint,seg2,StartPoint))
"Coincident",seg,start_point,seg2,start_point))
elif DraftVecUtils.equals(end1,end2):
constraints.append(Constraint(
"Coincident",seg,EndPoint,seg2,EndPoint))
"Coincident",seg,end_point,seg2,end_point))
else:
for wire in shape.Wires:
for edge in wire.OrderedEdges:
newedge = convertBezier(edge)
nobj.addGeometry(DraftGeomUtils.orientEdge(
newedge,norm,make_arc=True))
newedge, normal, make_arc=True))
ok = True
gui_utils.format_object(nobj,obj)
if ok and delete and hasattr(obj,'Shape'):
@@ -318,10 +368,9 @@ def make_sketch(objectslist, autoconstraints=False, addTo=None,
except Exception as ex:
App.Console.PrintWarning(translate("draft",
"Failed to delete object {}: {}".format(obj.Label,ex))+"\n")
if rotation:
nobj.Placement.Rotation = rotation
else:
print("-----error!!! rotation is still None...")
nobj.Placement.Rotation = rotation
nobj.addConstraint(constraints)
return nobj

View File

@@ -444,7 +444,10 @@ def placements_on_path(shapeRotation, pathwire, count, xlate, align,
Each copy will be distributed evenly.
"""
closedpath = DraftGeomUtils.isReallyClosed(pathwire)
normal = DraftGeomUtils.getNormal(pathwire)
normal = DraftGeomUtils.get_normal(pathwire)
# for backward compatibility with previous getNormal implementation
if normal == None:
normal = App.Vector(0, 0, 1)
if forceNormal and normalOverride:
normal = normalOverride