Fix scale command and simplify UI, add support for scale subelements
This commit is contained in:
committed by
Yorik van Havre
parent
d3b701df22
commit
5bd56c82e7
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user