Macro View Rotation: Difference between revisions

From FreeCAD Documentation
m (add "Discussion page")
(indentation error)
Line 25: Line 25:


<syntaxhighlight>
<syntaxhighlight>

from PyQt4 import QtGui, QtCore
from pivy import coin
from PyQt4 import QtGui, QtCore
from math import pi
from pivy import coin
from math import pi


def find_centre():
def find_centre():
xmax = xmin = ymax = ymin = zmax = zmin = 0
xmax = xmin = ymax = ymin = zmax = zmin = 0
for obj in App.ActiveDocument.Objects:
for obj in App.ActiveDocument.Objects:
if obj.Type[:4] == "Mesh":
box = obj.Mesh.BoundBox
if obj.Type[:4] == "Mesh":
box = obj.Mesh.BoundBox
elif obj.Type[:6] == "Points":
box = obj.Points.BoundBox
elif obj.Type[:6] == "Points":
elif obj.Type[:4] == "Part":
box = obj.Points.BoundBox
elif obj.Type[:4] == "Part":
box = obj.Shape.BoundBox
box = obj.Shape.BoundBox
else:
else:
continue
continue
xmax = max(xmax, box.XMax)
xmin = min(xmin, box.XMin)
xmax = max(xmax, box.XMax)
ymax = max(ymax, box.YMax)
xmin = min(xmin, box.XMin)
ymin = min(ymin, box.YMin)
ymax = max(ymax, box.YMax)
zmax = max(zmax, box.ZMax)
ymin = min(ymin, box.YMin)
zmin = min(zmin, box.ZMin)
zmax = max(zmax, box.ZMax)
zmin = min(zmin, box.ZMin)

centre = FreeCAD.Vector((xmax+xmin)/2.0, (ymax+ymin)/2.0, (zmax+zmin)/2.0)
centre = FreeCAD.Vector((xmax+xmin)/2.0, (ymax+ymin)/2.0, (zmax+zmin)/2.0)
return centre
return centre



class rotate_gui(QtGui.QWidget):
class rotate_gui(QtGui.QWidget):
def __init__(self):
def __init__(self):
super(rotate_gui, self).__init__()
self.initUI()
super(rotate_gui, self).__init__()
self.initRotate()
self.initUI()
self.initRotate()


def initUI(self):
def initUI(self):
self.sld = [0,1,2]
self.tbox = [0,1,2]
self.sld = [0,1,2]
self.tbox = [0,1,2]
path = FreeCAD.ConfigGet("UserAppData")
path = FreeCAD.ConfigGet("UserAppData")
icon = [0,1,2]
icon = [0,1,2]
icons = ('right.png', 'up.png', 'out.png')
icons = ('right.png', 'up.png', 'out.png')

for i in range(3):
for i in range(3):
self.sld[i] = QtGui.QSlider(QtCore.Qt.Horizontal, self)
self.sld[i].setFocusPolicy(QtCore.Qt.NoFocus)
self.sld[i] = QtGui.QSlider(QtCore.Qt.Horizontal, self)
self.sld[i].setSingleStep(5)
self.sld[i].setFocusPolicy(QtCore.Qt.NoFocus)
self.sld[i].setPageStep(15)
self.sld[i].setSingleStep(5)
self.sld[i].setValue(0)
self.sld[i].setPageStep(15)
self.sld[i].setMaximum(180)
self.sld[i].setValue(0)
self.sld[i].setMinimum(-180)
self.sld[i].setMaximum(180)
self.tbox[i] = QtGui.QLineEdit(self)
self.sld[i].setMinimum(-180)
self.tbox[i].setText("0")
self.tbox[i] = QtGui.QLineEdit(self)
self.tbox[i].setAlignment(QtCore.Qt.AlignRight)
self.tbox[i].setText("0")
self.tbox[i].setAlignment(QtCore.Qt.AlignRight)
icon[i] = QtGui.QLabel(self)
icon[i].setPixmap(QtGui.QPixmap(path + icons[i]))
icon[i] = QtGui.QLabel(self)
icon[i].setPixmap(QtGui.QPixmap(path + icons[i]))
self.sld[i].valueChanged[int].connect(self.valueChange)
self.tbox[i].returnPressed.connect(self.valueEntered)
self.sld[i].valueChanged[int].connect(self.valueChange)
self.tbox[i].returnPressed.connect(self.valueEntered)

