summaryrefslogtreecommitdiff
path: root/cad/doc/old_doc/logicman.txt
blob: 29cf3ce2d7723d90a7af689baf14fb00ad395649 (plain)
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
Meri Atom -- logic manual

Atom is arranged as a set of separate windows, each containing an
assembly. The intent is to be able to have multiple windows open onto
the same assembly if desired, but this isn't quite implemented yet. 

[Note: when I write foo.bar in this document, foo can be a module
(i.e. there's a file foo.py), in which case bar is a class, variable,
or function in that module; or foo can be a class, in which case bar
is a member function or variable.]

Overview:

form1.windowList, variable, a list of 
    form1.Form1, a class, containing
        Form1.glpane, member variable (m.v.) containing one
	    GLPane.GLPane, a class, containing
	        GLPane.assy, m.v. containing one
		    assembly.assembly, a class, containing
		        assembly.molecules, m.v., a list of 
			    chem.molecule, class
			        molecule.atoms, m.v., dictionary of
				    chem.atom, class
				        atom.element, m.v.:
					    atom.elem, class
				        atom.bonds, m.v., list of
					    chem.bond, class
				molecule.center, m.v. xyz position
				molecule.quat
				    VQT.Q (quaternion)
			assembly.motors, m.v., list of
			    assembly.motor, class
			assembly.selatoms, m.v., dictionary of
			    chem.atom, class
			assembly.selmols, m.v., list of
			    chem.molecule, class

There is a global variable windowList, in module form1, that contains
a list of window objects. The window object is class Form1, which
subclasses QMainWindow. A Form1 contains the controls (menus and
buttons, etc) found on the window and a GLPane object (Form1.glpane),
which is the graphics subwindow. The Form1 has methods that fire off
the various actions from the controls, and also a pointer to the
assembly object (Form1.assy).

The GLPane object contains the assembly (GLPane.assy) and a number of
variables that control the display. GLPane is where the mouse
interactions to the pane are handled.

The OpenGL setup and refresh in GLPane are tangled and suboptimal
because of the quick port from Tkinter to Qt. Sorry about that! Qt has
a nicer interface, and we should rewrite GLPane to take advantage.

In particular, GLPane subclasses QGLWidget, which has virtual methods 
initializeGL, mousePressEvent, mouseReleaseEvent, mouseMoveEvent, 
paintGL, and resizeGL which are called automatically when
appropriate.  

There are three major modes of mouse operation. With no modifiers, the
mouse changes the point of view. The assembly/molecule appears to move
on the screen, but actually the "camera" moves. The left button works
a virtual trackball (implemented as a trackball object in module VQT),
the right button moves the object (apparently, actually the camera) in
a plane orthogonal to the line from the camera to the center of view
(which is represented on-screen by a short set of axes). Using the
middle button, horizontal mouse motion moves the camera in and out,
leaving the center (and clipping planes) in place; moving the mouse
vertically moves the center as well.

The major member variables in GLPane that control the view are:

width, height (of the screen in pixels)

scale = 10.0, near = 0.66, far = 2.0 (initial values)
scale (*2) tells you how high the screen is in space units 
near and far are the distances to the near and far clipping planes 
as fractions of the distance from point of view to center of view

pov = V(0,0,0) "point of view" actually the center of view
quat = Q(1,0,0,0) the quaternion telling how the view is rotated

Mouse motion is dispatched to mousePressEvent, mouseReleaseEvent,
and mouseMoveEvent, which check the event object for which buttons and
shifts were used.  (mouseMoveEvent is not called when all mouse
buttons are up.)  

For example, the virtual trackball works as follows: mousePressEvent
detects left button and no shift, and calls StartRotate. StartRotate
calls SaveMouse, which sets MousePos to a vector of the x and y
positions on the screen. It then calls the start method of the
trackball object, which basically just saves the start position there
as well.

Then mouseMoveEvent dispatches a motion event to Rotate. Then
SaveMouse is called to set MousePos, and the trackball's update method
is called with the new position.

The trackball maps the screen to an imaginary sphere around the center
of view, and calculates the quaternion that would rotate the previous
mouse position to the new one. 

That quaternion is added to the quaternion representing the current
camera angle.

Then Rotate calls display. This is the general entry point for
paintGL, and currently doesn't have any extra housekeeping to do (but
will when we go to multiple screens on the same object). paintGL
recalculates the viewport (which should really be done in resizeGL),
sets the color (which should really be done in initializeGL), and then
does what it's supposed to.  First the projection matrix, which
depends on whether the view is orthographic or perspective (the flag
is GLPane.ortho). Then the camera is placed using the quaternion from
the trackball, and moved to match the center of view (GLPane.pov).

Then several drawing methods are called: The grid, if any
(GLPane.griddraw), the blue or red shape for freehand selection, if
any (GLPane.pickdraw), the shape being cut out if in cookie-cutter
mode (the draw method of the shape object, if any), and finally the
assembly's draw method which draws all the molecules and motors, etc.

Most of the mouse operations don't need any action done on button
release, except for the freehand selection / cookie-cutter.

The other buttons in move point of view mode start with SaveMouse and
then send mouse motion events to Pan and Zoom respectively.

