Fix scale command and simplify UI, add support for scale subelements

This commit is contained in:
Dion Moult
2019-03-03 17:37:02 +11:00
committed by Yorik van Havre
parent d3b701df22
commit 5bd56c82e7
3 changed files with 170 additions and 130 deletions

View File

@@ -1789,95 +1789,105 @@ def rotate(objectslist,angle,center=Vector(0,0,0),axis=Vector(0,0,1),copy=False)
if len(newobjlist) == 1: return newobjlist[0]
return newobjlist
def scale(objectslist,delta=Vector(1,1,1),center=Vector(0,0,0),copy=False,legacy=False):
def scaleVectorFromCenter(vector, scale, center):
return vector.sub(center).scale(scale.x, scale.y, scale.z).add(center)
def scaleVertex(object, vertex_index, scale, center):
points = object.Points
points[vertex_index] = object.Placement.inverse().multVec(
scaleVectorFromCenter(
object.Placement.multVec(points[vertex_index]),
scale, center))
object.Points = points
def scaleEdge(object, edge_index, scale, center):
scaleVertex(object, edge_index, scale, center)
if isClosedEdge(edge_index, object):
scaleVertex(object, 0, scale, center)
else:
scaleVertex(object, edge_index+1, scale, center)
def copyScaledEdges(arguments):
copied_edges = []
for argument in arguments:
copied_edges.append(copyScaledEdge(argument[0], argument[1],
argument[2], argument[3]))
joinWires(copied_edges)
def copyScaledEdge(object, edge_index, scale, center):
vertex1 = scaleVectorFromCenter(
object.Placement.multVec(object.Points[edge_index]),
scale, center)
if isClosedEdge(edge_index, object):
vertex2 = scaleVectorFromCenter(
object.Placement.multVec(object.Points[0]),
scale, center)
else:
vertex2 = scaleVectorFromCenter(
object.Placement.multVec(object.Points[edge_index+1]),
scale, center)
return makeLine(vertex1, vertex2)
def scale(objectslist,scale=Vector(1,1,1),center=Vector(0,0,0),copy=False):
'''scale(objects,vector,[center,copy,legacy]): Scales the objects contained
in objects (that can be a list of objects or an object) of the given scale
factors defined by the given vector (in X, Y and Z directions) around
given center. If legacy is True, direct (old) mode is used, otherwise
a parametric copy is made. If copy is True, the actual objects are not moved,
but copies are created instead. The objects (or their copies) are returned.'''
given center. If copy is True, the actual objects are not moved, but copies
are created instead. The objects (or their copies) are returned.'''
if not isinstance(objectslist, list):
objectslist = [objectslist]
if legacy:
newobjlist = []
for obj in objectslist:
if copy:
newobj = makeCopy(obj)
else:
newobj = obj
if obj.isDerivedFrom("Part::Feature"):
sh = obj.Shape.copy()
m = FreeCAD.Matrix()
m.scale(delta)
sh = sh.transformGeometry(m)
corr = Vector(center.x,center.y,center.z)
corr.scale(delta.x,delta.y,delta.z)
corr = (corr.sub(center)).negative()
sh.translate(corr)
if getType(obj) == "Rectangle":
p = []
for v in sh.Vertexes: p.append(v.Point)
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 < 0: l = -nb.Length
else: l = nb.Length
if obj.Height < 0: h = -nh.Length
else: h = nh.Length
newobj.Length = l
newobj.Height = h
tr = p[0].sub(obj.Shape.Vertexes[0].Point)
newobj.Placement = pl
elif getType(obj) == "Wire":
p = []
for v in sh.Vertexes: p.append(v.Point)
newobj.Points = p
elif getType(obj) == "BSpline":
p = []
for p1 in obj.Points:
p2 = p1.sub(center)
p2.scale(delta.x,delta.y,delta.z)
p.append(p2)
newobj.Points = p
elif (obj.isDerivedFrom("Part::Feature")):
newobj.Shape = sh
elif (obj.TypeId == "App::Annotation"):
factor = delta.y * obj.ViewObject.FontSize
newobj.ViewObject.FontSize = factor
d = obj.Position.sub(center)
newobj.Position = center.add(Vector(d.x*delta.x,d.y*delta.y,d.z*delta.z))
if copy:
formatObject(newobj,obj)
newobjlist.append(newobj)
if copy and getParam("selectBaseObjects",False):
select(objectslist)
newobjlist = []
for obj in objectslist:
if copy:
newobj = makeCopy(obj)
else:
select(newobjlist)
if len(newobjlist) == 1: return newobjlist[0]
return newobjlist
newobj = obj
if obj.isDerivedFrom("Part::Feature"):
scaled_shape = obj.Shape.copy()
m = FreeCAD.Matrix()
m.move(obj.Placement.Base.negative())
m.move(center.negative())
m.multiply(scale)
m.move(center)
m.move(obj.Placement.Base)
scaled_shape = scaled_shape.transformGeometry(m)
if getType(obj) == "Rectangled":
p = []
for v in scaled_shape.Vertexes: p.append(v.Point)
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 < 0: l = -nb.Length
else: l = nb.Length
if obj.Height < 0: h = -nh.Length
else: h = nh.Length
newobj.Length = l
newobj.Height = h
tr = p[0].sub(obj.Shape.Vertexes[0].Point)
newobj.Placement = pl
elif getType(obj) == "Wire" or getType(obj) == "BSpline":
for index, point in enumerate(newobj.Points):
scaleVertex(newobj, index, scale, center)
elif (obj.isDerivedFrom("Part::Feature")):
newobj.Shape = scaled_shape
elif (obj.TypeId == "App::Annotation"):
factor = scale.y * obj.ViewObject.FontSize
newobj.ViewObject.FontSize = factor
d = obj.Position.sub(center)
newobj.Position = center.add(Vector(d.x*scale.x,d.y*scale.y,d.z*scale.z))
if copy:
formatObject(newobj,obj)
newobjlist.append(newobj)
if copy and getParam("selectBaseObjects",False):
select(objectslist)
else:
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Scale")
_Clone(obj)
obj.Objects = objectslist
obj.Scale = delta
corr = Vector(center.x,center.y,center.z)
corr.scale(delta.x,delta.y,delta.z)
corr = (corr.sub(center)).negative()
p = obj.Placement
p.move(corr)
obj.Placement = p
if not copy:
for o in objectslist:
o.ViewObject.hide()
if gui:
_ViewProviderClone(obj.ViewObject)
formatObject(obj,objectslist[-1])
select(obj)
return obj
select(newobjlist)
if len(newobjlist) == 1: return newobjlist[0]
return newobjlist
def offset(obj,delta,copy=False,bind=False,sym=False,occ=False):
'''offset(object,delta,[copymode],[bind]): offsets the given wire by