resetButton = QtGui.QPushButton("Reset")
resetButton.clicked.connect(self.reset)
resetButton = QtGui.QPushButton("Reset")
resetButton.clicked.connect(self.reset)

okButton = QtGui.QPushButton("OK")
okButton.clicked.connect(self.close)
okButton = QtGui.QPushButton("OK")
okButton.clicked.connect(self.close)

cancelButton = QtGui.QPushButton("Cancel")
cancelButton.clicked.connect(self.cancel)
cancelButton = QtGui.QPushButton("Cancel")
cancelButton.clicked.connect(self.cancel)

hbox = [0,1,2,3]
hbox = [0,1,2,3]
vbox = QtGui.QVBoxLayout()
vbox = QtGui.QVBoxLayout()

for i in range(3):
for i in range(3):
hbox[i] = QtGui.QHBoxLayout()
hbox[i] = QtGui.QHBoxLayout()
hbox[i].addWidget(icon[i],1, QtCore.Qt.AlignCenter)
hbox[i].addWidget(self.sld[i],4)
hbox[i].addWidget(icon[i],1, QtCore.Qt.AlignCenter)
hbox[i].addWidget(self.tbox[i],1)
hbox[i].addWidget(self.sld[i],4)
vbox.addLayout(hbox[i])
hbox[i].addWidget(self.tbox[i],1)
vbox.addLayout(hbox[i])

hbox[3] = QtGui.QHBoxLayout()
hbox[3].addWidget(resetButton,1)
hbox[3] = QtGui.QHBoxLayout()
hbox[3].addWidget(okButton,1)
hbox[3].addWidget(resetButton,1)
hbox[3].addWidget(cancelButton,1)
hbox[3].addWidget(okButton,1)
hbox[3].addWidget(cancelButton,1)
vbox.addStretch(1)
vbox.addLayout(hbox[3])
vbox.addStretch(1)
vbox.addLayout(hbox[3])

self.setLayout(vbox)
self.setLayout(vbox)

a = QtGui.QDesktopWidget()
a = QtGui.QDesktopWidget()
right = a.availableGeometry().width()
right = a.availableGeometry().width()

self.setGeometry(right-300, 0, 300, 150)
self.setWindowTitle('Rotate view...')
self.setGeometry(right-300, 0, 300, 150)
self.show()
self.setWindowTitle('Rotate view...')
self.show()


def initRotate(self):
def initRotate(self):
self.internal = False
self.current = 0
self.internal = False
self.current = 0

self.cam = Gui.ActiveDocument.ActiveView.getCameraNode()
self.centre = coin.SbVec3f(find_centre())
self.cam = Gui.ActiveDocument.ActiveView.getCameraNode()
self.view = self.cam.orientation.getValue()
self.centre = coin.SbVec3f(find_centre())
self.pos = self.cam.position.getValue()
self.view = self.cam.orientation.getValue()
self.pos = self.cam.position.getValue()

#store a copy of the original view to be restored in the case of user selecting Reset or Cancel
#store a copy of the original view to be restored in the case of user selecting Reset or Cancel
self.original_view = coin.SbRotation(self.view.getValue())
self.original_pos = coin.SbVec3f(self.pos.getValue())
self.original_view = coin.SbRotation(self.view.getValue())
self.original_pos = coin.SbVec3f(self.pos.getValue())

self.config_direction(0)
self.config_direction(0)


def reset(self):
def reset(self):
#reset the view to the original one
#reset the view to the original one
self.cam.orientation = self.original_view
self.cam.position = self.original_pos
self.cam.orientation = self.original_view
self.internal = True
self.cam.position = self.original_pos
self.internal = True
for sld in self.sld:
for sld in self.sld:
sld.setValue(0)
sld.setValue(0)
self.internal = False
self.internal = False
for tbox in self.tbox:
tbox.setText("0")
for tbox in self.tbox:
tbox.setText("0")
self.config_direction(0)
self.config_direction(0)