The second major mode of mouse control is moving the selected
object. This is invoked by pressing the control key while moving the
mouse, and using the corresponding mouse buttons. These are similar to
the above except for using StartSelRot instead of StartRotate to
begin, and SelRotate, SelVert, and SelHoriz instead of Rotate, Pan,
and Zoom. For the rotates, the difference is updating the trackball of
each selected object instead of the main screen one. For the other
two, the actual motion in space is calculated and the movesel method
of the assembly called with the offset.

The third major mode is selection. The member functions Startpick,
ContinPick, and EndPick are called for mouse left button down, motion,
and release to draw the blue or red line (its color depends on the
total curvature, blue for clockwise and red for counterclockwise).
SelAtom and SelPart select the individual atom or part pointed to for
right and middle clicks respectively.

Startpick, ContinPick, and EndPick use the non-member functions
startpiectpick, piecepick, and pieceendpick respectively. I don't
remember why I separated these out, and it isn't necessary to keep
them that way. Essentially they map out (using pppoint) a sequence of
points in 3-space (kept in GLPane.sellist) defining the curve. 

If GLPane.mode is 0, the curve is used to select. If 1, you were in
cookie-cutter mode and the curve is used to create new diamond. In
either case, the logic is in module shape.

shape.curve is a class that represents a single flat closed curve in
3-space, with enough information to tell efficiently whether a point
on its plane is inside or outside. This is done with an array where
the curve is drawn raster-fashion and then filled. Member function
isin projects a 3-d point onto this plane and returns 1 for in,
0 for out. It optionally checks whether the point is within a distance
of the plane given by curve.thick.

shape.shape maintains a list of curves and thus allows you to build up
a more complicated shape. The isin function of shape calls that of
each curve sequentially. Member functions of shape also display the
chunk of diamond as it is being built, or select atoms that fall in
the shape.

assembly.assembly is the main class for everything you manipulate in
the window, namely molecules, motors, grounds, and so forth. Motors
are as yet incompletely implemented -- they need a properties box that
can be used to set speed and torque, for example. Motors and grounds
don't do anything in Atom -- they just get written as records for the
simulator to simulate.  

The file-reading and -writing functions are methods of assembly. In
particular, mmp files can contain an entire assembly, not just one
molecule. 

Besides the molecules and atoms, assemblies contain lists of which
atoms or molecules are selected. The molecules are in an actual Python
list, assembly.selmols, while the atoms are in a dictionary
(assembly.selatoms), for efficiency in removing them when they're
unselected. Selection of atoms and molecules is mutually exclusive --
you can't do both at the same time.

Note that "part" and "molecule" are used interchangeably. It would be
better to settle on one, but I haven't yet.

Molecules (chem.molecule) are the main data structure for building
"real" objects.  The molecule has its own quaternion and all the atoms
in it are represented in space relative to its center position and the
angle of the quaternion, so the whole molecule can be moved and rotated
without changing anything in every atom.  molecule.shakedown is the
function that puts the atoms into this relative form (and sets the
molecule's bounding box) after all the atoms have been added one at a
time (atom initialization includes a molecule it's part of). All the
atoms of a molecule are in a dictionary (molecule.atoms), again for 
ease of removal and membership checks. 

Once a molecule is drawn, its appearance is saved as a GL call list
so the python code doesn't have to put it together again every video
frame. This can be moved or rotated by changing the center or quat
members. If something is done that will change the appearance of the
molecule, changeapp must be called (or, internally, havelist set to
0).

Motors are attached to, and drawn by, molecules. This is a kludge that
should be fixed when we go to arbitrary structure nesting.

Each atom has a unique identifier, atom.key, to be used as a key in
the molecule and selected dictionaries. Each atom also has a back-link
(atom.molecule) to the molecule it's a part of. Molecules don't have
back links to assemblies, because I originally intended to be able to
have different assemblies contain the same molecule. Whether this is
doable or valuable remains to be seen, but its effects can be seen in
various calls to atom and molecule methods that have to supply the
assembly explicitly.

Atoms that are selected have atom.picked set non-zero as well as being 

To draw an assembly on the screen, call its draw method, which in turn
calls the draw method of everything in it. Each atom (and molecule)
has a local indication of whether it's selected, and its own draw
routine varies its appearance based on that. If selected, the bounding
box is drawn as well as the atom or molecule, and if an atom, it's
drawn in wireframe as well (the idea was to be able to see thru atoms
to pick ones underneath.  

There is a default display code for a GLPane, and a default for each
molecule which can override the GLPane one, and a code for each atom
which can override each of the above. These are the constants diXXX
which can be found in module "constants".

The draw method for a molecule calls the draw method for each atom in
the molecule and each bond attached to each atom (using the dictionary
"drawn" to keep from drawing a bond twice). Bonds are basically
structures that point to two atoms. Each atom keeps a list of its
bonds but there is no other list of bonds. Bonds draw themselves based
on the display codes of the atoms they are attached to.

A bond can exist between atoms belonging to different molecules.

Oddnesses: to delete an atom, call the killatom method of its
molecule. To move an atom from one molecule to another, call the
hopmol method of the atom.  (This is done by the "separate" function
of the Modify menu.)