Merge branch 'master' into master

This commit is contained in:
Dino del Favero
2020-12-14 22:24:50 +01:00
committed by GitHub
17 changed files with 218 additions and 143 deletions

View File

@@ -1241,14 +1241,14 @@ class _Wall(ArchComponent.Component):
# in some corner case != getSortedClusters()
elif obj.Base.isDerivedFrom("Sketcher::SketchObject"):
self.basewires = []
skGeom = obj.Base.Geometry
skGeom = obj.Base.GeometryFacadeList
skGeomEdges = []
skPlacement = obj.Base.Placement # Get Sketch's placement to restore later
for i in skGeom:
if not i.Construction:
# support Line, Arc, Circle for Sketch as Base at the moment
if isinstance(i, (Part.LineSegment, Part.Circle, Part.ArcOfCircle)):
skGeomEdgesI = i.toShape()
if isinstance(i.Geometry, (Part.LineSegment, Part.Circle, Part.ArcOfCircle)):
skGeomEdgesI = i.Geometry.toShape()
skGeomEdges.append(skGeomEdgesI)
for cluster in Part.getSortedClusters(skGeomEdges):
clusterTransformed = []

View File

@@ -1107,6 +1107,7 @@ class DraftToolBar:
self.continueCmd.show()
def modUi(self):
self.undoButton.hide()
self.isCopy.show()
self.isSubelementMode.show()
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")

View File