def cancel(self):
def cancel(self):
self.reset()
self.close()
self.reset()
self.close()


def config_direction(self, i):
def config_direction(self, i):
#evaluate the vectors corresponding to the three directions for the current view, and assign the i-th one to self.direction
#evaluate the vectors corresponding to the three directions for the current view, and assign the i-th one to self.direction
self.view = self.cam.orientation.getValue()
self.view = coin.SbRotation(self.view.getValue())
self.view = self.cam.orientation.getValue()
self.pos = self.cam.position.getValue()
self.view = coin.SbRotation(self.view.getValue())
self.pos = coin.SbVec3f(self.pos.getValue())
self.pos = self.cam.position.getValue()
self.pos = coin.SbVec3f(self.pos.getValue())

up = coin.SbVec3f(0,1,0)
self.up = self.view.multVec(up)
up = coin.SbVec3f(0,1,0)
out = coin.SbVec3f(0,0,1)
self.up = self.view.multVec(up)
self.out = self.view.multVec(out)
out = coin.SbVec3f(0,0,1)
u = self.up.getValue()
self.out = self.view.multVec(out)
o = self.out.getValue()
u = self.up.getValue()
o = self.out.getValue()
r = (u[1]*o[2]-u[2]*o[1], u[2]*o[0]-u[0]*o[2], u[0]*o[1]-u[1]*o[0])
r = (u[1]*o[2]-u[2]*o[1], u[2]*o[0]-u[0]*o[2], u[0]*o[1]-u[1]*o[0])
self.right = coin.SbVec3f(r)
self.right = coin.SbVec3f(r)

self.direction = [self.right, self.up, self.out][i]
self.direction = [self.right, self.up, self.out][i]

def check(self, i):
def check(self, i):
#check if the direction of rotation has changed, if so then set previous slider & textbox to zero, and setup the new direction
#check if the direction of rotation has changed, if so then set previous slider & textbox to zero, and setup the new direction
if i <> self.current:
if i <> self.current:
self.internal = True
self.internal = True
self.sld[self.current].setValue(0)
self.sld[self.current].setValue(0)
self.tbox[self.current].setText("0")
self.tbox[self.current].setText("0")
self.internal = False
self.internal = False
self.current = i
self.current = i
self.config_direction(i)
self.config_direction(i)


def rotate(self, value):
def rotate(self, value):
#carry out the desired rotation about self.direction
#carry out the desired rotation about self.direction
val = value*pi/180.0
val = value*pi/180.0
rot = coin.SbRotation(self.direction, -val)
rot = coin.SbRotation(self.direction, -val)
nrot = self.view*rot
nrot = self.view*rot
prot = rot.multVec(self.pos - self.centre) + self.centre
prot = rot.multVec(self.pos - self.centre) + self.centre
self.cam.orientation = nrot
self.cam.orientation = nrot
self.cam.position = prot
self.cam.position = prot


def valueChange(self, value):
def valueChange(self, value):
#respond to the change in value of a slider, update the corresponding text box, check for a direction change then rotate
#respond to the change in value of a slider, update the corresponding text box, check for a direction change then rotate
#if the value was changed internally, ignore event.
#if the value was changed internally, ignore event.
if self.internal:
if self.internal:
return
return

sender = self.sender()
sender = self.sender()
for i in range(3):
for i in range(3):
if sender == self.sld[i]:
if sender == self.sld[i]:
break
break
self.tbox[i].setText(str(value))
self.tbox[i].setText(str(value))
self.check(i)
self.check(i)
self.rotate(value)
self.rotate(value)


def valueEntered(self):
def valueEntered(self):
#respond to a value being entered in a text box, updating the corresponding slider, check for direction change then rotate
#respond to a value being entered in a text box, updating the corresponding slider, check for direction change then rotate
sender = self.sender()
sender = self.sender()
for i in range(3):
for i in range(3):
if sender == self.tbox[i]:
if sender == self.tbox[i]:
break
break
value = int(self.tbox[i].text())
value = int(self.tbox[i].text())
self.internal = True
self.internal = True
self.sld[i].setValue(value)
self.sld[i].setValue(value)
self.internal = False
self.internal = False
self.check(i)
self.check(i)
self.rotate(value)
self.rotate(value)

