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 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
|
## Automatically adapted for scipy Oct 31, 2005 by
# Copyright (c) 1996, 1997, The Regents of the University of California.
# All rights reserved. See Legal.htm for full text and disclaimer.
# Nar, Numeric, and shapetest are all imported by graph:
from types import *
from graph import *
from graftypes import *
class Graph3d ( Graph ) :
"""
g = Graph3d ( <surface list>, ...keyword arguments...) will create
a three-dimensional graphics object consisting of several surfaces
plus a global environment for the object. It will accept one or
a list of Plotter objects or plotter_identifiers, or will try
to complete a generic connection of its own if asked to plot
without such a plotter specification.
<surface list> is one or a sequence of Surface objects.
The keyword arguments for Graph3d are:
plotter = <Plotter object> or a sequence of <Plotter object>s
if you have a plotter object or a sequence of them that
you want the curve to use when plotting itself. I
recommend against this; a curve can create its own
plotter if you don't give it one.
filename = <string> or a sequence of <string>s if you want to
connect to Narcisse with the named file(s). (The default
is " ".) Only one of the keywords 'plotter' or 'filename'
is allowed, and both are optional.
NOTE: the possibility of a sequence of file names or
plotters allows one to display Narcisse graphs on one's
own machine as well as one or more remote machines.
In Gist, the filename argument is the same as the
display argument (below).
display = <string> or a sequence of <string>s if you want to
display on the named hosts. The form of <string> is
the usual "hostname:server.screen". The purpose of
this argument is to allow you to continue without
exiting python if you have to open a Gist window
without the DISPLAY environment variable having been
set, or if you want to open Gist windows on more
than one host.
titles = <value> where <value> is a string or a sequence
of up to four strings, giving the titles in the
order bottom, top, left, right.
title_colors = <value> where value is an integer or string
or a sequence of up to four integers or strings giving
the colors of the titles.
grid_type = <string> where "none" means no axis grid;
"axes" means a pair of axes with tick marks;
"wide" means a widely spaced 2d grid; and
"full" means a closely spaced 2d grid.
axis_labels = <value> where <value> is a string or
sequence of up to five strings representing
the labels of the x axis, the y axis, the z
axis, the c axis, and the right y axis.
For Gist, only the first three count, and
if necessary, will be truncated to a single
character.
gnomon = (0|1) (Gist only). If 1, show the gnomon (a simple display
showing the orientation of the xyz axes of the object).
x_axis_label, y_axis_label, z_axis_label, c_axis_label, and
yr_axis_label may be used to change individual
axis labels.
axis_limits = <value> where <value> is a pair [xmin, xmax] or
a sequence of up to five pairs, where the second
would be the y limits, the third the z limits,
the fourth the c limits, and the fifth the right
y limits.
x_axis_limits, y_axis_limits, z_axis_limits, c_axis_limits, and
yr_axis_limits may be used to change individual
axis limits.
x_factor, y_factor (defaults 1.0). A factor > 1.0 compresses
the graph by that factor in the specified direction
(relative to the plane of the graph). A factor < 1.0
would enlarge it in the specified direction. So far
available only in Gist.
axis_scales = <value> where <value> is a string or a sequence
of up to five strings, each of which is either "lin"
(linear scale) or "log" (logarithmic scale). Omitted
strings default to "lin". The order thay are
specified is x, y, z, c, and right y. If this
argument is missing, the existing axis scales
are unchanged.
x_axis_scale, y_axis_scale, z_axis_scale, c_axis_scale, and
yr_axis_scale may be used to change individual
axis scales.
text = <value> where value is one or a sequence of strings
representing texts to be placed on the plot.
text_color = <value> where <value> is one or a sequence
of integer color numbers or strings (names of
common colors) giving colors for the texts.
text_size = <value> where <value> is one or a sequence of
integers giving (roughly) the number of characters
in a line on the graph. The larger this number, the
smaller the size.
text_pos = <value> where <value> is a pair or a sequence of
reals between 0. and 1.0 giving the relative
position of the lower left corner of a text
in the graphics window.
Narcisse viewing angles:
phi = <integer value> specifies the angle that the line from
the view point to the origin makes with the positive
z axis. The angle is in degrees. (default 45)
theta = <integer value> specifies the angle made by the projection
of the line of view on the xy plane with the
positive x axis. The angle is in degrees. (default 45)
roll = <integer value> specifies the angle of rotation of the
graph around the line from the origin to the view point.
The angle is measured in the positive direction,
i. e., if your right thumb is aligned outwards along
the line from the origin to the view point, then
your fingers curl in the positive direction.
The angle is in degrees. Not implemented for
Gist graphics. (default 0)
Gist viewing angles:
theta = <integer value> specifies the angle that the z axis
makes with its projection onto the surface of the graph;
positive if the z axis points out of the screen,
negative if it points inwards. (default 30)
If the z axis is in the screen, then the x and y axes
will appear horizontal. Note: If the gnomon is turned on,
then axes in the plane of the graphics window or pointing
into it will have their labels highlighted.
phi = <integer value> specifies the angle through which the
x axis is rotated in the xy plane; positive if in the
direction of the y axis, negative otherwise. (default -45)
distance = <integer value> specifies the distance of the view
point from the origin. This is an integer between
0 and 20. 0 makes the distance infinite; otherwise
the smaller the number, the closer you are. This
number does not affect the size of the graph, but
rather the amount of distortion (on account of
perspective) in the picture (the closer you are,
the more distortion).
link = <value> Used to link surfaces of different 3d options.
normally all surfaces in a graph will have the same
3d options. This value should be set to 1 if you want to
graph two or more surfaces with different 3d options.
otherwise multiple surface graphs will appear with the
options of the last surface specified.
Narcisse only.
connect = <value> set to 1 for graphs of more than one surface
to provide better hidden line removal. Must not be used
with link.
Narcisse only.
sync = <value> set to 1 to synchronize with Narcisse before
plotting the next graph. Keeps graphs sent in rapid
succession from becoming garbled. Defaults to 1; set to
0 if you don't have a timing problem.
Narcisse only.
Lighting variables (so far available only in Gist):
ambient = <value> is a light level (arbitrary units)
that is added to every surface independent of its orientation.
diffuse = <value> is a light level which is proportional
to cos(theta), where theta is the angle between the surface
normal and the viewing direction, so that surfaces directly
facing the viewer are bright, while surfaces viewed edge on are
unlit (and surfaces facing away, if drawn, are shaded as if they
faced the viewer).
specular = <value>
spower = <value>
sdir = <value>
specular = S_LEVEL is a light level proportional to a high
power spower=N of 1+cos(alpha), where alpha is the angle between
the specular reflection angle and the viewing direction. The light
source for the calculation of alpha lies in the direction XYZ (a
3 element vector) in the viewer's coordinate system at infinite
distance. You can have ns light sources by making S_LEVEL, N, and
XYZ (or any combination) be vectors of length ns (3-by-ns in the
case of XYZ).
z_scale = <real value>
if not equal to 1, affects the depth in z.
color_bar = 0 or 1 (1 enables plotting of a color bar on
any graphs for which it is meaningful (colored contour
plots, filled contour plots, cell arrays, filled
meshes and polygons).
color_bar_pos (ignored unless a color bar is actually plotted)
is a 2d array [ [xmin, ymin], [xmax, ymax]] specifying
where (in window coordinates) the diagonally opposite
corners of the color bar are to be placed.
(the z_contours_array and/or c_contours array can be used
to control precisely what is shown in the color bar.)
currently not implemented in Gist.
split = 0 or 1 (default 1) If on, causes the palette to be split
when both planes and isosurfaces are present in a graph,
so that isosurfaces are shaded according to current
light settings, while plane sections of the mesh are
colored according to a specified function.
Currently not implemented in Narcisse.
"""
def type (self) :
return Graph3dType
_color_card_dict = { "absolute" : 0 , "binary" : 1 ,
"bluegreen" : 2 , "default" : 3 , "negative" : 4 , "positive" : 5 ,
"rainbow" : 6 , "rainbowhls" : 7 , "random" : 8 , "redblue" : 9 ,
"redgreen" : 10 , "shifted" : 11 }
_axes = ["x", "y", "z", "c", "yr"]
_NotASurface = "NotASurface"
_LinkError = "LinkError"
_SurfaceSpecError = "SurfaceSpecError"
_SurfaceChangeError = "SurfaceChangeError"
# The following are actually used in Graph
_limits_keywords = ["x_axis_limits", "y_axis_limits", "z_axis_limits",
"c_axis_limits", "yr_axis_limits"]
_scale_keywords = ["x_axis_scale", "y_axis_scale", "z_axis_scale",
"c_axis_scale", "yr_axis_scale"]
_label_keywords = ["x_axis_label", "y_axis_label", "z_axis_label",
"c_axis_label", "yr_axis_label"]
_no_of_axes = 5
def __init__ ( self , surface_list , *kwds , ** keywords ) :
if len ( kwds ) == 1 :
keywords = kwds[0]
if is_scalar ( surface_list ) :
if surface_list.type () != SurfaceType and \
surface_list.type () != Mesh3dType and \
surface_list.type () != Slice3dType :
raise self._NotASurface , \
"Attempt to initialize with something that is not a surface, " + \
"slice, or mesh."
self._s = [surface_list]
else :
for i in range ( len ( surface_list ) ) :
if surface_list[i].type () != SurfaceType and \
surface_list[i].type () != Mesh3dType and \
surface_list[i].type () != Slice3dType :
raise self._NotASurface , \
"Attempt to initialize with something that is not a surface, " \
+ "slice, or mesh."
self._s = surface_list
self._s_ln = len ( self._s )
if keywords.has_key ("x_factor") :
self._x_factor = keywords ["x_factor"]
else :
self._x_factor = 1.0
if keywords.has_key ("y_factor") :
self._y_factor = keywords ["y_factor"]
else :
self._y_factor = 1.0
if keywords.has_key ("link") :
if keywords.has_key ("connect") :
raise _LinkError , "Can't specify both link and connect."
self._link = keywords ["link"]
else :
self._link = 0
if keywords.has_key ("connect") :
self._connect = keywords ["connect"]
else :
self._connect = 0
if keywords.has_key ("phi") :
self._phi = keywords ["phi"]
else :
self._phi = None
if keywords.has_key ("theta") :
self._theta = keywords ["theta"]
else :
self._theta = None
if keywords.has_key ("roll") :
self._roll = keywords ["roll"]
else :
self._roll = None
if keywords.has_key ("distance") :
self._distance = keywords ["distance"]
else :
self._distance = 0
if keywords.has_key ("z_scale") :
self.z_scale = keywords ["z_scale"]
else :
self.z_scale = 1.0
if keywords.has_key ("gnomon") :
self._gnomon = keywords ["gnomon"]
else :
self._gnomon = None
if keywords.has_key ( "color_card" ) :
self._color_card = keywords ["color_card"]
else :
self._color_card = "default"
self._axis_limits = [[0., 0.], [0., 0.], [0., 0.], [0., 0.], [0., 0.]]
self._axis_scales = ["lin", "lin", "lin", "lin", "lin"]
self._axis_labels = ["X axis", "Y axis", "Z axis", "C axis", "YR axis"]
# Gist lighting
self.lighting_dict = {}
if keywords.has_key ("ambient") :
self.lighting_dict ["ambient"] = keywords ["ambient"]
else :
self.lighting_dict ["ambient"] = None
if keywords.has_key ("diffuse") :
self.lighting_dict ["diffuse"] = keywords ["diffuse"]
else :
self.lighting_dict ["diffuse"] = None
if keywords.has_key ("specular") :
self.lighting_dict ["specular"] = keywords ["specular"]
else :
self.lighting_dict ["specular"] = None
if keywords.has_key ("spower") :
self.lighting_dict ["spower"] = keywords ["spower"]
else :
self.lighting_dict ["spower"] = None
if keywords.has_key ("sdir") :
self.lighting_dict ["sdir"] = keywords ["sdir"]
else :
self.lighting_dict ["sdir"] = None
if keywords.has_key ("style") :
self._style = keywords ["style"]
else :
self._style = "nobox.gs"
if keywords.has_key ("grid_type") :
self._grid_type = keywords ["grid_type"]
else :
self._grid_type = "none"
if keywords.has_key ( "opt_3d" ) :
self.opt_3d = keywords ["opt_3d"]
else :
self.opt_3d = "wm"
if keywords.has_key ( "mask" ) :
self.mask = keywords ["mask"]
else :
self.mask = "max"
if keywords.has_key ( "split" ) :
self._split = keywords ["split"]
else :
self._split = 0
# Everything else is Graph generic:
self._label_type = " "
Graph.__init__ ( self , keywords )
def new ( self , surface_list , ** keywords ) :
"""new ( surface_list, <keyword arguments> ) cleans out a Graph3d
and reinitializes it. This has the same argument list as
Graph3d. Do not change the plotter list or filename list (to
avoid the tedious on-and-off flickering of windows) unless
this is actually requested.
"""
pl = self._plotter_list
fl = self._filename_list
self._plotter_list = []
self._filename_list = []
del self._s, self._s_ln, self._link, self._connect, self._phi, \
self._theta, self._roll, self._distance, self._titles, \
self._title_colors, self._text, self._text_color, self._text_size, \
self._sync, self._grid_type, self._axis_labels, self._axis_scales, \
self._axis_limits
self.__init__ ( surface_list , keywords )
if self._plotter_list == [] :
self._plotter_list = pl
self._filename_list = fl
def add ( self , surface ) :
""" add ( surface ) adds a surface with its characteristics to the
existing plot. Surfaces are numbered in the order that they are
added, beginning with 1.
"""
if surface.type () != SurfaceType and surface.type () != Mesh3dType and \
surface_list[i].type () != Slice3dType :
raise self._NotASurface , \
"Attempt to add something not a surface, slice, or mesh."
self._s.append ( surface )
self._s_ln = len (self._s)
def delete ( self , n ) :
"""delete ( n ) deletes the nth surface from the Graph. Note
that surfaces are numbered beginning with 1.
"""
DeleteError = "DeleteError"
if 1 <= n <= self._s_ln :
self._s [n-1:n] = []
self._s_ln = len (self._s)
else :
raise DeleteError , "There is no surface numbered " + `n` + \
" in the current graph, which has only " + `self._s_ln` + \
" surfaces."
def set_surface_list (self, surface_list) :
del self._s
self._s = surface_list
self._s_ln = len (surface_list)
return
def replace ( self , n , surface ) :
"""replace ( n , surface ) replaces the nth surface in the Graph
with the specified surface. Note that surfaces are numbered
beginning with 1.
"""
if surface.type () != SurfaceType and surface.type () != Mesh3dType and \
surface.type () != Slice3dType :
raise self._NotASurface , \
"Attempt to add something not a surface, slice, or mesh."
ReplaceError = "ReplaceError"
if 1 <= n <= self._s_ln :
self._s [n-1] = surface
else :
raise ReplaceError , "There is no surface numbered " + `n` + \
" in the surfacent graph, which has only " + `self._s_ln` + \
" surfaces."
def change_plot ( self , ** keywords ) :
"""change_plot ( <keyword arguments> ) is used to change any Graph3d
characteristics except the surfaces being graphed. Use the add,
delete, and/or replace commands to do that. change_plot will
draw the graph without sending surface coordinates, unless
keyword send is 1. Generally, change_plot should be used when
the graph needs to be recomputed, and quick_plot when it does not.
change_plot does no error checking and does not convert user-friendly
names of colors and such into numbers.
"""
for k in keywords.keys ():
if k == "surface" or k == "mesh" :
raise self._SurfaceChangeError, \
"Use add, delete, or replace to change surfaces in a graph."
setattr (self, "_" + k, keywords [k])
if "send" in keywords.keys ():
send = keywords ["send"]
else:
send = 0
self._send_coordinates = send
self.plot ( )
self._send_coordinates = 1
def quick_plot ( self , ** keywords ) :
"""quick_plot ( <keyword arguments> ) is used to change some Graph3d
characteristics which do not demand that the graph be recomputed.
You can change the characteristics of a surface in the graph by
specifying its number (surface = n) and any combination of the
traits color_card, opt_3d, mesh_type, or mask. Or you can change
such overall graph characteristics as titles, title_colors, text,
text_color, text_size, text_pos, color_card, grid_type, sync, theta,
phi, roll, and axis_labels.
Or you can do both.
The changes will be effected and the graph redrawn.
Things that you cannot change include axis limits and scales,
and the coordinates of a surface. Use change_plot if axis limits
and scales are among the things you want to change, and use add,
delete, or replace followed by a call to plot, if you wish to
change a surface.
quick_plot will not work right for link'ed surfaces. Once the
changes have been made, you will have to call plot.
"""
ChangeError = "ChangeError"
PlottersNotStarted = "PlottersNotStarted"
if not self._plotters_started :
raise PlottersNotStarted , \
"quick_plot requires that all plotters have already been started."
if keywords.has_key ("opt_3d") :
self._opt_3d = keywords ["opt_3d"]
self.opt_3d_change = 1
if keywords.has_key ("mesh_type" ) :
self._mesh_type = keywords ["mesh_type"]
self.mesh_type_change = 1
if keywords.has_key ("mask") :
self._mask = keywords ["mask"]
self.mask_change = 1
if keywords.has_key ("color_card") :
if type ( keywords [ "color_card" ] ) == IntType :
self._color_card = keywords [ "color_card" ]
self.color_card_change = 1
elif self._color_card_dict.has_key \
( keywords [ "color_card" ] ) :
self._color_card = \
self._color_card_dict [keywords [ "color_card" ]]
self.color_card_change = 1
if keywords.has_key ( "surface" ) or keywords.has_key ( "mesh" ) :
if keywords.has_key ( "surface" ) :
self.n = keywords ["surface"]
else :
self.n = keywords ["mesh"]
self.color_card_change = 0
self.opt_3d_change = 0
self.mesh_type_change = 0
self.mask_change = 0
if 1 <= self.n <= self._s_ln :
if keywords.has_key ("color_card") :
if type ( keywords [ "color_card" ] ) == IntType :
self._s [self.n - 1].color_card = keywords [ "color_card" ]
self.color_card_change = 1
elif self._color_card_dict.has_key \
( keywords [ "color_card" ] ) :
self._s [self.n - 1].color_card = \
self._color_card_dict [keywords [ "color_card" ]]
self.color_card_change = 1
else :
raise ChangeError , "There is no surface numbered " + `n` + \
" in the current graph, which has only " + `self._s_ln` + \
" surfaces."
if keywords.has_key ("grid_type") :
self._grid_type = keywords ["grid_type"]
if keywords.has_key ("sync") :
self._sync = keywords ["sync"]
if keywords.has_key ("theta") :
self._theta = keywords ["theta"]
if keywords.has_key ("phi") :
self._phi = keywords ["phi"]
if keywords.has_key ("roll") :
self._roll = keywords ["roll"]
if keywords.has_key ("ambient") :
self.lighting_dict ["ambient"] = keywords ["ambient"]
else :
self.lighting_dict ["ambient"] = None
if keywords.has_key ("diffuse") :
self.lighting_dict ["diffuse"] = keywords ["diffuse"]
else :
self.lighting_dict ["diffuse"] = None
if keywords.has_key ("specular") :
self.lighting_dict ["specular"] = keywords ["specular"]
else :
self.lighting_dict ["specular"] = None
if keywords.has_key ("spower") :
self.lighting_dict ["spower"] = keywords ["spower"]
else :
self.lighting_dict ["spower"] = None
if keywords.has_key ("sdir") :
self.lighting_dict ["sdir"] = keywords ["sdir"]
else :
self.lighting_dict ["sdir"] = None
Graph.change ( self , keywords ) # Change generic traits
for ipl in range (len (self._plotter_list)) :
pl = self._plotter_list [ipl]
pl.quick_plot (self)
def plot ( self ) :
"""plot ( ) plots a 3d graph object. If the user has not by
now specified plotter(s) or filename(s) then a generic plotter
object will be created, if it is possible to find a local
Graphics routine.
"""
self._init_plotter ( )
# grid_type means something different on 3d graphs
for ipl in range (len (self._plotter_list)) :
pl = self._plotter_list [ipl]
pl.plot3d (self)
def move_light_source (self, ** keywords) :
"""move_light_source (nframes = 30, angle = 12) causes a
movie of the surrent graph to be shown, with the light source
rotating through angle degrees each frame, for a total
of nframes. If angle is not specified and nframes is,
then the angle defaults to 360 / nframes.
"""
if not keywords.has_key ("angle") and keywords.has_key ("nframes") :
nframes = keywords ["nframes"]
angle = 2 * pi / nframes
else :
if keywords.has_key ("nframes") :
nframes = keywords ["nframes"]
else :
nframes = 30
if keywords.has_key ("angle") :
angle = keywords ["angle"]
else :
angle = 2 * pi / nframes
for ipl in range (len (self._plotter_list)) :
pl = self._plotter_list [ipl]
pl.move_light_source (self, angle, nframes)
def rotate (self, ** keywords) :
"""rotate (axis = array ([-1., 1., 0.], Float), angle = 12,
nframes = 30) Causes the current graph to be rotated about
the specified axis, through the specified angle each frame,
for a total of nframes frames. If angle is not specified and
nframes is, then the angle defaults to 360 / nframes.
"""
if keywords.has_key ("axis") :
axis = keywords ["axis"]
else :
axis = array ([-1., 1., 0.], Float)
if not keywords.has_key ("angle") and keywords.has_key ("nframes") :
nframes = keywords ["nframes"]
angle = 2 * pi / nframes
else :
if keywords.has_key ("nframes") :
nframes = keywords ["nframes"]
else :
nframes = 30
if keywords.has_key ("angle") :
angle = pi * keywords ["angle"] / 180. # angle is degrees
else :
angle = 2 * pi / nframes
for ipl in range (len (self._plotter_list)) :
pl = self._plotter_list [ipl]
pl.rotate_graph (self, axis, angle, nframes)
|