View File

@@ -2374,17 +2374,12 @@ class ScaleTaskPanel:
layout.addWidget(self.lock,3,0,1,2)
self.relative = QtGui.QCheckBox()
layout.addWidget(self.relative,4,0,1,2)
self.rLabel = QtGui.QLabel()
layout.addWidget(self.rLabel,5,0,1,2)
self.isClone = QtGui.QRadioButton()
layout.addWidget(self.isClone,6,0,1,2)
self.isClone.setChecked(True)
self.isOriginal = QtGui.QRadioButton()
layout.addWidget(self.isOriginal,7,0,1,2)
self.isCopy = QtGui.QRadioButton()
layout.addWidget(self.isCopy,8,0,1,2)
self.isCopy = QtGui.QCheckBox()
layout.addWidget(self.isCopy,5,0,1,2)
self.isSubelementMode = QtGui.QCheckBox()
layout.addWidget(self.isSubelementMode,6,0,1,2)
self.pickrefButton = QtGui.QPushButton()
layout.addWidget(self.pickrefButton,9,0,1,2)
layout.addWidget(self.pickrefButton,7,0,1,2)
QtCore.QObject.connect(self.xValue,QtCore.SIGNAL("valueChanged(double)"),self.setValue)
QtCore.QObject.connect(self.yValue,QtCore.SIGNAL("valueChanged(double)"),self.setValue)
QtCore.QObject.connect(self.zValue,QtCore.SIGNAL("valueChanged(double)"),self.setValue)
@@ -2406,10 +2401,8 @@ class ScaleTaskPanel:
self.zLabel.setText(QtGui.QApplication.translate("Draft", "Z factor", None))
self.lock.setText(QtGui.QApplication.translate("Draft", "Uniform scaling", None))
self.relative.setText(QtGui.QApplication.translate("Draft", "Working plane orientation", None))
self.rLabel.setText(QtGui.QApplication.translate("Draft", "Result", None))
self.isClone.setText(QtGui.QApplication.translate("Draft", "Create a clone", None))
self.isOriginal.setText(QtGui.QApplication.translate("Draft", "Modify original", None))
self.isCopy.setText(QtGui.QApplication.translate("Draft", "Create a copy", None))
self.isCopy.setText(QtGui.QApplication.translate("draft", "Copy"))
self.isSubelementMode.setText(QtGui.QApplication.translate("draft", "Modify subelements"))
self.pickrefButton.setText(QtGui.QApplication.translate("Draft", "Pick from/to points", None))
def pickRef(self):
@@ -2418,17 +2411,7 @@ class ScaleTaskPanel:
def accept(self):
if self.sourceCmd:
x = self.xValue.value()
y = self.yValue.value()
z = self.zValue.value()
rel = self.relative.isChecked()
if self.isClone.isChecked():
mod = 0
elif self.isOriginal.isChecked():
mod = 1
else:
mod = 2
self.sourceCmd.scale(x,y,z,rel,mod)
self.sourceCmd.scale()
FreeCADGui.ActiveDocument.resetEdit()
return True