rotate = rotate_gui()
rotate = rotate_gui()


</syntaxhighlight>
</syntaxhighlight>

Revision as of 11:04, 6 May 2014

File:Text-x-python View Rotation

Description
Macro provides a GUI to permit precise rotation of the objects in the view.

Author: Joe Dowsett
Author
Joe Dowsett
Download
None
Links
Macro Version
1.0
Date last modified
None
FreeCAD Version(s)
None
Default shortcut
None
See also
None

This GUI allows the view to be rotated with more precision than when using the mouse. Rotation is according to axes fixed with respect to the user and not the objects, though the aim is that the objects rotate about their approximate shared centre rather than the view centre.
The GUI defaults to the top right of the screen, this behaviour can be changed by editing


 		a = QtGui.QDesktopWidget()
 		right = a.availableGeometry().width()
  
 		self.setGeometry(right-300, 0, 300, 150)


at the end of the function 'initUI'. The first two arguments (right-300, 0) provide the position for the top left corner of the window - my experience is that the behaviour was as intended on Ubuntu but Vista positioned the window too high and the 0 needed to be changed to ~30.
Three icons are referred to to symbolise the rotation directions. A zip file containing these icons can be found here, the images should be placed in the folder containing your macros. Please feel free to contribute better ones!


from PyQt4 import QtGui, QtCore
from pivy import coin
from math import pi


def find_centre():
	xmax = xmin = ymax = ymin = zmax = zmin = 0	
	for obj in App.ActiveDocument.Objects:
		if obj.Type[:4] == "Mesh":
			box = obj.Mesh.BoundBox
		elif obj.Type[:6] == "Points":
			box = obj.Points.BoundBox
		elif obj.Type[:4] == "Part":
			box = obj.Shape.BoundBox
		else:
			continue
		xmax = max(xmax, box.XMax)
		xmin = min(xmin, box.XMin)
		ymax = max(ymax, box.YMax)
		ymin = min(ymin, box.YMin)
		zmax = max(zmax, box.ZMax)
		zmin = min(zmin, box.ZMin)

	centre = FreeCAD.Vector((xmax+xmin)/2.0, (ymax+ymin)/2.0, (zmax+zmin)/2.0)
	return centre