@@ -42,6 +42,7 @@ import draftmake.make_block as make_block
from draftutils.messages import _msg, _err
from draftutils.translate import _tr
from draftgeoutils.geometry import is_straight_line
# Delay import of module until first use because it is heavy
Part = lz.LazyLoader("Part", globals(), "Part")
@@ -160,6 +161,8 @@ def upgrade(objects, delete=False, force=None):
return None
if len(obj.Shape.Edges) == 1:
return None
if is_straight_line(obj.Shape) == True:
return None
if utils.get_type(obj) == "Wire":
obj.Closed = True
return True
@@ -333,26 +336,35 @@ def upgrade(objects, delete=False, force=None):
def makeWires(objectslist):
"""Join edges in the given objects list into wires."""
edges = []
for o in objectslist:
for e in o.Shape.Edges:
edges.append(e)
for object in objectslist:
for edge in object.Shape.Edges:
edges.append(edge)
try:
nedges = Part.__sortEdges__(edges[:])
sorted_edges = Part.sortEdges(edges)
if _DEBUG:
for e in nedges:
print("Curve: {}".format(e.Curve))
print("first: {}, last: {}".format(e.Vertexes[0].Point,
for item_sorted_edges in sorted_edges:
for e in item_sorted_edges:
print("Curve: {}".format(e.Curve))
print("first: {}, last: {}".format(e.Vertexes[0].Point,
e.Vertexes[-1].Point))
w = Part.Wire(nedges)
wires = [Part.Wire(e) for e in sorted_edges]
except Part.OCCError:
return None
else:
if len(w.Edges) == len(edges):
for wire in wires:
newobj = doc.addObject("Part::Feature", "Wire")
newobj.Shape = w
newobj.Shape = wire
add_list.append(newobj)
delete_list.extend(objectslist)
return True
# delete object only if there are no links to it
# TODO: A more refined criteria to delete object
for object in objectslist:
if object.InList:
if App.GuiUp:
object.ViewObject.Visibility = False
else:
delete_list.append(object)
return True
return None
# analyzing what we have in our selection
@@ -475,54 +487,56 @@ def upgrade(objects, delete=False, force=None):
_msg(_tr("Found 1 non-parametric objects: "
"draftifying it"))
# we have only one object that contains one edge
elif not faces and len(objects) == 1 and len(edges) == 1:
# we have a closed sketch: extract a face
if (objects[0].isDerivedFrom("Sketcher::SketchObject")
and len(edges[0].Vertexes) == 1):
result = makeSketchFace(objects[0])
# in the following cases there are no faces
elif not faces:
# we have only closed wires
if wires and not openwires and not loneedges:
# we have a sketch: extract a face
if (len(objects) == 1
and objects[0].isDerivedFrom("Sketcher::SketchObject")):
result = makeSketchFace(objects[0])
if result:
_msg(_tr("Found 1 closed sketch object: "
"creating a face from it"))
# only closed wires
else:
result = makeFaces(objects)
if result:
_msg(_tr("Found closed wires: creating faces"))
# wires or edges: we try to join them
elif len(wires) > 1 or len(loneedges) > 1:
result = makeWires(objects)
if result:
_msg(_tr("Found 1 closed sketch object: "
"creating a face from it"))
else:
_msg(_tr("Found several wires or edges: wiring them"))
# TODO: improve draftify function
# only one object: if not parametric, we "draftify" it
# elif (len(objects) == 1
# and not objects[0].isDerivedFrom("Part::Part2DObjectPython")):
# result = ext_draftify.draftify(objects[0])
# if result:
# _msg(_tr("Found 1 non-parametric objects: "
# "draftifying it"))
# 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:
result = closeWire(objects[0])
_msg(_tr("trying: closing it"))
if result:
_msg(_tr("Found 1 open wire: closing it"))
# we have only one object that contains one edge
# TODO: this case should be considered in draftify
elif len(objects) == 1 and len(edges) == 1:
# turn to Draft Line
e = objects[0].Shape.Edges[0]
if isinstance(e.Curve, (Part.LineSegment, Part.Line)):
result = turnToLine(objects[0])
if result:
_msg(_tr("Found 1 linear object: converting to line"))
# we have only closed wires, no faces
elif wires and not faces and not openwires:
# we have a sketch: extract a face
if (len(objects) == 1
and objects[0].isDerivedFrom("Sketcher::SketchObject")):
result = makeSketchFace(objects[0])
# only points, no edges
elif not edges and len(objects) > 1:
result = makeCompound(objects)
if result:
_msg(_tr("Found 1 closed sketch object: "
"creating a face from it"))
# only closed wires
else:
result = makeFaces(objects)
if result:
_msg(_tr("Found closed wires: creating faces"))
# special case, we have only one open wire. We close it,
# unless it has only 1 edge!
elif len(openwires) == 1 and not faces and not loneedges:
result = closeWire(objects[0])
if result:
_msg(_tr("Found 1 open wire: closing it"))
# only open wires and edges: we try to join their edges
elif openwires and not wires and not faces:
result = makeWires(objects)
if result:
_msg(_tr("Found several open wires: joining them"))
# only loneedges: we try to join them
elif loneedges and not facewires:
result = makeWires(objects)
if result:
_msg(_tr("Found several edges: wiring them"))
_msg(_tr("Found points: creating compound"))
# all other cases, if more than 1 object, make a compound
elif len(objects) > 1:
result = makeCompound(objects)

View File

@@ -53,21 +53,21 @@ class ScaleTaskPanel:
layout.addWidget(self.xLabel, 0, 0, 1, 1)
self.xValue = QtGui.QDoubleSpinBox()
self.xValue.setRange(0.0000001, 1000000.0)
self.xValue.setDecimals(Draft.getParam("precision"))
self.xValue.setDecimals(Draft.precision())
self.xValue.setValue(1)
layout.addWidget(self.xValue,0,1,1,1)
self.yLabel = QtGui.QLabel()
layout.addWidget(self.yLabel,1,0,1,1)
self.yValue = QtGui.QDoubleSpinBox()
self.yValue.setRange(.0000001,1000000.0)
self.yValue.setDecimals(Draft.getParam("precision"))
self.yValue.setDecimals(Draft.precision())
self.yValue.setValue(1)
layout.addWidget(self.yValue,1,1,1,1)
self.zLabel = QtGui.QLabel()
layout.addWidget(self.zLabel,2,0,1,1)
self.zValue = QtGui.QDoubleSpinBox()
self.zValue.setRange(.0000001,1000000.0)
self.zValue.setDecimals(Draft.getParam("precision"))
self.zValue.setDecimals(Draft.precision())
self.zValue.setValue(1)
layout.addWidget(self.zValue,2,1,1,1)
self.lock = QtGui.QCheckBox()

View File

@@ -232,7 +232,7 @@ class DraftModification(unittest.TestCase):
self.assertTrue(obj, "'{}' failed".format(operation))
def test_upgrade(self):
"""Upgrade two Draft Lines into a closed Draft Wire."""
"""Upgrade two Lines into a closed Wire, then draftify it."""
operation = "Draft Upgrade"
_msg(" Test '{}'".format(operation))
a = Vector(0, 0, 0)
@@ -242,8 +242,12 @@ class DraftModification(unittest.TestCase):
_msg(" a={0}, b={1}".format(a, b))
_msg(" Line 2")
_msg(" b={0}, c={1}".format(b, c))
line_1 = Draft.make_line(a, b)
line_2 = Draft.make_line(b, c)
shape_line_1 = Part.makeLine(a, b)
shape_line_2 = Part.makeLine(b, c)
line_1 = App.ActiveDocument.addObject("Part::Feature")
line_2 = App.ActiveDocument.addObject("Part::Feature")
line_1.Shape = shape_line_1
line_2.Shape = shape_line_2
App.ActiveDocument.recompute()
obj = Draft.upgrade([line_1, line_2], delete=True)
@@ -255,8 +259,7 @@ class DraftModification(unittest.TestCase):
obj2 = Draft.upgrade(obj[0], delete=True)
App.ActiveDocument.recompute()
s2 = obj2[0][0]
_msg(" 2: Result '{0}' ({1})".format(s2.Shape.ShapeType,
s2.TypeId))
_msg(" 2: Result '{0}' ({1})".format(s2.Shape.ShapeType, s2.TypeId))
self.assertTrue(bool(obj2[0]), "'{}' failed".format(operation))
obj3 = Draft.upgrade(obj2[0], delete=True)
@@ -265,10 +268,15 @@ class DraftModification(unittest.TestCase):
_msg(" 3: Result '{0}' ({1})".format(s3.Shape.ShapeType, s3.TypeId))
self.assertTrue(bool(obj3[0]), "'{}' failed".format(operation))
obj4 = Draft.upgrade(obj3[0], delete=True)
# when draftify, upgrade dont return a new object
Draft.upgrade(obj3[0], delete=True)
App.ActiveDocument.recompute()
wire = App.ActiveDocument.Wire
_msg(" 4: Result '{0}' ({1})".format(wire.Proxy.Type, wire.TypeId))
self.assertTrue(bool(wire), "'{}' failed".format(operation))
obj4 = Draft.upgrade(wire, delete=True)
App.ActiveDocument.recompute()
_msg(" The last object cannot be upgraded further")
self.assertFalse(bool(obj4[0]), "'{}' failed".format(operation))

View File

@@ -157,9 +157,9 @@ def process(doc, filename):
with the parsed information.
"""
# Regex to identify data rows and throw away unused metadata
xval = '(?P<xval>(\-|\d*)\.\d+(E\-?\d+)?)'
yval = '(?P<yval>\-?\s*\d*\.\d+(E\-?\d+)?)'
_regex = '^\s*' + xval + '\,?\s*' + yval + '\s*$'
xval = r'(?P<xval>(\-|\d*)\.*\d*([Ee]\-?\d+)?)'
yval = r'(?P<yval>\-?\s*\d*\.*\d*([Ee]\-?\d+)?)'
_regex = r'^\s*' + xval + r'\,?\s*' + yval + r'\s*$'
regex = re.compile(_regex)
afile = pythonopen(filename, 'r')

View File

@@ -171,6 +171,8 @@
# include <APIHeaderSection_MakeHeader.hxx>
# include <ShapeAnalysis_FreeBoundsProperties.hxx>
# include <ShapeAnalysis_FreeBoundData.hxx>
# include <BRepLProp_SLProps.hxx>
# include <BRepGProp_Face.hxx>
#if OCC_VERSION_HEX < 0x070300
# include <BRepAlgo_Fuse.hxx>
@@ -4161,26 +4163,13 @@ bool TopoShape::findPlane(gp_Pln &pln, double tol) const {
if(_Shape.IsNull())
return false;
TopoDS_Shape shape = _Shape;
TopExp_Explorer exp(_Shape,TopAbs_FACE);
TopExp_Explorer exp(_Shape,TopAbs_EDGE);
if(exp.More()) {
auto face = exp.Current();
TopoDS_Shape edge = exp.Current();
exp.Next();
if(!exp.More()) {
BRepAdaptor_Surface adapt(TopoDS::Face(face));
if(adapt.GetType() != GeomAbs_Plane)
return false;
pln = adapt.Plane();
return true;
}
}else{
TopExp_Explorer exp(_Shape,TopAbs_EDGE);
if(exp.More()) {
TopoDS_Shape edge = exp.Current();
exp.Next();
if(!exp.More()) {
// To deal with OCCT bug of wrong edge transformation
shape = BRepBuilderAPI_Copy(edge).Shape();
}
if (!exp.More()) {
// To deal with OCCT bug of wrong edge transformation
shape = BRepBuilderAPI_Copy(_Shape).Shape();
}
}
try {
@@ -4188,6 +4177,24 @@ bool TopoShape::findPlane(gp_Pln &pln, double tol) const {
if (!finder.Found())
return false;
pln = GeomAdaptor_Surface(finder.Surface()).Plane();
// To make the returned plane normal more stable, if the shape has any
// face, use the normal of the first face.
TopExp_Explorer exp(shape, TopAbs_FACE);
if(exp.More()) {
BRepAdaptor_Surface adapt(TopoDS::Face(exp.Current()));
double u = adapt.FirstUParameter()
+ (adapt.LastUParameter() - adapt.FirstUParameter())/2.;
double v = adapt.FirstVParameter()
+ (adapt.LastVParameter() - adapt.FirstVParameter())/2.;
BRepLProp_SLProps prop(adapt,u,v,2,Precision::Confusion());
if(prop.IsNormalDefined()) {
gp_Pnt pnt; gp_Vec vec;
// handles the orientation state of the shape
BRepGProp_Face(TopoDS::Face(exp.Current())).Normal(u,v,pnt,vec);
pln = gp_Pln(pnt, gp_Dir(vec));
}
}
return true;
}catch (Standard_Failure &e) {
// For some reason the above BRepBuilderAPI_Copy failed to copy

View File

@@ -2963,20 +2963,19 @@ std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &shapes,
pstart = *_pstart;
bool use_bound = !has_start || _pstart==NULL;
if(use_bound || sort_mode == SortMode2D5 || sort_mode == SortModeGreedy) {
//Second stage, group shape by its plane, and find overall boundary
//Second stage, group shape by its plane, and find overall boundary
if(arcPlaneFound || use_bound) {
for(auto &info : shape_list) {
if(arcPlaneFound) {
info.myShape.Move(trsf);
if(info.myPlanar) info.myPln.Transform(trsf);
}
if(use_bound)
BRepBndLib::Add(info.myShape, bounds, Standard_False);
}
for(auto &info : shape_list) {
if(arcPlaneFound) {
info.myShape.Move(trsf);
if(info.myPlanar) info.myPln.Transform(trsf);
}
BRepBndLib::Add(info.myShape, bounds, Standard_False);
}
if(use_bound || sort_mode == SortMode2D5 || sort_mode == SortModeGreedy) {
for(auto itNext=shape_list.begin(),it=itNext;it!=shape_list.end();it=itNext) {
++itNext;
if(!it->myPlanar) continue;
@@ -3005,16 +3004,36 @@ std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &shapes,
//FC_DURATION_DECL_INIT(td);
bounds.SetGap(0.0);
Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
AREA_TRACE("bound (" << xMin<<", "<<xMax<<"), ("<<
yMin<<", "<<yMax<<"), ("<<zMin<<", "<<zMax<<')');
if(use_bound) {
bounds.SetGap(0.0);
Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
AREA_TRACE("bound (" << xMin<<", "<<xMax<<"), ("<<
yMin<<", "<<yMax<<"), ("<<zMin<<", "<<zMax<<')');
pstart.SetCoord(xMax,yMax,zMax);
if(_pstart) *_pstart = pstart;
}else{
switch(retract_axis) {
case RetractAxisX:
if (pstart.X()<xMax){
pstart.SetX(xMax);
}
break;
case RetractAxisY:
if (pstart.Y()<yMax){
pstart.SetY(yMax);
}
break;
default:
if (pstart.Z()<zMax){
pstart.SetZ(zMax);
}
}
if(_pstart) *_pstart = pstart;
}
gp_Pln pln;
double hint = 0.0;
bool hint_first = true;

View File

@@ -103,12 +103,12 @@ int TooltablePy::PyInit(PyObject* args, PyObject* /*kwd*/)
Py::Dict TooltablePy::getTools(void) const
{
PyObject *dict = PyDict_New();
Py::Dict dict;
for(std::map<int,Path::Tool*>::iterator i = getTooltablePtr()->Tools.begin(); i != getTooltablePtr()->Tools.end(); ++i) {
PyObject *tool = new Path::ToolPy(i->second);
PyDict_SetItem(dict,PYINT_FROMLONG(i->first),tool);
dict.setItem(Py::Long(i->first), Py::asObject(tool));
}
return Py::Dict(dict);
return dict;
}
void TooltablePy::setTools(Py::Dict arg)

View File

@@ -30,6 +30,7 @@ import copy
# lazily loaded modules
from lazy_loader.lazy_loader import LazyLoader
DraftGeomUtils = LazyLoader('DraftGeomUtils', globals(), 'DraftGeomUtils')
Part = LazyLoader('Part', globals(), 'Part')
from PySide import QtCore
@@ -69,9 +70,11 @@ class ObjectOp(PathOp.ObjectOp):
# reorder the wire
if hasattr(obj, 'StartVertex'):
offset = DraftGeomUtils.rebaseWire(offset, obj.StartVertex)
start_idx = obj.StartVertex
edges = copy.copy(PathOpTools.orientWire(offset, forward).Edges)
edges = Part.sortEdges(edges)[0];
last = None
for z in zValues:

View File

@@ -253,6 +253,8 @@ class ToolControllerEditor(object):
self.form.vertFeed.editingFinished.connect(self.refresh)
self.form.horizRapid.editingFinished.connect(self.refresh)
self.form.vertRapid.editingFinished.connect(self.refresh)
self.form.spindleSpeed.editingFinished.connect(self.refresh)
self.form.spindleDirection.currentIndexChanged.connect(self.refresh)
class TaskPanel:

View File

@@ -105,11 +105,16 @@ DrawViewBalloon::DrawViewBalloon(void)
ADD_PROPERTY_TYPE(ShapeScale,(1.0),"",(App::PropertyType)(App::Prop_None),"Balloon shape scale");
ShapeScale.setConstraints(&SymbolScaleRange);
ADD_PROPERTY_TYPE(EndTypeScale,(1.0),"",(App::PropertyType)(App::Prop_None),"EndType shape scale");
ShapeScale.setConstraints(&SymbolScaleRange);
ADD_PROPERTY_TYPE(TextWrapLen,(-1),"",(App::PropertyType)(App::Prop_None),"Text wrap length; -1 means no wrap");
ADD_PROPERTY_TYPE(KinkLength,(prefKinkLength()),"",(App::PropertyType)(App::Prop_None),
"Distance from symbol to leader kink");
ADD_PROPERTY_TYPE(LineVisible,(true),"",(App::PropertyType)(App::Prop_None),"Balloon line visible or hidden");
SourceView.setScope(App::LinkScope::Global);
Rotation.setStatus(App::Property::Hidden,true);
Caption.setStatus(App::Property::Hidden,true);

View File

@@ -49,15 +49,17 @@ public:
DrawViewBalloon();
virtual ~DrawViewBalloon();
App::PropertyLink SourceView;
App::PropertyString Text;
App::PropertyEnumeration EndType;
App::PropertyEnumeration BubbleShape;
App::PropertyLink SourceView;
App::PropertyString Text;
App::PropertyEnumeration EndType;
App::PropertyEnumeration BubbleShape;
App::PropertyFloatConstraint ShapeScale;
App::PropertyDistance OriginX;
App::PropertyDistance OriginY;
App::PropertyFloat TextWrapLen;
App::PropertyDistance KinkLength;
App::PropertyFloatConstraint EndTypeScale;
App::PropertyDistance OriginX;
App::PropertyDistance OriginY;
App::PropertyFloat TextWrapLen;
App::PropertyDistance KinkLength;
App::PropertyBool LineVisible;
short mustExecute() const override;

View File

@@ -691,21 +691,12 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int
// - the value in the base unit but without displayed unit
// - the value + unit (not necessarily the base unit!)
// the user can overwrite the decimal settings, so we must in every case use the formatSpecifier
// if useDecimals(), then formatSpecifier = global decimals, otherwise it is %.2f
// the default is: if useDecimals(), then formatSpecifier = global decimals, otherwise it is %.2f
QLocale loc;
double userVal;
bool checkDecimals = true;
if (showUnits() || (Type.isValue("Angle")) || (Type.isValue("Angle3Pt"))) {
formattedValue = qUserString; // result value + unit (not necessarily base unit!)
// remove unit
formattedValue.remove(rxUnits);
// to number
userVal = loc.toDouble(formattedValue);
if (userVal >= 1.0)
// we can assure we didn't make an error > 10% via getUserString()
checkDecimals = false;
}
if (checkDecimals){
userVal = asQuantity.getValue();
} else {
// get value in the base unit with default decimals
// for the conversion we use the same method as in DlgUnitsCalculator::valueChanged
// get the conversion factor for the unit
@@ -750,7 +741,6 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int
//qUserString from Quantity includes units - prefix + R + nnn ft + suffix
qMultiValueStr = formatPrefix + qGenPrefix + qUserString + formatSuffix;
}
formattedValue = qMultiValueStr;
}

View File

@@ -193,7 +193,11 @@ private:
} else {
vpp->showMDIViewPage();
mdi = vpp->getMDIViewPage();
mdi->printPdf(filePath);
if (mdi) {
mdi->printPdf(filePath);
} else {
throw Py::TypeError("Page not available! Is it Hidden?");
}
}
}
}
@@ -234,7 +238,11 @@ private:
} else {
vpp->showMDIViewPage();
mdi = vpp->getMDIViewPage();
mdi->saveSVG(filePath);
if (mdi) {
mdi->saveSVG(filePath);
} else {
throw Py::TypeError("Page not available! Is it Hidden?");
}
}
}
}

View File

@@ -717,14 +717,14 @@ void QGIViewBalloon::draw()
double yAdj = 0.0;
int endType = balloon->EndType.getValue();
double arrowAdj = QGIArrow::getOverlapAdjust(endType,
QGIArrow::getPrefArrowSize());
balloon->EndTypeScale.getValue()*QGIArrow::getPrefArrowSize());
if (endType == ArrowType::NONE) {
arrow->hide();
} else {
arrow->setStyle(endType);
arrow->setSize(QGIArrow::getPrefArrowSize());
arrow->setSize(balloon->EndTypeScale.getValue()*QGIArrow::getPrefArrowSize());
arrow->draw();
Base::Vector3d arrowTipPos(arrowTipX, arrowTipY, 0.0);
@@ -738,7 +738,7 @@ void QGIViewBalloon::draw()
float arAngle = atan2(dirballoonLinesLine.y, dirballoonLinesLine.x) * 180 / M_PI;
arrow->setPos(arrowTipX, arrowTipY);
if ( (endType == ArrowType::FILLED_TRIANGLE) &&
if ( (endType == ArrowType::FILLED_TRIANGLE) &&
(prefOrthoPyramid()) ) {
if (arAngle < 0.0) {
arAngle += 360.0;
@@ -765,6 +765,12 @@ void QGIViewBalloon::draw()
dLinePath.lineTo(arrowTipX - xAdj, arrowTipY - yAdj);
balloonLines->setPath(dLinePath);
// This overwrites the previously created QPainterPath with empty one, in case it should be hidden. Should be refactored.
if (!balloon->LineVisible.getValue()) {
arrow->hide();
balloonLines->setPath(QPainterPath());
}
// redraw the Balloon and the parent View
if (hasHover && !isSelected()) {
setPrettyPre();