View File

@@ -4171,39 +4171,81 @@ class Scale(Modifier):
and arg["State"] == "DOWN" \
and (arg["Button"] == "BUTTON1") \
and self.point:
if not self.ghosts:
self.set_ghosts()
self.numericInput(self.point.x, self.point.y, self.point.z)
self.handle_mouse_click_event()
def handle_mouse_move_event(self, arg):
for ghost in self.ghosts:
ghost.off()
self.point, ctrlPoint, info = getPoint(self, arg, sym=True)
def finish(self,closed=False,cont=False):
Modifier.finish(self)
for ghost in self.ghosts:
ghost.finalize()
def handle_mouse_click_event(self):
if not self.ghosts:
self.set_ghosts()
self.numericInput(self.point.x, self.point.y, self.point.z)
def scale(self,x,y,z,rel,mode):
delta = Vector(x,y,z)
if rel:
delta = FreeCAD.DraftWorkingPlane.getGlobalCoords(delta)
if mode == 0:
copy = False
legacy = False
elif mode == 1:
copy = False
legacy = True
elif mode == 2:
copy = True
legacy = True
def scale(self):
self.delta = Vector(self.task.xValue.value(), self.task.yValue.value(), self.task.zValue.value())
self.center = self.node[0]
if self.task.isSubelementMode.isChecked():
self.scale_subelements()
else:
self.scale_object()
self.finish()
def scale_subelements(self):
try:
if self.task.isCopy.isChecked():
self.commit(translate("draft", "Copy"), self.build_copy_subelements_command())
else:
self.commit(translate("draft", "Scale"), self.build_scale_subelements_command())
except:
FreeCAD.Console.PrintError(translate("draft", "Some subelements could not be scaled."))
def build_copy_subelements_command(self):
import Part
command = []
arguments = []
for object in self.selected_subelements:
for index, subelement in enumerate(object.SubObjects):
if not isinstance(subelement, Part.Edge):
continue
arguments.append('[FreeCAD.ActiveDocument.{}, {}, {}, {}]'.format(
object.ObjectName,
int(object.SubElementNames[index][len("Edge"):])-1,
DraftVecUtils.toString(self.delta),
DraftVecUtils.toString(self.center)))
command.append('Draft.copyScaledEdges([{}])'.format(','.join(arguments)))
command.append('FreeCAD.ActiveDocument.recompute()')
return command
def build_scale_subelements_command(self):
import Part
command = []
for object in self.selected_subelements:
for index, subelement in enumerate(object.SubObjects):
if isinstance(subelement, Part.Vertex):
command.append('Draft.scaleVertex(FreeCAD.ActiveDocument.{}, {}, {}, {})'.format(
object.ObjectName,
int(object.SubElementNames[index][len("Vertex"):])-1,
DraftVecUtils.toString(self.delta),
DraftVecUtils.toString(self.center)))
elif isinstance(subelement, Part.Edge):
command.append('Draft.scaleEdge(FreeCAD.ActiveDocument.{}, {}, {}, {})'.format(
object.ObjectName,
int(object.SubElementNames[index][len("Edge"):])-1,
DraftVecUtils.toString(self.delta),
DraftVecUtils.toString(self.center)))
command.append('FreeCAD.ActiveDocument.recompute()')
return command
def scale_object(self):
if self.task.relative.isChecked():
self.delta = FreeCAD.DraftWorkingPlane.getGlobalCoords(self.delta)
objects = '[' + ','.join(['FreeCAD.ActiveDocument.' + object.Name for object in self.selected_objects]) + ']'
FreeCADGui.addModule("Draft")
self.commit(translate("draft","Copy"),
['Draft.scale('+objects+',delta='+DraftVecUtils.toString(delta)+',center='+DraftVecUtils.toString(self.node[0])+',copy='+str(copy)+',legacy='+str(legacy)+')',
self.commit(translate("draft","Copy" if self.task.isCopy.isChecked() else "Scale"),
['Draft.scale('+objects+',scale='+DraftVecUtils.toString(self.delta)+',center='+DraftVecUtils.toString(self.center)+',copy='+str(self.task.isCopy.isChecked())+')',
'FreeCAD.ActiveDocument.recompute()'])
self.finish()
def scaleGhost(self,x,y,z,rel):
delta = Vector(x,y,z)
@@ -4247,6 +4289,11 @@ class Scale(Modifier):
self.task.lock.setChecked(True)
self.task.setValue(d2/d1)
def finish(self,closed=False,cont=False):
Modifier.finish(self)
for ghost in self.ghosts:
ghost.finalize()
class ToggleConstructionMode():
"The Draft_ToggleConstructionMode FreeCAD command definition"