class rotate_gui(QtGui.QWidget):  
	def __init__(self):
		super(rotate_gui, self).__init__()
		self.initUI()
		self.initRotate()


	def initUI(self):
		self.sld = [0,1,2]
		self.tbox = [0,1,2]
		path = FreeCAD.ConfigGet("UserAppData")
		icon = [0,1,2]
		icons = ('right.png', 'up.png', 'out.png')

		for i in range(3): 
			self.sld[i] = QtGui.QSlider(QtCore.Qt.Horizontal, self)
			self.sld[i].setFocusPolicy(QtCore.Qt.NoFocus)
			self.sld[i].setSingleStep(5)
			self.sld[i].setPageStep(15)
			self.sld[i].setValue(0)
			self.sld[i].setMaximum(180)
			self.sld[i].setMinimum(-180)
			self.tbox[i] = QtGui.QLineEdit(self)
			self.tbox[i].setText("0")
			self.tbox[i].setAlignment(QtCore.Qt.AlignRight)
			icon[i] = QtGui.QLabel(self)
			icon[i].setPixmap(QtGui.QPixmap(path + icons[i]))
			self.sld[i].valueChanged[int].connect(self.valueChange)
			self.tbox[i].returnPressed.connect(self.valueEntered)

		resetButton = QtGui.QPushButton("Reset")
		resetButton.clicked.connect(self.reset)

		okButton = QtGui.QPushButton("OK")
		okButton.clicked.connect(self.close)

		cancelButton = QtGui.QPushButton("Cancel")
		cancelButton.clicked.connect(self.cancel)

		hbox = [0,1,2,3]
		vbox = QtGui.QVBoxLayout()

		for i in range(3):
			hbox[i] = QtGui.QHBoxLayout()
			hbox[i].addWidget(icon[i],1, QtCore.Qt.AlignCenter)
			hbox[i].addWidget(self.sld[i],4)
			hbox[i].addWidget(self.tbox[i],1)
			vbox.addLayout(hbox[i])

		hbox[3] = QtGui.QHBoxLayout()
		hbox[3].addWidget(resetButton,1)
		hbox[3].addWidget(okButton,1)
		hbox[3].addWidget(cancelButton,1)
		vbox.addStretch(1)
		vbox.addLayout(hbox[3])

		self.setLayout(vbox)

		a = QtGui.QDesktopWidget()
		right = a.availableGeometry().width()

		self.setGeometry(right-300, 0, 300, 150)
		self.setWindowTitle('Rotate view...')
		self.show()


	def initRotate(self):
		self.internal = False
		self.current = 0

		self.cam = Gui.ActiveDocument.ActiveView.getCameraNode()	
		self.centre = coin.SbVec3f(find_centre())		
		self.view = self.cam.orientation.getValue()
		self.pos = self.cam.position.getValue()

		#store a copy of the original view to be restored in the case of user selecting Reset or Cancel
		self.original_view = coin.SbRotation(self.view.getValue())
		self.original_pos = coin.SbVec3f(self.pos.getValue())

		self.config_direction(0)


	def reset(self):
		#reset the view to the original one
		self.cam.orientation = self.original_view
		self.cam.position = self.original_pos
		self.internal = True
		for sld in self.sld:
			sld.setValue(0)
		self.internal = False
		for tbox in self.tbox:
			tbox.setText("0")
		self.config_direction(0)


	def cancel(self):
		self.reset()
		self.close()


	def config_direction(self, i):
		#evaluate the vectors corresponding to the three directions for the current view, and assign the i-th one to self.direction
		self.view = self.cam.orientation.getValue()	
		self.view = coin.SbRotation(self.view.getValue())
		self.pos = self.cam.position.getValue()
		self.pos = coin.SbVec3f(self.pos.getValue())

		up = coin.SbVec3f(0,1,0)
		self.up = self.view.multVec(up)
		out = coin.SbVec3f(0,0,1)
		self.out = self.view.multVec(out)
		u = self.up.getValue()
		o = self.out.getValue()
		r = (u[1]*o[2]-u[2]*o[1], u[2]*o[0]-u[0]*o[2], u[0]*o[1]-u[1]*o[0])
		self.right = coin.SbVec3f(r)

		self.direction = [self.right, self.up, self.out][i]

 	def check(self, i):
		#check if the direction of rotation has changed, if so then set previous slider & textbox to zero, and setup the new direction
		if i <> self.current:
			self.internal = True
			self.sld[self.current].setValue(0)
			self.tbox[self.current].setText("0")
			self.internal = False
			self.current = i
			self.config_direction(i)


	def rotate(self, value):
		#carry out the desired rotation about self.direction
		val = value*pi/180.0
		rot = coin.SbRotation(self.direction, -val)		
		nrot = self.view*rot
		prot = rot.multVec(self.pos - self.centre) + self.centre
		self.cam.orientation = nrot
		self.cam.position = prot


	def valueChange(self, value):
		#respond to the change in value of a slider, update the corresponding text box, check for a direction change then rotate
		#if the value was changed internally, ignore event.
		if self.internal:
			return

		sender = self.sender()
		for i in range(3):
			if sender == self.sld[i]:
				break
		self.tbox[i].setText(str(value))
		self.check(i)
		self.rotate(value)


	def valueEntered(self):
		#respond to a value being entered in a text box, updating the corresponding slider, check for direction change then rotate
		sender = self.sender()
		for i in range(3):
			if sender == self.tbox[i]:
				break
		value = int(self.tbox[i].text())
		self.internal = True
		self.sld[i].setValue(value)
		self.internal = False
		self.check(i)
		self.rotate(value)
 

rotate = rotate_gui()

Discussion page

View+Rotation