1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
|
# Copyright 2004-2007 Nanorex, Inc. See LICENSE file for details.
"""
ops_motion.py -- various ways of moving or spatially distorting
selected atoms or chunks (and someday, attached jigs).
These operations don't create or destroy atoms or bonds.
@version: $Id$
@copyright: 2004-2007 Nanorex, Inc. See LICENSE file for details.
History:
bruce 050507 made this by collecting appropriate methods from class Part.
"""
from utilities.Log import greenmsg, redmsg
from platform_dependent.PlatformDependent import fix_plurals
from geometry.VQT import V, norm, Q, vlen, orthodist
import foundation.env as env
from math import pi
from utilities.debug import print_compact_traceback
from model.chunk import Chunk
from model.jigs import Jig
from model.jigs_motors import Motor
from analysis.ESP.ESPImage import ESPImage
from foundation.Group import Group
class ops_motion_Mixin:
"""
Mixin class for providing these methods to class Part
"""
###@@@ move/rot should be extended to apply to jigs too (and fit into some naming convention)
def movesel(self, offset):
"""
move selected chunks and jigs in space
"""
movables = self.getSelectedMovables()
self.translateSpecifiedMovables(offset, movables = movables)
def translateSpecifiedMovables(self, offset, movables =()):
"""
Translate the specified movables.
@param movables: a list of movables (default value is empty tuple)
@type movables: list
"""
for m in movables:
self.changed()
m.move(offset)
def rotsel(self, quat):
"""
Rotate selected chunks/jigs in space. [Huaicai 8/30/05: Fixed the problem of each rotating
around its own center, they will now rotate around their common center]
"""
movables = self.getSelectedMovables()
self.rotateSpecifiedMovables(quat, movables = movables)
return
def rotateSpecifiedMovables(self, quat, movables =(), commonCenter = None):
"""
Rotate the movables specified in the 'movables' list.
(Rotated as a unit)
@param quat: Quaternion for the rotation.
@param movables: A list of movables. These movables will be
rotated around a common axis
@type movables: list
"""
numMovables = len(movables)
if commonCenter is None:
# Find the common center of all selected chunks to fix bug 594
#--Huaicai 8/30/05
comCenter = V(0.0, 0.0, 0.0)
if numMovables:
for m in movables:
comCenter += m.center
comCenter /= numMovables
else:
comCenter = commonCenter
# Move the selected chunks
for m in movables:
self.changed() #Not sure if this can be combined into one call
# Get the moving offset because of the rotation around each
# movable's own center
rotOff = quat.rot(m.center - comCenter)
rotOff = comCenter - m.center + rotOff
m.move(rotOff)
m.rot(quat)
def Stretch(self):
"""
stretch a Chunk
"""
mc = env.begin_op("Stretch")
try:
cmd = greenmsg("Stretch: ")
if not self.selmols:
msg = redmsg("No selected chunks to stretch")
env.history.message(cmd + msg)
else:
self.changed()
for m in self.selmols:
m.stretch(1.1)
self.o.gl_update()
# Added history message. Mark 050413.
info = fix_plurals( "Stretched %d chunk(s)" % len(self.selmols))
env.history.message( cmd + info)
finally:
env.end_op(mc)
return
def Invert(self):
"""
Invert the atoms of the selected chunk(s) around the chunk centers
"""
mc = env.begin_op("Invert")
cmd = greenmsg("Invert: ")
if not self.selmols:
msg = redmsg("No selected chunks to invert")
env.history.message(cmd + msg)
return
self.changed()
for m in self.selmols:
m.stretch(-1.0)
self.o.gl_update()
info = fix_plurals( "Inverted %d chunk(s)" % len(self.selmols))
env.history.message( cmd + info)
env.end_op(mc) #e try/finally?
def Mirror(self):
"""
Mirror the selected chunk(s) about a selected grid plane.
"""
cmd = greenmsg("Mirror: ")
#ninad060814 this is necessary to fix a bug. Otherwise program will
#crash if you try to mirror when the top node of the part
#(main part of clipboard) is selected
if self.topnode.picked:
self.topnode.unpick_top()
self.mirrorJigs = self.getQualifiedMirrorJigs()
jigCounter = len(self.mirrorJigs)
if jigCounter < 1:
msg1 = "No mirror plane selected."
msg2 = " Please select a Reference Plane or a Grid Plane first."
msg = redmsg(msg1+msg2)
instr1 = "(If it doesn't exist, create it using"
instr2 = "<b>Insert > Reference Geometry menu </b> )"
instruction = instr1 + instr2
env.history.message(cmd + msg + instruction)
return
elif jigCounter >1:
msg = redmsg("More than one plane selected. Please select only one plane and try again")
env.history.message(cmd + msg )
return
for j in self.mirrorJigs:
j.unpick()
copiedObject = self.o.assy.part.copy_sel_in_same_part()
# ninad060812 Get the axis vector of the Grid Plane. Then you need to
#rotate the inverted chunk by pi around this axis vector
self.mirrorAxis = self.mirrorJigs[0].getaxis()
if isinstance(copiedObject, Chunk):
copiedObject.name = copiedObject.name + "-Mirror"
self._mirrorChunk(copiedObject)
return
elif isinstance(copiedObject, Group):
copiedObject.name = "Mirrored Items"
def mirrorChild(obj):
if isinstance(obj, Chunk):
self._mirrorChunk(obj)
elif isinstance(obj, Jig):
self._mirrorJig(obj)
copiedObject.apply2all(mirrorChild)
return
def _mirrorChunk(self, chunkToMirror):
"""
Converts the given chunk into its own mirror.
@param chunkToMirror: The chunk that needs to be converted into its own
mirror chunk.
@type chunkToMirror: instance of class Chunk
@see: self.Mirror
"""
m = chunkToMirror
# ninad060813 Following gives an orthogonal distance between the
#chunk center and mirror plane.
self.mirrorDistance, self.wid = orthodist(m.center,
self.mirrorAxis,
self.mirrorJigs[0].center)
# @@@@ ninad060813 This moves the mirror chunk on the other side of
# the mirror plane. It surely moves the chunk along the axis of the
# mirror plane but I am still unsure if this *always* moves the
# chunk on the other side of the mirror.
#Probably the 'orthodist' function has helped me here??
m.move(2*(self.mirrorDistance)*self.mirrorAxis)
m.stretch(-1.0)
m.rot(Q(self.mirrorAxis, pi))
return
def _mirrorJig(self, jigToMirror):
"""
Converts the given jig into its own mirror. If the jig is a motor,
it also reverses its direction.
@param jigToMirror: The jig that needs to be converted into its own
mirror jig.
@type jigToMirror: instance of class Jig
@see: self.Mirror
"""
j = jigToMirror
# ninad060813 This gives an orthogonal distance between the chunk
# center and mirror plane.
#Fixed bug 2503.
if not (isinstance(j, Motor) or isinstance(j, ESPImage)):
return
self.mirrorDistance, self.wid = orthodist(j.center, self.mirrorAxis,
self.mirrorJigs[0].center)
j.move(2*(self.mirrorDistance)*self.mirrorAxis)
j.rot(Q(self.mirrorAxis, pi))
#Reverse the direction of Linear and Rotary motor for correct
#mirror operation
if isinstance(j, Motor):
j.reverse_direction()
return
def getQualifiedMirrorJigs(self):
"""
Returns a list of objects that can be used as a
reference in Mirror Feature. (referece plane and grid planes are valid
objects). Only the first object in this list is used for mirror.
See Mirror method for details
"""
jigs = self.assy.getSelectedJigs()
mirrorJigs = []
for j in jigs:
if j.mmp_record_name is "gridplane" or j.mmp_record_name is "plane":
mirrorJigs.append(j)
return mirrorJigs
def align_NEW(self):
"""
Align the axes of the selected movables to the axis of the movable
that is placed at the highest order in the Model Tree
"""
#@@This is not called yet.
#method *always* uses the MT order to align chunks or jigs
#This supports jigs (including reference planes) but it has following
#bug -- It always uses the selected movable that is placed highest
#in the Model Tree, as the reference axis for alignment. (Instead
#it should align to the 'first selected movable'
#(this doesn't happen (or very rarely happens) in old align method where
#'selmols' is used.)
cmd = greenmsg("Align to Common Axis: ")
movables = self.assy.getSelectedMovables()
for m in movables:
print "movable =", m.name
numMovables = len(movables)
if len(movables) < 2:
msg = redmsg("Need two or more selected chunks to align")
env.history.message(cmd + msg)
return
self.changed()
try:
firstAxis = movables[0].getaxis()
for m in movables[1:]:
m.rot(Q(m.getaxis(),firstAxis))
self.o.gl_update()
except:
print_compact_traceback ("bug: selected movable object doesn't have an \
axis")
msg = redmsg("bug: selected movable object doesn't have an axis")
env.history.message(cmd + msg)
return
self.o.gl_update()
info = fix_plurals( "Aligned %d item(s)" % (len(movables) - 1) ) \
+ " to axis of %s" % movables[0].name
env.history.message( cmd + info)
return
def align(self):
"""
"""
cmd = greenmsg("Align to Common Axis: ")
if len(self.selmols) < 2:
msg = redmsg("Need two or more selected chunks to align")
env.history.message(cmd + msg)
return
self.changed() #bruce 050131 bugfix or precaution
#ax = V(0,0,0)
#for m in self.selmols:
# ax += m.getaxis()
#ax = norm(ax)
ax = self.selmols[0].getaxis() # Axis of first selected chunk
for m in self.selmols[1:]:
m.rot(Q(m.getaxis(),ax))
self.o.gl_update()
info = fix_plurals( "Aligned %d chunk(s)" % (len(self.selmols) - 1) ) \
+ " to chunk %s" % self.selmols[0].name
env.history.message( cmd + info)
#Ninad 060904 The following is not called from UI. Need to see if this is useful to the user.
def alignPerpendicularToPlane(self):
"""
Aligns the axes of selected jigs or chunks perpendicular to a reference plane
"""
cmd = greenmsg("Align to Plane:")
referencePlaneList = self.getQualifiedReferencePlanes()
jigCounter = len(referencePlaneList)
self.changed()
if jigCounter:
referencePlane = referencePlaneList[0] #ninad060904 If more than 1 ref planes are selected, it selectes the first in the order in the mmp file
if jigCounter < 1:
msg = redmsg("Please select a plane first.")
instruction = " Planes can also be created using the <b>Insert > Plane</b> command."
env.history.message(cmd + msg + instruction)
return
movables = self.assy.getSelectedMovables()
numMovables = len(movables)
#print len(movables)
if numMovables >1:
for obj in movables:
if obj is referencePlane:
pass
refAxis = referencePlane.getaxis()
obj.rot(Q(obj.getaxis(),refAxis))
self.o.gl_update()
else:
msg = redmsg("No chunks or movable jigs selected to align perpendicular to the reference plane.")
env.history.message(cmd + msg + instruction)
return
def getQualifiedReferencePlanes(self, jigs): #Ninad 060904
"""
Returns a list of jigs that can be used a reference plane in align to plane feature.
"""
referencePlaneList = []
for j in jigs:
if j.mmp_record_name is "gridplane":
referencePlaneList += [j]
return referencePlaneList
def alignmove(self):
cmd = greenmsg("Move to Axis: ")
if len(self.selmols) < 2:
msg = redmsg("Need two or more selected chunks to align")
env.history.message(cmd + msg)
return
self.changed()
#ax = V(0,0,0)
#for m in self.selmols:
# ax += m.getaxis()
#ax = norm(ax)
ax = self.selmols[0].getaxis() # Axis of first selected chunk
ctr = self.selmols[0].center # Center of first selected chunk
for m in self.selmols[1:]:
m.rot(Q(m.getaxis(),ax))
m.move(ctr-m.center) # offset
self.o.gl_update()
info = fix_plurals( "Aligned %d chunk(s)" % (len(self.selmols) - 1) ) \
+ " to chunk %s" % self.selmols[0].name
env.history.message( cmd + info)
pass # end of class ops_motion_Mixin
# end
|