Add Draft workbench to .pre-commit-config (#24664)

* Add Draft workbench to .pre-commit-config

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
marcuspollio
2025-10-15 11:21:09 +02:00
committed by GitHub
parent 87f88bba30
commit 50e4864efb
225 changed files with 10713 additions and 9269 deletions

View File

@@ -63,52 +63,65 @@ def array(objectslist, arg1, arg2, arg3, arg4=None, arg5=None, arg6=None):
if arg6:
rectArray2(objectslist, arg1, arg2, arg3, arg4, arg5, arg6)
elif arg4:
rectArray(objectslist, arg1,arg2, arg3, arg4)
rectArray(objectslist, arg1, arg2, arg3, arg4)
else:
polarArray(objectslist, arg1, arg2, arg3)
def rectArray(objectslist,xvector,yvector,xnum,ynum):
utils.type_check([(xvector, App.Vector),
(yvector, App.Vector),
(xnum,int), (ynum,int)],
"rectArray")
if not isinstance(objectslist,list): objectslist = [objectslist]
def rectArray(objectslist, xvector, yvector, xnum, ynum):
utils.type_check(
[(xvector, App.Vector), (yvector, App.Vector), (xnum, int), (ynum, int)], "rectArray"
)
if not isinstance(objectslist, list):
objectslist = [objectslist]
for xcount in range(xnum):
currentxvector=App.Vector(xvector).multiply(xcount)
if not xcount==0:
move.move(objectslist,currentxvector,True)
currentxvector = App.Vector(xvector).multiply(xcount)
if not xcount == 0:
move.move(objectslist, currentxvector, True)
for ycount in range(ynum):
currentxvector=App.Vector(currentxvector)
currentyvector=currentxvector.add(App.Vector(yvector).multiply(ycount))
if not ycount==0:
move.move(objectslist,currentyvector,True)
currentxvector = App.Vector(currentxvector)
currentyvector = currentxvector.add(App.Vector(yvector).multiply(ycount))
if not ycount == 0:
move.move(objectslist, currentyvector, True)
def rectArray2(objectslist,xvector,yvector,zvector,xnum,ynum,znum):
utils.type_check([(xvector,App.Vector), (yvector,App.Vector), (zvector,App.Vector),(xnum,int), (ynum,int),(znum,int)], "rectArray2")
if not isinstance(objectslist,list): objectslist = [objectslist]
def rectArray2(objectslist, xvector, yvector, zvector, xnum, ynum, znum):
utils.type_check(
[
(xvector, App.Vector),
(yvector, App.Vector),
(zvector, App.Vector),
(xnum, int),
(ynum, int),
(znum, int),
],
"rectArray2",
)
if not isinstance(objectslist, list):
objectslist = [objectslist]
for xcount in range(xnum):
currentxvector=App.Vector(xvector).multiply(xcount)
if not xcount==0:
move.move(objectslist,currentxvector,True)
currentxvector = App.Vector(xvector).multiply(xcount)
if not xcount == 0:
move.move(objectslist, currentxvector, True)
for ycount in range(ynum):
currentxvector=App.Vector(currentxvector)
currentyvector=currentxvector.add(App.Vector(yvector).multiply(ycount))
if not ycount==0:
move.move(objectslist,currentyvector,True)
currentxvector = App.Vector(currentxvector)
currentyvector = currentxvector.add(App.Vector(yvector).multiply(ycount))
if not ycount == 0:
move.move(objectslist, currentyvector, True)
for zcount in range(znum):
currentzvector=currentyvector.add(App.Vector(zvector).multiply(zcount))
if not zcount==0:
move.move(objectslist,currentzvector,True)
currentzvector = currentyvector.add(App.Vector(zvector).multiply(zcount))
if not zcount == 0:
move.move(objectslist, currentzvector, True)
def polarArray(objectslist,center,angle,num):
utils.type_check([(center,App.Vector), (num,int)], "polarArray")
if not isinstance(objectslist,list): objectslist = [objectslist]
fraction = float(angle)/num
def polarArray(objectslist, center, angle, num):
utils.type_check([(center, App.Vector), (num, int)], "polarArray")
if not isinstance(objectslist, list):
objectslist = [objectslist]
fraction = float(angle) / num
for i in range(num):
currangle = fraction + (i*fraction)
rotate.rotate(objectslist,currangle,center,copy=True)
currangle = fraction + (i * fraction)
rotate.rotate(objectslist, currangle, center, copy=True)
## @}

View File

@@ -54,7 +54,7 @@ def cut(object1, object2):
If there is a problem and the new object can't be created.
"""
if not App.activeDocument():
_err(translate("draft","No active document. Aborting."))
_err(translate("draft", "No active document. Aborting."))
return
obj = App.activeDocument().addObject("Part::Cut", "Cut")
@@ -69,4 +69,5 @@ def cut(object1, object2):
return obj
## @}

View File

@@ -247,7 +247,6 @@ def downgrade(objects, delete=False, force=None):
return True
return False
# helper functions (same as in upgrade.py)
def get_parent(obj):
@@ -311,7 +310,6 @@ def downgrade(objects, delete=False, force=None):
for newobj in new_list:
gui_utils.format_object(newobj, obj, ignore_construction=True)
doc = App.ActiveDocument
add_list = []
delete_list = []
@@ -347,15 +345,15 @@ def downgrade(objects, delete=False, force=None):
elif force:
# functions that work on a single object:
single_funcs = {"explode": explode,
"getWire": getWire,
"shapify": _shapify}
single_funcs = {"explode": explode, "getWire": getWire, "shapify": _shapify}
# functions that work on multiple objects:
multi_funcs = {"cut2": cut2,
"splitCompounds": splitCompounds,
"splitFaces": splitFaces,
"splitWires": splitWires,
"subtr": subtr}
multi_funcs = {
"cut2": cut2,
"splitCompounds": splitCompounds,
"splitFaces": splitFaces,
"splitWires": splitWires,
"subtr": subtr,
}
if force in single_funcs:
result = any([single_funcs[force](obj) for obj in objects])
elif force in multi_funcs:
@@ -388,21 +386,21 @@ def downgrade(objects, delete=False, force=None):
_msg(translate("draft", "Found 1 array: exploding it"))
# special case, we have one parametric object: we "de-parametrize" it
elif len(objects) == 1 \
and hasattr(objects[0], "Shape") \
and (
hasattr(objects[0], "Base")
or hasattr(objects[0], "Profile")
or hasattr(objects[0], "Sections")
):
elif (
len(objects) == 1
and hasattr(objects[0], "Shape")
and (
hasattr(objects[0], "Base")
or hasattr(objects[0], "Profile")
or hasattr(objects[0], "Sections")
)
):
result = _shapify(objects[0])
if result:
_msg(translate("draft", "Found 1 parametric object: breaking its dependencies"))
# we have one multi-solids compound object: extract its solids
elif len(objects) == 1 \
and hasattr(objects[0], "Shape") \
and len(solids) > 1:
elif len(objects) == 1 and hasattr(objects[0], "Shape") and len(solids) > 1:
result = splitCompounds(objects)
if result:
_msg(translate("draft", "Found 1 multi-solids compound: exploding it"))
@@ -423,7 +421,11 @@ def downgrade(objects, delete=False, force=None):
elif same_parent and same_parent_type != "PartDesign::Body":
result = subtr(objects)
if result:
_msg(translate("draft", "Found several faces: subtracting them from the first one"))
_msg(
translate(
"draft", "Found several faces: subtracting them from the first one"
)
)
# only one face: we extract its wires
elif len(faces) > 0:
@@ -448,4 +450,5 @@ def downgrade(objects, delete=False, force=None):
gui_utils.select(add_list)
return add_list, delete_list
## @}

View File

@@ -43,6 +43,7 @@ import draftmake.make_arc_3points as make_arc_3points
Part = lz.LazyLoader("Part", globals(), "Part")
DraftGeomUtils = lz.LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils")
def draftify(objectslist, makeblock=False, delete=True):
"""draftify(objectslist,[makeblock],[delete])
@@ -62,11 +63,11 @@ def draftify(objectslist, makeblock=False, delete=True):
If delete = False, old objects are not deleted
"""
if not isinstance(objectslist,list):
if not isinstance(objectslist, list):
objectslist = [objectslist]
newobjlist = []
for obj in objectslist:
if hasattr(obj,'Shape'):
if hasattr(obj, "Shape"):
for cluster in Part.sortEdges(obj.Shape.Edges):
w = Part.Wire(cluster)
nobj = draftify_shape(w)
@@ -88,11 +89,12 @@ def draftify(objectslist, makeblock=False, delete=True):
return newobjlist[0]
return newobjlist
def draftify_shape(shape):
nobj = None
if DraftGeomUtils.hasCurves(shape):
if (len(shape.Edges) == 1):
if len(shape.Edges) == 1:
edge = shape.Edges[0]
edge_type = DraftGeomUtils.geomType(edge)
if edge_type == "Circle":
@@ -101,21 +103,24 @@ def draftify_shape(shape):
else:
first_parameter = edge.FirstParameter
last_parameter = edge.LastParameter
points = [edge.Curve.value(first_parameter),
edge.Curve.value((first_parameter + last_parameter)/2),
edge.Curve.value(last_parameter)]
points = [
edge.Curve.value(first_parameter),
edge.Curve.value((first_parameter + last_parameter) / 2),
edge.Curve.value(last_parameter),
]
nobj = make_arc_3points.make_arc_3points(points)
# TODO: take into consideration trimmed curves and capture the specific
# type of BSpline and Bezier that can be converted to a draft object.
# elif edge_type == "BSplineCurve":
# knots = [edge.Curve.value(p) for p in edge.Curve.getKnots()]
# nobj = make_bspline.make_bspline(knots, closed=edge.isClosed())
# elif edge_type == "BezierCurve":
# nobj = make_bezcurve.make_bezcurve(edge.Curve.getPoles(),
# closed=edge.isClosed())
# TODO: take into consideration trimmed curves and capture the specific
# type of BSpline and Bezier that can be converted to a draft object.
# elif edge_type == "BSplineCurve":
# knots = [edge.Curve.value(p) for p in edge.Curve.getKnots()]
# nobj = make_bspline.make_bspline(knots, closed=edge.isClosed())
# elif edge_type == "BezierCurve":
# nobj = make_bezcurve.make_bezcurve(edge.Curve.getPoles(),
# closed=edge.isClosed())
else:
nobj = make_wire.make_wire(shape)
return nobj
## @}

View File

@@ -20,8 +20,7 @@
# * USA *
# * *
# ***************************************************************************
"""Provides functions to return the DXF representation of various shapes.
"""
"""Provides functions to return the DXF representation of various shapes."""
## @package dxf
# \ingroup draftfunctions
# \brief Provides functions to return the DXF representation of shapes.
@@ -44,6 +43,7 @@ TechDraw = lz.LazyLoader("TechDraw", globals(), "TechDraw")
## \addtogroup draftfunctions
# @{
def _get_proj(vec, plane=None):
"""Get a projection of the vector in the plane's u and v directions.
@@ -85,9 +85,9 @@ def get_dxf(obj, direction=None):
p2 = _get_proj(obj.End, plane=plane)
p3 = _get_proj(obj.Dimline, plane=plane)
result += "0\nDIMENSION\n8\n0\n62\n0\n3\nStandard\n70\n1\n"
result += "10\n"+str(p3.x)+"\n20\n"+str(p3.y)+"\n30\n"+str(p3.z)+"\n"
result += "13\n"+str(p1.x)+"\n23\n"+str(p1.y)+"\n33\n"+str(p1.z)+"\n"
result += "14\n"+str(p2.x)+"\n24\n"+str(p2.y)+"\n34\n"+str(p2.z)+"\n"
result += "10\n" + str(p3.x) + "\n20\n" + str(p3.y) + "\n30\n" + str(p3.z) + "\n"
result += "13\n" + str(p1.x) + "\n23\n" + str(p1.y) + "\n33\n" + str(p1.z) + "\n"
result += "14\n" + str(p2.x) + "\n24\n" + str(p2.y) + "\n34\n" + str(p2.z) + "\n"
elif utils.get_type(obj) == "Annotation":
# Only for App::Annotation
@@ -104,7 +104,7 @@ def get_dxf(obj, direction=None):
result += "7\nSTANDARD\n"
count += 1
elif hasattr(obj, 'Shape'):
elif hasattr(obj, "Shape"):
# TODO do this the Draft way, for ex. using polylines and rectangles
if not direction:
direction = App.Vector(0, 0, -1)
@@ -117,8 +117,7 @@ def get_dxf(obj, direction=None):
except Exception:
# TODO: trap only specific exception.
# Impossible to generate DXF from Shape? Which exception is throw?
_wrn("get_dxf: "
"unable to project '{}' to {}".format(obj.Label, direction))
_wrn("get_dxf: " "unable to project '{}' to {}".format(obj.Label, direction))
else:
result += d
else:
@@ -127,11 +126,10 @@ def get_dxf(obj, direction=None):
return result
def getDXF(obj,
direction=None):
def getDXF(obj, direction=None):
"""Return DXF string of the object. DEPRECATED. Use 'get_dxf'."""
utils.use_instead("get_dxf")
return get_dxf(obj,
direction=direction)
return get_dxf(obj, direction=direction)
## @}

View File

@@ -55,9 +55,10 @@ def extrude(obj, vector, solid=False):
newobj.Solid = solid
if App.GuiUp:
obj.ViewObject.Visibility = False
gui_utils.format_object(newobj,obj)
gui_utils.format_object(newobj, obj)
gui_utils.select(newobj)
return newobj
## @}

View File

@@ -49,6 +49,7 @@ def fuse(object1, object2):
return
import Part
import DraftGeomUtils
# testing if we have holes:
holes = False
fshape = object1.Shape.fuse(object2.Shape)
@@ -74,9 +75,10 @@ def fuse(object1, object2):
if App.GuiUp:
object1.ViewObject.Visibility = False
object2.ViewObject.Visibility = False
gui_utils.format_object(obj,object1)
gui_utils.format_object(obj, object1)
gui_utils.select(obj)
return obj
## @}

View File

@@ -61,7 +61,7 @@ def heal(objlist=None, delete=True, reparent=True):
else:
print("Manual mode: Force-healing selected objects…")
if not isinstance(objlist,list):
if not isinstance(objlist, list):
objlist = [objlist]
dellist = []
@@ -70,34 +70,34 @@ def heal(objlist=None, delete=True, reparent=True):
for obj in objlist:
dtype = utils.get_type(obj)
ftype = obj.TypeId
if ftype in ["Part::FeaturePython","App::FeaturePython","Part::Part2DObjectPython"]:
if ftype in ["Part::FeaturePython", "App::FeaturePython", "Part::Part2DObjectPython"]:
proxy = obj.Proxy
if hasattr(obj,"ViewObject"):
if hasattr(obj.ViewObject,"Proxy"):
if hasattr(obj, "ViewObject"):
if hasattr(obj.ViewObject, "Proxy"):
proxy = obj.ViewObject.Proxy
if (proxy == 1) or (dtype in ["Unknown","Part"]) or (not auto):
if (proxy == 1) or (dtype in ["Unknown", "Part"]) or (not auto):
got = True
dellist.append(obj.Name)
props = obj.PropertiesList
if ("Dimline" in props) and ("Start" in props):
print("Healing " + obj.Name + " of type Dimension")
nobj = make_copy(obj,force="Dimension",reparent=reparent)
nobj = make_copy(obj, force="Dimension", reparent=reparent)
elif ("Height" in props) and ("Length" in props):
print("Healing " + obj.Name + " of type Rectangle")
nobj = make_copy(obj,force="Rectangle",reparent=reparent)
nobj = make_copy(obj, force="Rectangle", reparent=reparent)
elif ("Points" in props) and ("Closed" in props):
if "BSpline" in obj.Name:
print("Healing " + obj.Name + " of type BSpline")
nobj = make_copy(obj,force="BSpline",reparent=reparent)
nobj = make_copy(obj, force="BSpline", reparent=reparent)
else:
print("Healing " + obj.Name + " of type Wire")
nobj = make_copy(obj,force="Wire",reparent=reparent)
nobj = make_copy(obj, force="Wire", reparent=reparent)
elif ("Radius" in props) and ("FirstAngle" in props):
print("Healing " + obj.Name + " of type Circle")
nobj = make_copy(obj,force="Circle",reparent=reparent)
nobj = make_copy(obj, force="Circle", reparent=reparent)
elif ("DrawMode" in props) and ("FacesNumber" in props):
print("Healing " + obj.Name + " of type Polygon")
nobj = make_copy(obj,force="Polygon",reparent=reparent)
nobj = make_copy(obj, force="Polygon", reparent=reparent)
else:
dellist.pop()
print("Object " + obj.Name + " is not healable")
@@ -105,10 +105,11 @@ def heal(objlist=None, delete=True, reparent=True):
if not got:
print("No object seems to need healing")
else:
print("Healed ",len(dellist)," objects")
print("Healed ", len(dellist), " objects")
if dellist and delete:
for n in dellist:
App.ActiveDocument.removeObject(n)
## @}

View File

@@ -31,7 +31,7 @@ import FreeCAD as App
import DraftVecUtils
def join_wires(wires, joinAttempts = 0):
def join_wires(wires, joinAttempts=0):
"""join_wires(objects): merges a set of wires where possible, if any of those
wires have a coincident start and end point"""
wires = list(wires)
@@ -58,10 +58,13 @@ def join_two_wires(wire1, wire2):
"""
wire1AbsPoints = [wire1.Placement.multVec(point) for point in wire1.Points]
wire2AbsPoints = [wire2.Placement.multVec(point) for point in wire2.Points]
if ((DraftVecUtils.equals(wire1AbsPoints[0], wire2AbsPoints[-1])
and DraftVecUtils.equals(wire1AbsPoints[-1], wire2AbsPoints[0]))
or (DraftVecUtils.equals(wire1AbsPoints[0], wire2AbsPoints[0])
and DraftVecUtils.equals(wire1AbsPoints[-1], wire2AbsPoints[-1]))):
if (
DraftVecUtils.equals(wire1AbsPoints[0], wire2AbsPoints[-1])
and DraftVecUtils.equals(wire1AbsPoints[-1], wire2AbsPoints[0])
) or (
DraftVecUtils.equals(wire1AbsPoints[0], wire2AbsPoints[0])
and DraftVecUtils.equals(wire1AbsPoints[-1], wire2AbsPoints[-1])
):
wire2AbsPoints.pop()
wire1.Closed = True
elif DraftVecUtils.equals(wire1AbsPoints[0], wire2AbsPoints[0]):
@@ -76,10 +79,9 @@ def join_two_wires(wire1, wire2):
else:
return False
wire2AbsPoints.pop(0)
wire1.Points = ([wire1.Placement.inverse().multVec(point)
for point in wire1AbsPoints] +
[wire1.Placement.inverse().multVec(point)
for point in wire2AbsPoints])
wire1.Points = [wire1.Placement.inverse().multVec(point) for point in wire1AbsPoints] + [
wire1.Placement.inverse().multVec(point) for point in wire2AbsPoints
]
App.ActiveDocument.removeObject(wire2.Name)
return True

View File

@@ -80,11 +80,11 @@ def mirror(objlist, p1, p2):
"""
if not objlist:
_err(translate("draft","No object given"))
_err(translate("draft", "No object given"))
return
if p1 == p2:
_err(translate("draft","The two points are coincident"))
_err(translate("draft", "The two points are coincident"))
return
if not isinstance(objlist, list):
@@ -110,4 +110,5 @@ def mirror(objlist, p1, p2):
return result
## @}

View File

@@ -92,7 +92,9 @@ def move(selection, vector, copy=False, subelements=False):
if copy:
for obj in objs:
if obj.isDerivedFrom("App::DocumentObjectGroup") and obj.Name not in newgroups:
newgroups[obj.Name] = obj.Document.addObject(obj.TypeId, utils.get_real_name(obj.Name))
newgroups[obj.Name] = obj.Document.addObject(
obj.TypeId, utils.get_real_name(obj.Name)
)
for idx, obj in enumerate(objs):
newobj = None
@@ -217,7 +219,7 @@ def move_edge(obj, edge_idx, vector, global_place=None):
if utils.is_closed_edge(edge_idx, obj):
move_vertex(obj, 0, vector, global_place)
else:
move_vertex(obj, edge_idx+1, vector, global_place)
move_vertex(obj, edge_idx + 1, vector, global_place)
def copy_moved_edge(obj, edge_idx, vector, global_place=None):
@@ -233,9 +235,10 @@ def copy_moved_edge(obj, edge_idx, vector, global_place=None):
if utils.is_closed_edge(edge_idx, obj):
vertex2 = glp.multVec(obj.Points[0]).add(vector)
else:
vertex2 = glp.multVec(obj.Points[edge_idx+1]).add(vector)
vertex2 = glp.multVec(obj.Points[edge_idx + 1]).add(vector)
newobj = make_line.make_line(vertex1, vertex2)
gui_utils.format_object(newobj, obj)
return newobj
## @}

View File

@@ -70,43 +70,51 @@ def offset(obj, delta, copy=False, bind=False, sym=False, occ=False):
"""
import Part
import DraftGeomUtils
newwire = None
delete = None
if (copy is False
and (utils.get_type(obj).startswith("Sketcher::")
or utils.get_type(obj).startswith("Part::")
or utils.get_type(obj).startswith("PartDesign::"))): # For PartDesign_SubShapeBinders which can reference sketches.
print("the offset tool is currently unable to offset a non-Draft object directly - Creating a copy")
if copy is False and (
utils.get_type(obj).startswith("Sketcher::")
or utils.get_type(obj).startswith("Part::")
or utils.get_type(obj).startswith("PartDesign::")
): # For PartDesign_SubShapeBinders which can reference sketches.
print(
"the offset tool is currently unable to offset a non-Draft object directly - Creating a copy"
)
copy = True
def getRect(p,obj):
def getRect(p, obj):
"""returns length,height,placement"""
pl = obj.Placement.copy()
pl.Base = p[0]
diag = p[2].sub(p[0])
bb = p[1].sub(p[0])
bh = p[3].sub(p[0])
nb = DraftVecUtils.project(diag,bb)
nh = DraftVecUtils.project(diag,bh)
if obj.Length.Value < 0: l = -nb.Length
else: l = nb.Length
if obj.Height.Value < 0: h = -nh.Length
else: h = nh.Length
return l,h,pl
nb = DraftVecUtils.project(diag, bb)
nh = DraftVecUtils.project(diag, bh)
if obj.Length.Value < 0:
l = -nb.Length
else:
l = nb.Length
if obj.Height.Value < 0:
h = -nh.Length
else:
h = nh.Length
return l, h, pl
def getRadius(obj,delta):
def getRadius(obj, delta):
"""returns a new radius for a regular polygon"""
an = math.pi/obj.FacesNumber
nr = DraftVecUtils.rotate(delta,-an)
nr.multiply(1/math.cos(an))
an = math.pi / obj.FacesNumber
nr = DraftVecUtils.rotate(delta, -an)
nr.multiply(1 / math.cos(an))
nr = obj.Shape.Vertexes[0].Point.add(nr)
nr = nr.sub(obj.Placement.Base)
nr = nr.Length
if obj.DrawMode == "inscribed":
return nr
else:
return nr * math.cos(math.pi/obj.FacesNumber)
return nr * math.cos(math.pi / obj.FacesNumber)
newwire = None
if utils.get_type(obj) == "Circle":
@@ -117,27 +125,29 @@ def offset(obj, delta, copy=False, bind=False, sym=False, occ=False):
if sym:
d1 = App.Vector(delta).multiply(0.5)
d2 = d1.negative()
n1 = DraftGeomUtils.offsetWire(obj.Shape,d1)
n2 = DraftGeomUtils.offsetWire(obj.Shape,d2)
n1 = DraftGeomUtils.offsetWire(obj.Shape, d1)
n2 = DraftGeomUtils.offsetWire(obj.Shape, d2)
else:
if isinstance(delta,float) and (len(obj.Shape.Edges) == 1):
if isinstance(delta, float) and (len(obj.Shape.Edges) == 1):
# circle
c = obj.Shape.Edges[0].Curve
nc = Part.Circle(c.Center,c.Axis,delta)
nc = Part.Circle(c.Center, c.Axis, delta)
if len(obj.Shape.Vertexes) > 1:
nc = Part.ArcOfCircle(nc,obj.Shape.Edges[0].FirstParameter,obj.Shape.Edges[0].LastParameter)
nc = Part.ArcOfCircle(
nc, obj.Shape.Edges[0].FirstParameter, obj.Shape.Edges[0].LastParameter
)
newwire = Part.Wire(nc.toShape())
p = []
else:
newwire = DraftGeomUtils.offsetWire(obj.Shape,delta)
newwire = DraftGeomUtils.offsetWire(obj.Shape, delta)
if DraftGeomUtils.hasCurves(newwire) and copy:
p = []
else:
p = DraftGeomUtils.getVerts(newwire)
if occ:
newobj = App.ActiveDocument.addObject("Part::Feature","Offset")
newobj.Shape = DraftGeomUtils.offsetWire(obj.Shape,delta,occ=True)
gui_utils.formatObject(newobj,obj)
newobj = App.ActiveDocument.addObject("Part::Feature", "Offset")
newobj.Shape = DraftGeomUtils.offsetWire(obj.Shape, delta, occ=True)
gui_utils.formatObject(newobj, obj)
if not copy:
delete = obj.Name
elif bind:
@@ -151,35 +161,36 @@ def offset(obj, delta, copy=False, bind=False, sym=False, occ=False):
if s1 and s2:
w1 = s1.Edges
w2 = s2.Edges
w3 = Part.LineSegment(s1.Vertexes[0].Point,s2.Vertexes[0].Point).toShape()
w4 = Part.LineSegment(s1.Vertexes[-1].Point,s2.Vertexes[-1].Point).toShape()
newobj = App.ActiveDocument.addObject("Part::Feature","Offset")
newobj.Shape = Part.Face(Part.Wire(w1+[w3]+w2+[w4]))
w3 = Part.LineSegment(s1.Vertexes[0].Point, s2.Vertexes[0].Point).toShape()
w4 = Part.LineSegment(s1.Vertexes[-1].Point, s2.Vertexes[-1].Point).toShape()
newobj = App.ActiveDocument.addObject("Part::Feature", "Offset")
newobj.Shape = Part.Face(Part.Wire(w1 + [w3] + w2 + [w4]))
else:
print("Draft.offset: Unable to bind wires")
else:
newobj = App.ActiveDocument.addObject("Part::Feature","Offset")
newobj = App.ActiveDocument.addObject("Part::Feature", "Offset")
newobj.Shape = Part.Face(obj.Shape.Wires[0])
if not copy:
delete = obj.Name
elif copy:
newobj = None
if sym: return None
if sym:
return None
if utils.get_type(obj) == "Wire":
if p:
newobj = make_wire(p)
newobj.Closed = obj.Closed
elif newwire:
newobj = App.ActiveDocument.addObject("Part::Feature","Offset")
newobj = App.ActiveDocument.addObject("Part::Feature", "Offset")
newobj.Shape = newwire
else:
print("Draft.offset: Unable to duplicate this object")
elif utils.get_type(obj) == "Rectangle":
if p:
length,height,plac = getRect(p,obj)
newobj = make_rectangle(length,height,plac)
length, height, plac = getRect(p, obj)
newobj = make_rectangle(length, height, plac)
elif newwire:
newobj = App.ActiveDocument.addObject("Part::Feature","Offset")
newobj = App.ActiveDocument.addObject("Part::Feature", "Offset")
newobj.Shape = newwire
else:
print("Draft.offset: Unable to duplicate this object")
@@ -192,7 +203,7 @@ def offset(obj, delta, copy=False, bind=False, sym=False, occ=False):
elif utils.get_type(obj) == "Polygon":
pl = obj.Placement
newobj = make_polygon(obj.FacesNumber)
newobj.Radius = getRadius(obj,delta)
newobj.Radius = getRadius(obj, delta)
newobj.DrawMode = obj.DrawMode
newobj.Placement = pl
elif utils.get_type(obj) == "BSpline":
@@ -207,37 +218,38 @@ def offset(obj, delta, copy=False, bind=False, sym=False, occ=False):
except Part.OCCError:
pass
if (not newobj) and newwire:
newobj = App.ActiveDocument.addObject("Part::Feature","Offset")
newobj = App.ActiveDocument.addObject("Part::Feature", "Offset")
newobj.Shape = newwire
if not newobj:
print("Draft.offset: Unable to create an offset")
if newobj:
gui_utils.formatObject(newobj,obj)
gui_utils.formatObject(newobj, obj)
else:
newobj = None
if sym: return None
if sym:
return None
if utils.get_type(obj) == "Wire":
if obj.Base or obj.Tool:
App.Console.PrintWarning("Warning: object history removed\n")
obj.Base = None
obj.Tool = None
obj.Placement = App.Placement() # p points are in the global coordinate system
obj.Placement = App.Placement() # p points are in the global coordinate system
obj.Points = p
elif utils.get_type(obj) == "BSpline":
#print(delta)
# print(delta)
obj.Points = delta
#print("done")
# print("done")
elif utils.get_type(obj) == "Rectangle":
length,height,plac = getRect(p,obj)
length, height, plac = getRect(p, obj)
obj.Placement = plac
obj.Length = length
obj.Height = height
elif utils.get_type(obj) == "Circle":
obj.Radius = delta
elif utils.get_type(obj) == "Polygon":
obj.Radius = getRadius(obj,delta)
elif utils.get_type(obj) == 'Part':
print("unsupported object") # TODO
obj.Radius = getRadius(obj, delta)
elif utils.get_type(obj) == "Part":
print("unsupported object") # TODO
newobj = obj
if copy and params.get_param("selectBaseObjects"):
gui_utils.select(obj)
@@ -247,4 +259,5 @@ def offset(obj, delta, copy=False, bind=False, sym=False, occ=False):
App.ActiveDocument.removeObject(delete)
return newobj
## @}

View File

@@ -42,8 +42,14 @@ from draftutils import params
from draftutils import utils
def rotate(selection, angle, center=App.Vector(0, 0, 0),
axis=App.Vector(0, 0, 1), copy=False, subelements=False):
def rotate(
selection,
angle,
center=App.Vector(0, 0, 0),
axis=App.Vector(0, 0, 1),
copy=False,
subelements=False,
):
"""rotate(selection, angle, [center], [axis], [copy], [subelements])
Rotates or copies selected objects.
@@ -80,8 +86,16 @@ def rotate(selection, angle, center=App.Vector(0, 0, 0),
single object / list with 2 or more objects / empty list
The objects (or their copies).
"""
utils.type_check([(angle, (float, int)), (center, App.Vector),
(axis, App.Vector), (copy, bool), (subelements, bool)], "rotate")
utils.type_check(
[
(angle, (float, int)),
(center, App.Vector),
(axis, App.Vector),
(copy, bool),
(subelements, bool),
],
"rotate",
)
if not isinstance(selection, list):
selection = [selection]
if not selection:
@@ -106,7 +120,9 @@ def rotate(selection, angle, center=App.Vector(0, 0, 0),
if copy:
for obj in objs:
if obj.isDerivedFrom("App::DocumentObjectGroup") and obj.Name not in newgroups:
newgroups[obj.Name] = obj.Document.addObject(obj.TypeId, utils.get_real_name(obj.Name))
newgroups[obj.Name] = obj.Document.addObject(
obj.TypeId, utils.get_real_name(obj.Name)
)
for idx, obj in enumerate(objs):
newobj = None
@@ -230,9 +246,8 @@ def rotate_vertex(obj, vert_idx, angle, center, axis, global_place=None):
glp = global_place
points = obj.Points
points[vert_idx] = glp.inverse().multVec(
rotate_vector_from_center(
glp.multVec(points[vert_idx]),
angle, axis, center))
rotate_vector_from_center(glp.multVec(points[vert_idx]), angle, axis, center)
)
obj.Points = points
@@ -245,7 +260,7 @@ def rotate_edge(obj, edge_idx, angle, center, axis, global_place=None):
if utils.is_closed_edge(edge_idx, obj):
rotate_vertex(obj, 0, angle, center, axis, global_place)
else:
rotate_vertex(obj, edge_idx+1, angle, center, axis, global_place)
rotate_vertex(obj, edge_idx + 1, angle, center, axis, global_place)
def copy_rotated_edge(obj, edge_idx, angle, center, axis, global_place=None):
@@ -257,19 +272,16 @@ def copy_rotated_edge(obj, edge_idx, angle, center, axis, global_place=None):
glp = obj.getGlobalPlacement()
else:
glp = global_place
vertex1 = rotate_vector_from_center(
glp.multVec(obj.Points[edge_idx]),
angle, axis, center)
vertex1 = rotate_vector_from_center(glp.multVec(obj.Points[edge_idx]), angle, axis, center)
if utils.is_closed_edge(edge_idx, obj):
vertex2 = rotate_vector_from_center(
glp.multVec(obj.Points[0]),
angle, axis, center)
vertex2 = rotate_vector_from_center(glp.multVec(obj.Points[0]), angle, axis, center)
else:
vertex2 = rotate_vector_from_center(
glp.multVec(obj.Points[edge_idx+1]),
angle, axis, center)
glp.multVec(obj.Points[edge_idx + 1]), angle, axis, center
)
newobj = make_line.make_line(vertex1, vertex2)
gui_utils.format_object(newobj, obj)
return newobj
## @}

View File

@@ -43,8 +43,7 @@ from draftutils import params
from draftutils import utils
def scale(selection, scale, center=App.Vector(0, 0, 0),
copy=False, clone=False, subelements=False):
def scale(selection, scale, center=App.Vector(0, 0, 0), copy=False, clone=False, subelements=False):
"""scale(selection, scale, [center], [copy], [clone], [subelements])
Scales or copies selected objects.
@@ -82,8 +81,16 @@ def scale(selection, scale, center=App.Vector(0, 0, 0),
single object / list with 2 or more objects / empty list
The objects (or their copies).
"""
utils.type_check([(scale, App.Vector), (center, App.Vector),
(copy, bool), (clone, bool), (subelements, bool)], "scale")
utils.type_check(
[
(scale, App.Vector),
(center, App.Vector),
(copy, bool),
(clone, bool),
(subelements, bool),
],
"scale",
)
sx, sy, sz = scale
if sx * sy * sz == 0:
raise ValueError("Zero component in scale vector")
@@ -98,9 +105,13 @@ def scale(selection, scale, center=App.Vector(0, 0, 0),
if subelements:
return _scale_subelements(selection, scale, center, copy)
else:
objs, parent_places, sel_info = utils._modifiers_process_selection(selection, (copy or clone), scale=True)
objs, parent_places, sel_info = utils._modifiers_process_selection(
selection, (copy or clone), scale=True
)
else:
objs = utils._modifiers_filter_objects(utils._modifiers_get_group_contents(selection), (copy or clone), scale=True)
objs = utils._modifiers_filter_objects(
utils._modifiers_get_group_contents(selection), (copy or clone), scale=True
)
parent_places = None
sel_info = None
@@ -113,7 +124,9 @@ def scale(selection, scale, center=App.Vector(0, 0, 0),
if copy or clone:
for obj in objs:
if obj.isDerivedFrom("App::DocumentObjectGroup") and obj.Name not in newgroups:
newgroups[obj.Name] = obj.Document.addObject(obj.TypeId, utils.get_real_name(obj.Name))
newgroups[obj.Name] = obj.Document.addObject(
obj.TypeId, utils.get_real_name(obj.Name)
)
for idx, obj in enumerate(objs):
newobj = None
@@ -174,7 +187,9 @@ def scale(selection, scale, center=App.Vector(0, 0, 0),
continue
if sx == sy == sz:
newobj = make_clone.make_clone(obj, forcedraft=True)
newobj.Placement.Base = scale_vector_from_center(newobj.Placement.Base, scale, center)
newobj.Placement.Base = scale_vector_from_center(
newobj.Placement.Base, scale, center
)
newobj.Scale = scale
else:
if parent_place.isIdentity():
@@ -221,10 +236,10 @@ def scale(selection, scale, center=App.Vector(0, 0, 0),
else:
pla = parent_place * obj.Placement
pts = [
App.Vector (0.0, 0.0, 0.0),
App.Vector (obj.Length.Value, 0.0, 0.0),
App.Vector (obj.Length.Value, obj.Height.Value, 0.0),
App.Vector (0.0, obj.Height.Value, 0.0)
App.Vector(0.0, 0.0, 0.0),
App.Vector(obj.Length.Value, 0.0, 0.0),
App.Vector(obj.Length.Value, obj.Height.Value, 0.0),
App.Vector(0.0, obj.Height.Value, 0.0),
]
pts = [pla.multVec(p) for p in pts]
pts = [scale_vector_from_center(p, scale, center) for p in pts]
@@ -232,7 +247,7 @@ def scale(selection, scale, center=App.Vector(0, 0, 0),
x_vec = pts[1] - pts[0]
y_vec = pts[3] - pts[0]
ang = x_vec.getAngle(y_vec)
if math.isclose(ang % math.pi/2, math.pi/4, abs_tol=1e-6):
if math.isclose(ang % math.pi / 2, math.pi / 4, abs_tol=1e-6):
if copy:
newobj = make_copy.make_copy(obj)
newobj.Placement = pla
@@ -275,6 +290,7 @@ def scale(selection, scale, center=App.Vector(0, 0, 0),
if create_non_parametric:
import Part
if parent_place.isIdentity():
pla = obj.Placement
else:
@@ -288,6 +304,7 @@ def scale(selection, scale, center=App.Vector(0, 0, 0),
if App.GuiUp:
if utils.get_type(obj) in ("Circle", "Ellipse"):
from draftviewproviders.view_base import ViewProviderDraft
ViewProviderDraft(newobj.ViewObject)
else:
newobj.ViewObject.Proxy = 0
@@ -384,7 +401,7 @@ def scale_edge(obj, edge_idx, scale, center, global_place=None):
if utils.is_closed_edge(edge_idx, obj):
scale_vertex(obj, 0, scale, center, global_place)
else:
scale_vertex(obj, edge_idx+1, scale, center, global_place)
scale_vertex(obj, edge_idx + 1, scale, center, global_place)
def copy_scaled_edge(obj, edge_idx, scale, center, global_place=None):
@@ -400,9 +417,10 @@ def copy_scaled_edge(obj, edge_idx, scale, center, global_place=None):
if utils.is_closed_edge(edge_idx, obj):
vertex2 = scale_vector_from_center(glp.multVec(obj.Points[0]), scale, center)
else:
vertex2 = scale_vector_from_center(glp.multVec(obj.Points[edge_idx+1]), scale, center)
vertex2 = scale_vector_from_center(glp.multVec(obj.Points[edge_idx + 1]), scale, center)
newobj = make_line.make_line(vertex1, vertex2)
gui_utils.format_object(newobj, obj)
return newobj
## @}

View File

@@ -30,6 +30,7 @@
from draftmake import make_copy
from draftutils import utils
def split(wire, newPoint, edgeIndex):
if utils.get_type(wire) != "Wire":
return None
@@ -44,8 +45,10 @@ def split_closed_wire(wire, edgeIndex):
if edgeIndex == len(wire.Points):
new.Points = [wire.Points[0], wire.Points[-1]]
else:
new.Points = [wire.Points[edgeIndex-1], wire.Points[edgeIndex]]
wire.Points = list(reversed(wire.Points[0:edgeIndex])) + list(reversed(wire.Points[edgeIndex:]))
new.Points = [wire.Points[edgeIndex - 1], wire.Points[edgeIndex]]
wire.Points = list(reversed(wire.Points[0:edgeIndex])) + list(
reversed(wire.Points[edgeIndex:])
)
return new

File diff suppressed because it is too large Load Diff

View File

@@ -21,8 +21,7 @@
# * USA *
# * *
# ***************************************************************************
"""Provides functions to return the SVG representation of some shapes.
"""
"""Provides functions to return the SVG representation of some shapes."""
## @package svgshapes
# \ingroup draftfunctions
# \brief Provides functions to return the SVG representation of some shapes.
@@ -95,21 +94,21 @@ def get_discretized(edge, plane):
if pieces == 0:
pieces = 10
d = int(edge.Length/pieces)
d = int(edge.Length / pieces)
if d == 0:
d = 1
edata = ""
for i in range(d + 1):
_length = edge.LastParameter - edge.FirstParameter
_point = edge.FirstParameter + float(i)/d * _length
_point = edge.FirstParameter + float(i) / d * _length
_vec = edge.valueAt(_point)
v = get_proj(_vec, plane)
if not edata:
edata += 'M ' + str(v.x) + ' ' + str(v.y) + ' '
edata += "M " + str(v.x) + " " + str(v.y) + " "
else:
edata += 'L ' + str(v.x) + ' ' + str(v.y) + ' '
edata += "L " + str(v.x) + " " + str(v.y) + " "
return edata
@@ -120,9 +119,9 @@ def getDiscretized(edge, plane):
return get_discretized(edge, plane)
def _get_path_circ_ellipse(plane, edge, verts, edata,
iscircle, isellipse,
fill, stroke, linewidth, lstyle):
def _get_path_circ_ellipse(
plane, edge, verts, edata, iscircle, isellipse, fill, stroke, linewidth, lstyle
):
"""Get the edge data from a path that is a circle or ellipse."""
if plane:
drawing_plane_normal = plane.axis
@@ -166,9 +165,7 @@ def _get_path_circ_ellipse(plane, edge, verts, edata,
if not done:
if len(edge.Vertexes) == 1 and iscircle:
# Complete circle not only arc
svg = get_circle(plane,
fill, stroke, linewidth, lstyle,
edge)
svg = get_circle(plane, fill, stroke, linewidth, lstyle, edge)
# If it's a circle we will return the final SVG string,
# otherwise it will process the `edata` further
return "svg", svg
@@ -180,9 +177,8 @@ def _get_path_circ_ellipse(plane, edge, verts, edata,
# return svg
# Difference in angles
_diff = (center.LastParameter - center.FirstParameter)/2.0
endpoints = [get_proj(center.value(_diff), plane),
get_proj(verts[-1].Point, plane)]
_diff = (center.LastParameter - center.FirstParameter) / 2.0
endpoints = [get_proj(center.value(_diff), plane), get_proj(verts[-1].Point, plane)]
else:
endpoints = [get_proj(verts[-1].Point, plane)]
@@ -213,16 +209,15 @@ def _get_path_circ_ellipse(plane, edge, verts, edata,
# between tangents
_diff = edge.LastParameter - edge.FirstParameter
t1 = edge.tangentAt(edge.FirstParameter)
t2 = edge.tangentAt(edge.FirstParameter + _diff/10)
t2 = edge.tangentAt(edge.FirstParameter + _diff / 10)
flag_sweep = DraftVecUtils.angle(t1, t2, drawing_plane_normal) < 0
for v in endpoints:
edata += ('A {} {} {} '
'{} {} '
'{} {} '.format(rx, ry, rot,
int(flag_large_arc),
int(flag_sweep),
v.x, v.y))
edata += (
"A {} {} {} "
"{} {} "
"{} {} ".format(rx, ry, rot, int(flag_large_arc), int(flag_sweep), v.x, v.y)
)
return "edata", edata
@@ -232,7 +227,7 @@ def _get_path_bspline(plane, edge, edata):
bspline = edge.Curve.toBSpline(edge.FirstParameter, edge.LastParameter)
if bspline.Degree > 3 or bspline.isRational():
try:
bspline = bspline.approximateBSpline(0.05, 50, 3, 'C0')
bspline = bspline.approximateBSpline(0.05, 50, 3, "C0")
except RuntimeError:
_wrn("Debug: unable to approximate bspline from edge")
@@ -242,30 +237,30 @@ def _get_path_bspline(plane, edge, edata):
_wrn("Bezier segment of degree > 3")
raise AssertionError
elif bezierseg.Degree == 1:
edata += 'L '
edata += "L "
elif bezierseg.Degree == 2:
edata += 'Q '
edata += "Q "
elif bezierseg.Degree == 3:
edata += 'C '
edata += "C "
for pole in bezierseg.getPoles()[1:]:
v = get_proj(pole, plane)
edata += '{} {} '.format(v.x, v.y)
edata += "{} {} ".format(v.x, v.y)
else:
_msg("Debug: one edge (hash {}) "
"has been discretized "
"with parameter 0.1".format(edge.hashCode()))
_msg(
"Debug: one edge (hash {}) "
"has been discretized "
"with parameter 0.1".format(edge.hashCode())
)
for linepoint in bspline.discretize(0.1)[1:]:
v = get_proj(linepoint, plane)
edata += 'L {} {} '.format(v.x, v.y)
edata += "L {} {} ".format(v.x, v.y)
return edata
def get_circle(plane,
fill, stroke, linewidth, lstyle,
edge):
def get_circle(plane, fill, stroke, linewidth, lstyle, edge):
"""Get the SVG representation from a circular edge."""
cen = get_proj(edge.Curve.Center, plane)
rad = edge.Curve.Radius
@@ -277,7 +272,7 @@ def get_circle(plane,
if round(edge.Curve.Axis.getAngle(drawing_plane_normal), 2) in [0, 3.14]:
# Perpendicular projection: circle
svg = '<circle '
svg = "<circle "
svg += 'cx="{}" cy="{}" r="{}" '.format(cen.x, cen.y, rad)
else:
# Any other projection: ellipse
@@ -288,57 +283,60 @@ def get_circle(plane,
# for the generated SVG?
svg += 'stroke-width="{} px" '.format(linewidth)
svg += 'style="'
svg += 'stroke-width:{};'.format(linewidth)
svg += 'stroke-miterlimit:4;'
svg += 'stroke-dasharray:{};'.format(lstyle)
svg += 'stroke-linecap:square;'
svg += 'fill:{}'.format(fill) + '"'
svg += '/>\n'
svg += "stroke-width:{};".format(linewidth)
svg += "stroke-miterlimit:4;"
svg += "stroke-dasharray:{};".format(lstyle)
svg += "stroke-linecap:square;"
svg += "fill:{}".format(fill) + '"'
svg += "/>\n"
return svg
def getCircle(plane,
fill, stroke, linewidth, lstyle,
edge):
def getCircle(plane, fill, stroke, linewidth, lstyle, edge):
"""Get the SVG representation from a circular edge."""
utils.use_instead("get_circle")
return get_circle(plane, fill, stroke, linewidth, lstyle, edge)
def get_ellipse(plane,
fill, stroke, linewidth, lstyle,
edge):
def get_ellipse(plane, fill, stroke, linewidth, lstyle, edge):
"""Get the SVG representation from an elliptical edge."""
cen = get_proj(edge.Curve.Center, plane)
mir = edge.Curve.MinorRadius
mar = edge.Curve.MajorRadius
svg = '<ellipse '
svg = "<ellipse "
svg += 'cx="{}" cy="{}" '.format(cen.x, cen.y)
svg += 'rx="{}" ry="{}" '.format(mar, mir)
svg += 'stroke="{}" '.format(stroke)
svg += 'stroke-width="{} px" '.format(linewidth)
svg += 'style="'
svg += 'stroke-width:{};'.format(linewidth)
svg += 'stroke-miterlimit:4;'
svg += 'stroke-dasharray:{};'.format(lstyle)
svg += 'stroke-linecap:square;'
svg += 'fill:{}'.format(fill) + '"'
svg += '/>\n'
svg += "stroke-width:{};".format(linewidth)
svg += "stroke-miterlimit:4;"
svg += "stroke-dasharray:{};".format(lstyle)
svg += "stroke-linecap:square;"
svg += "fill:{}".format(fill) + '"'
svg += "/>\n"
return svg
def getEllipse(plane,
fill, stroke, linewidth, lstyle,
edge):
def getEllipse(plane, fill, stroke, linewidth, lstyle, edge):
"""Get the SVG representation from an elliptical edge. DEPRECATED."""
utils.use_instead("get_ellipse")
return get_ellipse(plane, fill, stroke, linewidth, lstyle, edge)
def get_path(obj, plane,
fill, pathdata, stroke, linewidth, lstyle,
fill_opacity=None,
edges=[], wires=[], pathname=None):
def get_path(
obj,
plane,
fill,
pathdata,
stroke,
linewidth,
lstyle,
fill_opacity=None,
edges=[],
wires=[],
pathname=None,
):
"""Get the SVG representation from an object's edges or wires.
TODO: the `edges` and `wires` must not default to empty list `[]`
@@ -382,28 +380,27 @@ def get_path(obj, plane,
if len(_edges) > 1:
last_pt = verts[-1].Point
nextverts = _edges[1].Vertexes
if (last_pt - nextverts[0].Point).Length > 1e-6 \
and (last_pt - nextverts[-1].Point).Length > 1e-6:
if (last_pt - nextverts[0].Point).Length > 1e-6 and (
last_pt - nextverts[-1].Point
).Length > 1e-6:
verts.reverse()
v = get_proj(verts[0].Point, plane)
edata += 'M {} {} '.format(v.x, v.y)
edata += "M {} {} ".format(v.x, v.y)
else:
previousverts = verts
verts = edge.Vertexes
if (verts[0].Point - previousverts[-1].Point).Length > 1e-6:
verts.reverse()
if (verts[0].Point - previousverts[-1].Point).Length > 1e-6:
raise ValueError('edges not ordered')
raise ValueError("edges not ordered")
iscircle = DraftGeomUtils.geomType(edge) == "Circle"
isellipse = DraftGeomUtils.geomType(edge) == "Ellipse"
if iscircle or isellipse:
_type, data = _get_path_circ_ellipse(plane, edge, verts,
edata,
iscircle, isellipse,
fill, stroke,
linewidth, lstyle)
_type, data = _get_path_circ_ellipse(
plane, edge, verts, edata, iscircle, isellipse, fill, stroke, linewidth, lstyle
)
if _type == "svg":
# final svg string already calculated, so just return it
return data
@@ -412,14 +409,14 @@ def get_path(obj, plane,
edata = data
elif DraftGeomUtils.geomType(edge) == "Line":
v = get_proj(verts[-1].Point, plane)
edata += 'L {} {} '.format(v.x, v.y)
edata += "L {} {} ".format(v.x, v.y)
else:
# If it's not a circle nor ellipse nor straight line
# convert the curve to BSpline
edata = _get_path_bspline(plane, edge, edata)
if fill != 'none':
edata += 'Z '
if fill != "none":
edata += "Z "
if edata in pathdata:
# do not draw a path on another identical path
@@ -432,29 +429,48 @@ def get_path(obj, plane,
svg += 'stroke="{}" '.format(stroke)
svg += 'stroke-width="{} px" '.format(linewidth)
svg += 'style="'
svg += 'stroke-width:{};'.format(linewidth)
svg += 'stroke-miterlimit:4;'
svg += 'stroke-dasharray:{};'.format(lstyle)
svg += 'stroke-linecap:square;'
svg += 'fill:{};'.format(fill)
svg += "stroke-width:{};".format(linewidth)
svg += "stroke-miterlimit:4;"
svg += "stroke-dasharray:{};".format(lstyle)
svg += "stroke-linecap:square;"
svg += "fill:{};".format(fill)
# fill_opacity must be a number, but if it's `None` it is omitted
if fill_opacity is not None:
svg += 'fill-opacity:{};'.format(fill_opacity)
svg += "fill-opacity:{};".format(fill_opacity)
svg += 'fill-rule: evenodd"'
svg += '/>\n'
svg += "/>\n"
return svg
def getPath(obj, plane,
fill, pathdata, stroke, linewidth, lstyle,
fill_opacity,
edges=[], wires=[], pathname=None):
def getPath(
obj,
plane,
fill,
pathdata,
stroke,
linewidth,
lstyle,
fill_opacity,
edges=[],
wires=[],
pathname=None,
):
"""Get the SVG representation from a path. DEPRECATED."""
utils.use_instead("get_path")
return get_path(obj, plane,
fill, pathdata, stroke, linewidth, lstyle,
fill_opacity,
edges=edges, wires=wires, pathname=pathname)
return get_path(
obj,
plane,
fill,
pathdata,
stroke,
linewidth,
lstyle,
fill_opacity,
edges=edges,
wires=wires,
pathname=pathname,
)
## @}

View File

@@ -36,9 +36,7 @@ import draftutils.utils as utils
# @{
def _get_text_techdraw(text, tcolor, fontsize, anchor,
align, fontname, angle, base,
linespacing):
def _get_text_techdraw(text, tcolor, fontsize, anchor, align, fontname, angle, base, linespacing):
"""Return the SVG representation of text for TechDraw display.
`text` is a list of textual elements; they are iterated, styled,
@@ -56,28 +54,23 @@ def _get_text_techdraw(text, tcolor, fontsize, anchor,
_t = _t.replace("<", "&lt;")
t = _t.replace(">", "&gt;")
svg += '<text '
svg += "<text "
svg += 'stroke-width="0" stroke="{}" '.format(tcolor)
svg += 'fill="{}" font-size="{}" '.format(tcolor, fontsize)
svg += 'style="text-anchor:{};text-align:{};'.format(anchor,
align.lower())
svg += 'style="text-anchor:{};text-align:{};'.format(anchor, align.lower())
svg += 'font-family:{}" '.format(fontname)
svg += 'transform="'
svg += 'rotate({},{},{}) '.format(math.degrees(angle),
point.x,
point.y)
svg += 'translate({},{}) '.format(point.x,
point.y)
svg += "rotate({},{},{}) ".format(math.degrees(angle), point.x, point.y)
svg += "translate({},{}) ".format(point.x, point.y)
svg += 'scale(1,-1)"'
# svg += 'freecad:skip="1"'
svg += '>\n'
svg += ">\n"
svg += t
svg += '</text>\n'
svg += "</text>\n"
return svg
def _get_text_header(tcolor, fontsize, anchor, align,
fontname, angle, base, flip):
def _get_text_header(tcolor, fontsize, anchor, align, fontname, angle, base, flip):
"""Return the initial <text> tag with style options.
The text must be added after this tag, and then must be closed.
@@ -86,37 +79,43 @@ def _get_text_header(tcolor, fontsize, anchor, align,
...
</text>
"""
svg = '<text '
svg = "<text "
svg += 'stroke-width="0" stroke="{}" '.format(tcolor)
svg += 'fill="{}" font-size="{}" '.format(tcolor, fontsize)
svg += 'style="text-anchor:{};text-align:{};'.format(anchor,
align.lower())
svg += 'style="text-anchor:{};text-align:{};'.format(anchor, align.lower())
svg += 'font-family:{}" '.format(fontname)
svg += 'transform="'
svg += 'rotate({},{},{}) '.format(math.degrees(angle),
base.x,
base.y)
svg += "rotate({},{},{}) ".format(math.degrees(angle), base.x, base.y)
if flip:
svg += 'translate({},{}) '.format(base.x, base.y)
svg += "translate({},{}) ".format(base.x, base.y)
else:
svg += 'translate({},{}) '.format(base.x, -base.y)
svg += "translate({},{}) ".format(base.x, -base.y)
# svg += 'scale({},-{}) '.format(tmod/2000, tmod/2000)
if flip:
svg += 'scale(1,-1) '
svg += "scale(1,-1) "
else:
svg += 'scale(1,1) '
svg += "scale(1,1) "
svg += '" '
svg += 'freecad:skip="1"'
svg += '>\n'
svg += ">\n"
return svg
def get_text(plane, techdraw,
tcolor, fontsize, fontname,
angle, base, text,
linespacing=0.5, align="center", flip=True):
def get_text(
plane,
techdraw,
tcolor,
fontsize,
fontname,
angle,
base,
text,
linespacing=0.5,
align="center",
flip=True,
):
"""Get the SVG representation of a textual element."""
if isinstance(angle, App.Rotation):
if not plane:
@@ -129,14 +128,14 @@ def get_text(plane, techdraw,
angle = -angle.Angle
else:
angle = angle.Angle
elif abs(plane.axis.getAngle(angle.Axis) - math.pi/2) < 0.001:
elif abs(plane.axis.getAngle(angle.Axis) - math.pi / 2) < 0.001:
# text is perpendicular to view, so it shouldn't appear
return ""
else:
# Compute an X vector
vec = App.Vector(1,0,0)
vec = angle.multVec(App.Vector(1,0,0))
angle = DraftVecUtils.angle(vec,plane.u)
vec = App.Vector(1, 0, 0)
vec = angle.multVec(App.Vector(1, 0, 0))
angle = DraftVecUtils.angle(vec, plane.u)
# text should be a list of strings separated by a newline
if not isinstance(text, list):
@@ -154,9 +153,9 @@ def get_text(plane, techdraw,
# in an individual tag.
# <text ...> text[0] </text>
# <text ...> text[1] </text>
svg = _get_text_techdraw(text, tcolor, fontsize, anchor,
align, fontname, angle, base,
linespacing)
svg = _get_text_techdraw(
text, tcolor, fontsize, anchor, align, fontname, angle, base, linespacing
)
else:
# If the SVG is not for TechDraw, and there is a single item
# in the text list, place it in a single tag.
@@ -167,8 +166,7 @@ def get_text(plane, techdraw,
# <tspan>text[0]</tspan>
# <tspan>text[1]</tspan>
# </text>
svg = _get_text_header(tcolor, fontsize, anchor, align,
fontname, angle, base, flip)
svg = _get_text_header(tcolor, fontsize, anchor, align, fontname, angle, base, flip)
if len(text) == 1:
_t = text[0].replace("&", "&amp;").replace("<", "&lt;")
@@ -176,25 +174,34 @@ def get_text(plane, techdraw,
else:
for i in range(len(text)):
if i == 0:
svg += '<tspan>'
svg += "<tspan>"
else:
svg += '<tspan x="0" dy="{}">'.format(linespacing)
_t = text[i].replace("&", "&amp;").replace("<", "&lt;")
svg += _t.replace(">", "&gt;")
svg += '</tspan>\n'
svg += '</text>\n'
svg += "</tspan>\n"
svg += "</text>\n"
return svg
def getText(plane, techdraw,
tcolor, fontsize, fontname,
angle, base, text,
linespacing=0.5, align="center", flip=True):
def getText(
plane,
techdraw,
tcolor,
fontsize,
fontname,
angle,
base,
text,
linespacing=0.5,
align="center",
flip=True,
):
"""Get the SVG representation of a textual element. DEPRECATED."""
utils.use_instead("get_text")
return get_text(plane, techdraw,
tcolor, fontsize, fontname,
angle, base, text,
linespacing, align, flip)
return get_text(
plane, techdraw, tcolor, fontsize, fontname, angle, base, text, linespacing, align, flip
)
## @}

View File

@@ -220,8 +220,9 @@ def upgrade(objects, delete=False, force=None):
new_area = new_face.Area
new_cen = new_face.CenterOfMass
for old_area, old_cen, old_color in old_data:
if math.isclose(new_area, old_area, abs_tol=1e-7) \
and new_cen.isEqual(old_cen, 1e-7):
if math.isclose(new_area, old_area, abs_tol=1e-7) and new_cen.isEqual(
old_cen, 1e-7
):
new_colors.append(old_color)
break
newobj.ViewObject.DiffuseColor = new_colors
@@ -316,8 +317,11 @@ def upgrade(objects, delete=False, force=None):
for cluster in sorted_edges:
for edge in cluster:
print("Curve: {}".format(edge.Curve))
print("first: {}, last: {}".format(edge.Vertexes[0].Point,
edge.Vertexes[-1].Point))
print(
"first: {}, last: {}".format(
edge.Vertexes[0].Point, edge.Vertexes[-1].Point
)
)
wires = [Part.Wire(cluster) for cluster in sorted_edges]
except Part.OCCError:
return False
@@ -349,7 +353,6 @@ def upgrade(objects, delete=False, force=None):
delete_list.append(obj)
return True
# helper functions (same as in downgrade.py)
def get_parent(obj):
@@ -413,7 +416,6 @@ def upgrade(objects, delete=False, force=None):
for newobj in new_list:
gui_utils.format_object(newobj, obj, ignore_construction=True)
doc = App.ActiveDocument
add_list = []
delete_list = []
@@ -476,17 +478,21 @@ def upgrade(objects, delete=False, force=None):
result = turnToParts(meshes)
else:
# functions that work on a single object:
single_funcs = {"closeWire": closeWire,
"draftify": _draftify,
"makeSketchFace": makeSketchFace,
"makeSolid": makeSolid}
single_funcs = {
"closeWire": closeWire,
"draftify": _draftify,
"makeSketchFace": makeSketchFace,
"makeSolid": makeSolid,
}
# functions that work on multiple objects:
multi_funcs = {"joinFaces": joinFaces,
"makeCompound": makeCompound,
"makeFaces": makeFaces,
"makeFusion": makeFusion,
"makeShell": makeShell,
"makeWires": makeWires}
multi_funcs = {
"joinFaces": joinFaces,
"makeCompound": makeCompound,
"makeFaces": makeFaces,
"makeFusion": makeFusion,
"makeShell": makeShell,
"makeWires": makeWires,
}
if force in single_funcs:
result = any([single_funcs[force](obj) for obj in objects])
elif force in multi_funcs:
@@ -536,39 +542,55 @@ def upgrade(objects, delete=False, force=None):
_msg(translate("draft", "Found 1 solidifiable object: solidifying it"))
# we have exactly 2 objects: we fuse them
elif len(objects) == 2 \
and not faces_coplanarity \
and same_parent \
and same_parent_type != "PartDesign::Body":
elif (
len(objects) == 2
and not faces_coplanarity
and same_parent
and same_parent_type != "PartDesign::Body"
):
result = makeFusion(objects)
if result:
_msg(translate("draft", "Found 2 objects: fusing them"))
# we have many separate faces: we try to make a shell or compound
elif len(objects) > 1 \
and len(faces) > 1 \
and same_parent \
and same_parent_type != "PartDesign::Body":
elif (
len(objects) > 1
and len(faces) > 1
and same_parent
and same_parent_type != "PartDesign::Body"
):
result = makeShell(objects)
if result:
_msg(translate(
"draft",
"Found several objects: creating a " + result.Shape.ShapeType
))
_msg(
translate(
"draft", "Found several objects: creating a " + result.Shape.ShapeType
)
)
# we have faces: we try to join them if they are coplanar
elif len(objects) == 1 and len(faces) > 1 and faces_coplanarity:
result = joinFaces(objects)
if result:
_msg(translate("draft", "Found object with several coplanar faces: refining them"))
_msg(
translate(
"draft", "Found object with several coplanar faces: refining them"
)
)
# only one object: if not parametric, we "draftify" it
elif len(objects) == 1 \
and not objects[0].isDerivedFrom("Part::Part2DObjectPython") \
and not utils.get_type(objects[0]) in ["BezCurve", "BSpline", "Wire"]:
elif (
len(objects) == 1
and not objects[0].isDerivedFrom("Part::Part2DObjectPython")
and not utils.get_type(objects[0]) in ["BezCurve", "BSpline", "Wire"]
):
result = _draftify(objects[0])
if result:
_msg(translate("draft", "Found 1 non-parametric object: replacing it with a Draft object"))
_msg(
translate(
"draft",
"Found 1 non-parametric object: replacing it with a Draft object",
)
)
# in the following cases there are no faces
elif not faces:
@@ -579,7 +601,11 @@ def upgrade(objects, delete=False, force=None):
if len(objects) == 1 and objects[0].isDerivedFrom("Sketcher::SketchObject"):
result = makeSketchFace(objects[0])
if result:
_msg(translate("draft", "Found 1 closed sketch object: creating a face from it"))
_msg(
translate(
"draft", "Found 1 closed sketch object: creating a face from it"
)
)
# only closed wires
else:
result = makeFaces(objects)
@@ -594,7 +620,11 @@ def upgrade(objects, delete=False, force=None):
else:
result = makeCompound(objects)
if result:
_msg(translate("draft", "Found several non-treatable objects: creating compound"))
_msg(
translate(
"draft", "Found several non-treatable objects: creating compound"
)
)
# special case, we have only one open wire. We close it, unless it has only 1 edge!
elif len(objects) == 1 and len(openwires) == 1:
@@ -603,16 +633,23 @@ def upgrade(objects, delete=False, force=None):
_msg(translate("draft", "Found 1 open wire: closing it"))
# only one object: if not parametric, we "draftify" it
elif len(objects) == 1 \
and len(edges) == 1 \
and not objects[0].isDerivedFrom("Part::Part2DObjectPython") \
and not utils.get_type(objects[0]) in ["BezCurve", "BSpline", "Wire"]:
elif (
len(objects) == 1
and len(edges) == 1
and not objects[0].isDerivedFrom("Part::Part2DObjectPython")
and not utils.get_type(objects[0]) in ["BezCurve", "BSpline", "Wire"]
):
edge_type = DraftGeomUtils.geomType(objects[0].Shape.Edges[0])
# currently only support Line and Circle
if edge_type in ("Line", "Circle"):
result = _draftify(objects[0])
if result:
_msg(translate("draft", "Found 1 non-parametric object: replacing it with a Draft object"))
_msg(
translate(
"draft",
"Found 1 non-parametric object: replacing it with a Draft object",
)
)
# only points, no edges
elif not edges and len(objects) > 1:
@@ -637,4 +674,5 @@ def upgrade(objects, delete=False, force=None):
gui_utils.select(add_list)
return add_list, delete_list
## @}