Skip to content

Commit

Permalink
Fix scale command and simplify UI, add support for scale subelements
Browse files Browse the repository at this point in the history
  • Loading branch information
Moult authored and yorikvanhavre committed May 8, 2019
1 parent fc24bf1 commit dc284e4
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 130 deletions.
174 changes: 92 additions & 82 deletions src/Mod/Draft/Draft.py
Expand Up @@ -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
Expand Down
33 changes: 8 additions & 25 deletions src/Mod/Draft/DraftGui.py
Expand Up @@ -2374,17 +2374,12 @@ def __init__(self):
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)
Expand All @@ -2406,10 +2401,8 @@ def retranslateUi(self,widget=None):
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):
Expand All @@ -2418,17 +2411,7 @@ def pickRef(self):

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

Expand Down
93 changes: 70 additions & 23 deletions src/Mod/Draft/DraftTools.py
Expand Up @@ -4171,39 +4171,81 @@ def action(self,arg):
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)
Expand Down Expand Up @@ -4247,6 +4289,11 @@ def numericInput(self,numx,numy,numz):
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"

Expand Down

0 comments on commit dc284e4

Please sign in to comment.