mayavi-4.1.0/0000755000175100001440000000000011674464665014006 5ustar ischnellusers00000000000000mayavi-4.1.0/MANIFEST.in0000644000175100001440000000003511674464502015530 0ustar ischnellusers00000000000000prune artwork prune docs/pdf mayavi-4.1.0/integrationtests/0000755000175100001440000000000011674464502017402 5ustar ischnellusers00000000000000mayavi-4.1.0/integrationtests/mayavi/0000755000175100001440000000000011674464502020670 5ustar ischnellusers00000000000000mayavi-4.1.0/integrationtests/mayavi/test_close_scene.py0000644000175100001440000000133611674464502024566 0ustar ischnellusers00000000000000#!/usr/bin/env mayavi2 # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. """This tests that closing a hidden TVTK scene window does not crash or raise PyDeadObjectErrors. """ from common import TestCase class TestCloseScene(TestCase): def test(self): self.main() def do(self): mayavi = self.script engine = mayavi.engine s1 = mayavi.new_scene() s2 = mayavi.new_scene() # This should not crash or throw any errors (PyDeadObjects etc.) engine.close_scene(s1) # Neither should this. engine.close_scene(s2) if __name__ == "__main__": t = TestCloseScene() t.test() mayavi-4.1.0/integrationtests/mayavi/common.py0000644000175100001440000004443311674464502022542 0ustar ischnellusers00000000000000"""MayaVi test related utilities. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Standard library imports import os.path import sys import logging import traceback from optparse import OptionParser # Enthought library imports from traits.etsconfig.api import ETSConfig from traits.api import Bool, Instance from pyface.api import GUI from pyface.timer.api import do_later from tvtk.api import tvtk from mayavi.plugins.app import Mayavi, setup_logger # The TVTK window. from tvtk.pyface.tvtk_scene import TVTKWindow # Global variables. VERBOSE = False logger = logging.getLogger() def off_screen_viewer(): """A factory that creates an offscreen viewer.""" win = TVTKWindow(off_screen_rendering=True) # Need to set some non-zero size for the off screen window. If # not we get VTK errors on Linux. win.scene.set_size((300,300)) return win class MayaviTestError(Exception): pass ###################################################################### # Image comparison utility functions. ###################################################################### # Much of this code is translated from `vtk.test.Testing`. def _print_image_error(img_err, err_index, img_base): """Prints out image related error information.""" msg = """Failed image test with error: %(img_err)f Baseline image, error index: %(img_base)s, %(err_index)s Test image: %(img_base)s.test.small.jpg Difference image: %(img_base)s.diff.small.jpg Valid image: %(img_base)s.small.jpg"""%locals() logger.error(msg) if VERBOSE: print msg def _print_image_success(img_err, err_index): "Prints XML data for Dart when image test succeeded." msg = "Image Error, image_index: %s, %s"%(img_err, err_index) logger.debug(msg) if VERBOSE: print msg def _handle_failed_image(idiff, src_img, pngr, img_fname): """Writes all the necessary images when an image comparison failed.""" f_base, f_ext = os.path.splitext(img_fname) # write out the difference file in full. pngw = tvtk.PNGWriter(file_name=f_base + ".diff.png", input=idiff.output) pngw.write() # write the difference image scaled and gamma adjusted for the # dashboard. sz = pngr.output.dimensions if sz[1] <= 250.0: mag = 1.0 else: mag = 250.0/sz[1] shrink = tvtk.ImageResample(input=idiff.output, interpolate=1) shrink.set_axis_magnification_factor(0, mag) shrink.set_axis_magnification_factor(1, mag) gamma = tvtk.ImageShiftScale(input=shrink.output, shift=0, scale=10) jpegw = tvtk.JPEGWriter(file_name=f_base + ".diff.small.jpg", input=gamma.output, quality=85) jpegw.write() # write out the image that was generated. pngw.set(input=src_img, file_name=f_base + ".test.png") pngw.write() # write out a smaller version of the image that was generated. shrink.input = idiff.input jpegw.set(input=shrink.output, file_name=f_base + ".test.small.jpg") jpegw.write() # write out the valid image that matched. shrink.input = idiff.image jpegw.set(input=shrink.output, file_name=f_base + ".small.jpg") jpegw.write() def _set_scale(r1, r2): """Given two instances of tvtk.ImageResample, this sets the scale of the two such that their outputs are of the same. The final size is chosen as the minumum of the height and width of each image. """ img1, img2 = r1.input, r2.input ex1 = img1.whole_extent w1, h1 = ex1[1] + 1, ex1[3] + 1 ex2 = img2.whole_extent w2, h2 = ex2[1] + 1, ex2[3] + 1 w = min(w1, w2) h = min(h1, h2) r1.set_axis_magnification_factor(0, float(w)/w1) r1.set_axis_magnification_factor(1, float(h)/h1) r1.update() r2.set_axis_magnification_factor(0, float(w)/w2) r2.set_axis_magnification_factor(1, float(h)/h2) r2.update() def compare_image_with_saved_image(src_img, img_fname, threshold=10, allow_resize=True): """Compares a source image (src_img, which is a tvtk.ImageData) with the saved image file whose name is given in the second argument. If the image file does not exist the image is generated and stored. If not the source image is compared to that of the figure. This function also handles multiple images and finds the best matching image. If `allow_resize` is True then the images are rescaled if they are not of the same size. """ f_base, f_ext = os.path.splitext(os.path.abspath(img_fname)) if not os.path.isfile(img_fname): # generate the image pngw = tvtk.PNGWriter(file_name=img_fname, input=src_img) pngw.write() if VERBOSE: print "Creating baseline image '%s'."%img_fname return pngr = tvtk.PNGReader(file_name=img_fname) pngr.update() if allow_resize: src_resample = tvtk.ImageResample(input=src_img, interpolate=1, interpolation_mode='cubic') img_resample = tvtk.ImageResample(input=pngr.output, interpolate=1, interpolation_mode='cubic') _set_scale(src_resample, img_resample) idiff = tvtk.ImageDifference(input=src_resample.output, image=img_resample.output) else: idiff = tvtk.ImageDifference(input=src_img, image=pngr.output) idiff.update() min_err = idiff.thresholded_error img_err = min_err best_img = img_fname err_index = 0 count = 0 if min_err > threshold: count = 1 test_failed = 1 err_index = -1 while 1: # keep trying images till we get the best match. new_fname = f_base + "_%d.png"%count if not os.path.exists(new_fname): # no other image exists. break # since file exists check if it matches. pngr.file_name = new_fname pngr.update() if allow_resize: _set_scale(src_resample, img_resample) idiff.update() alt_err = idiff.thresholded_error if alt_err < threshold: # matched, err_index = count test_failed = 0 min_err = alt_err img_err = alt_err best_img = new_fname break else: if alt_err < min_err: # image is a better match. err_index = count min_err = alt_err img_err = alt_err best_img = new_fname count = count + 1 # closes while loop. if test_failed: _handle_failed_image(idiff, src_img, pngr, best_img) _print_image_error(img_err, err_index, f_base) msg = "Failed image test: %f\n"%idiff.thresholded_error raise AssertionError, msg # output the image error even if a test passed _print_image_success(img_err, err_index) def compare_image_raw(renwin, img_fname, threshold=10, allow_resize=True): """Compares renwin's (a tvtk.RenderWindow) contents with the image file whose name is given in the second argument. If the image file does not exist the image is generated and stored. If not the image in the render window is compared to that of the figure. This function also handles multiple images and finds the best matching image. If `allow_resize` is True then the images are rescaled if they are not of the same size. """ # If this is not done the window may not be parented correctly. GUI.process_events() w2if = tvtk.WindowToImageFilter(read_front_buffer=False, input=renwin) w2if.update() return compare_image_with_saved_image(w2if.output, img_fname, threshold, allow_resize) def compare_image_offscreen(scene, img_path): """Given a MayaVi scene and a path to a valid image, this compares the image rendered on the scene to that saved as the image. This functionality relies on the off screen rendering capabilities of VTK. Under Linux and Mac OS X this only works with VTK from CVS (i.e. VTK-5.1.x). It definitely works with a CVS checkout later than March 2006. """ abs_img_path = img_path if not os.path.isabs(img_path): abs_img_path = fixpath(img_path) s = scene.scene saved = s.off_screen_rendering s.off_screen_rendering = True s.render_window.size = (300, 300) s.reset_zoom() s.render() try: compare_image_raw(s.render_window, abs_img_path) finally: s.off_screen_rendering = saved s.render() def compare_image(scene, img_path): """Given a MayaVi scene and a path to a valid image, this compares the image rendered on the scene to that saved as the image. This will pop up a new tvtk render window and use that to perform the image comparison. """ abs_img_path = img_path if not os.path.isabs(img_path): abs_img_path = fixpath(img_path) s = scene.scene s.disable_render = True ren = s.renderer s.render_window.remove_renderer(ren) rw = tvtk.RenderWindow(size=(300,300)) rw.add_renderer(ren) ren.reset_camera() rw.render() try: compare_image_raw(rw, abs_img_path) finally: rw.remove_renderer(ren) s.render_window.add_renderer(ren) s.disable_render = False ren.reset_camera() s.render() ########################################################################### # `TestCase` class. ########################################################################### class TestCase(Mayavi): """ This class is to be subclassed when you write a test. """ # Interact with the user after test is done? Normally tests just # exit after completion, this prevents that. interact = Bool(False) # Always use offscreen rendering to generate images -- even if # `self.compare_image` was called in the test.. offscreen = Bool(False) # Use the standalone mode. standalone = Bool(True) app_window = Instance('pyface.api.ApplicationWindow') gui = Instance('pyface.gui.GUI') ###################################################################### # `Mayavi` interface. ###################################################################### def main(self, argv=None, plugins=None): """Overridden main method that sets the argv to sys.argv[1:] by default. Call this to run the test. """ if argv is None: argv = sys.argv[1:] self.parse_command_line(argv) if self.standalone: self.run_standalone() else: # Call the superclass main method. super(TestCase, self).main(argv, plugins) def setup_logger(self): """Overridden logger setup.""" if self.standalone: path = os.path.join(ETSConfig.application_data, 'mayavi_e3', 'mayavi-test.log') path = os.path.abspath(path) else: path = 'mayavi-test.log' setup_logger(logger, path, mode=self.log_mode) def run_standalone(self): from mayavi.core.engine import Engine from mayavi.plugins.script import Script from pyface.api import ApplicationWindow, GUI self.setup_logger() if self.offscreen: engine = Engine(scene_factory=off_screen_viewer) else: engine = Engine() engine.start() self.script = Script(engine=engine) self.gui = g = GUI() self.app_window = a = ApplicationWindow() a.open() a.show(False) g.invoke_later(self.run) g.start_event_loop() def run(self): """This starts everything up and runs the test. Call main to run the test.""" # Calls the users test code. try: self.do() except Exception, e: # To mimic behavior of unittest. sys.stderr.write('\nfailures=1\n') type, value, tb = sys.exc_info() info = traceback.extract_tb(tb) filename, lineno, function, text = info[-1] # last line only exc_msg = "%s\nIn %s:%d\n%s: %s (in %s)" %\ ('Exception', filename, lineno, type.__name__, str(value), function) sys.stderr.write(exc_msg + '\n') # Log the message. logger.exception(exc_msg) if not self.interact: sys.exit(1) if not self.interact: if self.standalone: # Close all existing viewers. e = self.script.engine for scene in e.scenes: viewer = e.get_viewer(scene) if viewer is not None: if self.offscreen: viewer.scene.close() else: viewer.close() GUI.process_events() # Shut down the app and the event loop. self.app_window.close() self.gui.stop_event_loop() else: self.application.gui.invoke_later(self.application.exit) def parse_command_line(self, argv): """Parse command line options.""" usage = "usage: %prog [options]" parser = OptionParser(usage) parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="Print verbose output") parser.add_option("-i", "--interact", action="store_true", dest="interact", default=False, help="Allow interaction after test (default: False)") parser.add_option("-s", "--nostandalone", action="store_true", dest="standalone", default=False, help="Run test using envisage without standalone "\ "(default: False)") parser.add_option("-o", "--offscreen", action="store_true", dest="offscreen", default=False, help="Always use offscreen rendering when "\ "generating images (default: False)") (options, args) = parser.parse_args(argv) global VERBOSE if options.verbose: VERBOSE = True self.log_mode = logging.DEBUG self.offscreen = options.offscreen self.interact = options.interact self.standalone = not options.standalone ###################################################################### # `TestCase` interface. ###################################################################### def do(self): """Override this to do whatever you want to do as your test code. *Make sure all other MayaVi specific imports are made here!* If you import MayaVi related code earlier you will run into difficulties. """ raise NotImplementedError def new_scene(self): """Creates a new TVTK scene, sets its size to that prescribed and returns the scene. """ script = self.script # Create a new VTK scene. script.new_scene() # Set its background. if self.standalone: GUI.process_events() s = script.engine.current_scene s.scene.background = (0.5, 0.5, 0.5) return s def compare_image_offscreen(self, scene, img_path): """Given a MayaVi scene and a path to a valid image, this compares the image rendered on the scene to that saved as the image. This functionality relies on the off screen rendering capabilities of VTK. Under Linux and Mac OS X this only works with VTK from CVS (i.e. VTK-5.1.x). It definitely works with a CVS checkout later than March 2006. """ return compare_image_offscreen(scene, img_path) def compare_image(self, scene, img_path): """Given a MayaVi scene and a path to a valid image, this compares the image rendered on the scene to that saved as the image. This will pop up a new tvtk render window and use that to perform the image comparison. """ if self.offscreen: return self.compare_image_offscreen(scene, img_path) else: return compare_image(scene, img_path) def failUnlessRaises(self, excClass, callableObj, *args, **kwargs): """Fail unless an exception of class excClass is thrown by callableObj when invoked with arguments args and keyword arguments kwargs. If a different type of exception is thrown, it will not be caught, and the test case will be deemed to have suffered an error, exactly as for an unexpected exception. """ try: callableObj(*args, **kwargs) except excClass: return else: if hasattr(excClass,'__name__'): excName = excClass.__name__ else: excName = str(excClass) raise MayaviTestError, excName assertRaises = failUnlessRaises def fixpath(filename): """Given a relative file path it sets the path relative to this directory. This allows us to run the tests from other directories as well. """ return os.path.join(os.path.dirname(__file__), filename) def get_example_data(fname): """Given a relative path to data inside the examples directory, obtains the full path to the file. """ p = os.path.join(os.pardir, os.pardir, 'examples', 'mayavi', 'data', fname) return os.path.abspath(fixpath(p)) def test(function): """A decorator to make a simple mayavi2 script function into a test case. Note that this will not work with nosetests. """ class MyTest(TestCase): def do(self): g = sys.modules['__main__'].__dict__ if 'mayavi' not in g: g['mayavi'] = self.script if 'application' not in g: g['application'] = self.application # Call the test. function() def __call__(self): self.main() test = MyTest() return test mayavi-4.1.0/integrationtests/mayavi/test_vtk_xml_reader.py0000644000175100001440000000202411674464502025305 0ustar ischnellusers00000000000000"""Simple test to check the VTK XML reader -- this is basically a copy of test_contour.py with just the reader changed. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Local imports. from common import TestCase, get_example_data from test_vtk_data_source import TestVTKDataSource class TestVTKXMLReader(TestVTKDataSource): def make_data(self): script = self.script from mayavi.sources.vtk_xml_file_reader import VTKXMLFileReader ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() # Read a VTK XML data file. r = VTKXMLFileReader() r.initialize(get_example_data('heart.vti')) script.add_source(r) def test(self): self.main() if __name__ == "__main__": t = TestVTKXMLReader() t.test() mayavi-4.1.0/integrationtests/mayavi/images/0000755000175100001440000000000011674464502022135 5ustar ischnellusers00000000000000mayavi-4.1.0/integrationtests/mayavi/images/test_streamline_2.png0000644000175100001440000006630711674464502026302 0ustar ischnellusers00000000000000PNG  IHDR,," IDATxy|}DIH,ɇ$xIӸI&nJ&v&6mN_tn8a1v&oI3#ǖluD J$!  ER) 8H^3B$AAp T;] *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hUX *hU& QJ\ tũ#4c0 ] XgwJD"  Z`RI4! `F(znƤ7Ȃr-?bv-c):W ^d,SF,Ӟv $M|78u4p Ƨ؁FtIiA?߶bEg.O*T+HYK9z]uYVv \Db(D}~3d%D rdkw[z/YY9yM`=+gIZp'u ??U=;]8a۝Q_!K&D|Rdp ϞYU5&nM]17gMEp(+]7˘Kw"ިח E$]/uv69u5 2Xgy;iK'sH|ֺ:/Fm ^\Leh grGݴuFS&u@3?="OwѕXNz̝zE9Af4%< 5^8`w<˖ |{'XG59Ey)Yhew zf7=+32 $bȊQTlRmwOQŞbsJLHLH$-+T$9KVիf(v'gzDڱy.j   &JDA- Τ} 5^LfXA;̎3%2B=env}QP1"[%iAoݮ,F^HpueN9V Ihoa-])Sկs r뙊I_/=0]%3+eW}E0GWl]:0NJ>CI Q"BAbS濤w=emUkfEwXuV5E̗eϬHle-ۑ}fj'%ns dvOwNԹ*Mtec~?3}/xiw uLmqq!\Ώ1F [傧@ƚ9YP3tѦYkQŶ75fyd b8v|$+Rhw^3=AU$fPd}ȵЋݑ=ѕ#l2 \fMMM(:AkPS&QQ1:<{*clsr ]v[p*#9v7\l y,.2HcrQ\|ώ@"Zݦ Q7,2QƄ(%w^7sIuyvfA'ieGVv'x~ о&wfIvw1]4]Rx9=FI ѦC/RRZWW BpxjjjO{J;?p!rWehEѶoSͽNp΅ *CC5uީ4A4x FPg q|,LeT8G7cW;Z]]?ۢȫO3Q#Gع/WY[i\ ->c>H]4;P/w[4/]/5LAfF=f Ak*f;!DD ?Wӑ^Ѳ^t}B.gg*ta=Gfg[kSE !.ơ^w.Z]{"LS3Ϊd Q`B$-O/A͚_[͚ʰA;e%]Nj#u/k>@̮ gz3 [^erU-/W aCCC]]xAsaw]qyv5 +(,e(2x$ )dvxٹٝE]+p2γ+} BCJlk .wmfq(giN:8S j!lt壸X![SlKh4ݯN[VADsf) 2@USI@o; fBUv̄$"uk:2B;*xbVežEޘ{#r(@$qkut? ^>ʸQ\ȩ6Ȳvϻ҂Pfs@IiL՗{LQJɍ3%wkADb0!+HO8l[Nsa6 ĩ OVY\͍*3NHȥ3#D" /*+ ] Οɡѡ i#R_}1yt8u SuUM`x;X&;N_xiٝ`ٝb~ ]ҎuLxśX!*E*v'ssgm.g%s:lt6+;KCjGˡ{|s&^1O.+i$|ۋUb /iN040<`ןGv:Ǵ-^K,9wYUE@O~2oggdw901n3eH ڻJV#aŶ(qȽ{t9\pĜĎ@ƕׯƐ?ʛ"״mY~4["0|j>Sꃟ>%2GsԬ|ǰ/Mdtiش /=BeF30+gTyzfnG2q],g ^Id >7fdziE("̳%} [6xh%\(ּonf!p86 3j0YZOCVDN?&d9pS^3ٓ$^-Xs$y{i65qvM\_n}筚8uL<& ֭ _DB7RܨTƺ߲ZR__KVǾk_|Mv%xWwBT9l>01cCRPj;޵cI↑t; 8^(?r$?$u[fZ@ܮhw)^~bwIl.T!]G \~_jÑ_h';Y{^'&pXWvZ@ >K`U?C;+MB83*;ão;1O4 x5 V&!n'Z'-^Nhr][/qXOv1C+o (-E[ɤK{WOb^b|On>U U/׶[+؞C%kCm?}7iOzp]&UՈ͡i3y,g9UVd ~H&hBwlퟛxX xj%}VPӛ͓ ^ ʀQ<C'' ooT! X~{;6c $735 9ڎg0}7'x tم#qSׄ\4:'hexsNqv'tF!p8'\q3tǙvY|w q&WZ-3sf^ Q:5AĂъ%c'2*N(JТN8<AP Dj?AP8S9v&'][[Xmf+J: Hc*K,Ef)_̦{l]|gݵ-15Gk@3#! L`i9߬J0M lx PQmUP.Q0&d C2)I = :죝=?w ONv Kiޱ/f+$1^O)*2o]=:u9XWwG5'BKW3c?}귎֞ҲLM/؞g(װfV恛 x %!HGGK T'!ro-~k IDATD,8eNjY3+.,#q͡ q CeLAaw~4QD%ϻ)4tv>;#3ʏ>Z4%4ouA뺳o:W>ޑNE(ŦT%ɫؒj??(7 N(MFª$xP{2MgޜtXUITHɶ-Bͬ. QxiM?h+ Xm H #3@"X(0=1׉L:򀧋> NȨX7'Z4&zNDWw}HLf/:E !_ R7ԛ۵y/~ĮFW\WY9z ||=kg7Ӣ;fӺ*+qHsrIӞ"ˋ,Q ڱg!쪦s23g:/6=xUY! ހ l'dWf^PI O*U%ٌU-U_Zy6~iޜ(!02.E 9e<=*B?ƞݏ=؝6OhmcZ>mL+jֵ[{GglqǙRjCEdG|x,z VFQxkGwx]Ҥuui ,2o#Ě=ņ1U$yNkV9cN4o=UVφE`#ZG?8 5͛cY-0W-bDdw% aR6Ո=8'XWrhWw!p›Hǵ[j>,<1ί?f"NJpvPyNjJׇ-x S!ɘxOsOdM^B콱a׌72R@V )mHXf@`Qu5ם883,mlXz`Ö6-y+إc.ފI% Q?;B/MFtJ0 {^q.t]a4:$%|W?)6zJklj(R-}D[zv~sn,*2#D^; q-u-PIR5 <dzxS$LKxy+yjG )B"+TCʘFH0 5=.I G}6r3K4FЗo4WUSzKiW x }+JW%_NW2R+A]Im$ `[=t<w Hy#7j ~}W j]_y64g)<Y؍YxD̎63K$'hڝdA9H+cBмBj҃ɲQrWYwm$ю6 ]YA`AZړk\|CQEtJn>ӆ;`Ҷm絿ڐ;w jo,MCYf{p/rdlw~߿_փj,,zb+ =Yq ^yBM`y;QYv 55=҃Dq,+k4/oG9pŋR퇿X!eG6nSLО?~xe9N:/iȯjРwSk S|-X`bre~eк;yHtda~hidC {|sΎ@mui0 EL u#}B'aze ag J2iODP6 8+AR6v|[Tu:#UXӏ;}WbVe,Og5[vTVWWC)W mړO?^Wרh6񓺑> !t8)YWr y"L! |6gdBM4oL&eڞ?}zf%I,Ѡ*M@T0BNh[~\D55tuBhuuuZ9gKr2&AEDõ5xB;9Wܛ?O>Ƨrm?O{ؚյE 9_& jJVY6GM5O S&gIE}%gwGv?}"A z(|)V4ړYw3MqYu D|2Xn|TC[e$R=w%̒)@Z\O@dMXWj(Ϟ⋁d>7rǍ׫?j]{7?_:JA#YM`E; 6kzLDU̮4LS_ɛMW_q^[h3FC /kQd.kVVѓi.U"NHPG7N}W+BPó>ӿ8>Ȏ٩oVۉrsQb:)Ҏݱo2Be[[{(x db/pGܨ7"f++j;Hd݇V:(ttG'^}HwxcV}EEuz5=O/l@L,1ͻ[~oڦb~PоzCJ+gM:XK靴pZx<ƴ ڴ+h^#M]841DR;vap0,Ӓ,<㳚8GqEjڃܠ׵6u6@^<ےנ.X4*l6]r|Ck9]_TD{UӼ79Hǚ y A6bC|_ҲOAj%m$?~5^^-zwWنe¿*+]!!xÆ%܆@tɟqjEj uiޒK-rW܎gG=6!>'$xcm%_0s5%{7$wwByQ-J\~̮)ad[ۛښRj١8r)]#ckHD2hЇR%h٣}J5>uv}nv絵3fgCkyRpK`'GZdp<ε ȍAd{[4w'Z<૑եvRg';!d-gO6F+̾LeEV$ȑS<-9?\y[/y/fXyȪVR3ڜQ(^k/Qi>5V ް >K7VԨRi-doam"yZ̀ QƦaQȀ'>L8x7)9d2/FC /x5ւ%GK%b)kGѾJrK ʰLfj?yo`tEC8xv,ιvL֮mRJՏ (&W*rFX;sLAb (g[X`QGp2aic+V<>Z_W=ms_|LJ2̮8'h'Vjs.E;\b.ZiIahjPȦ=f鏊bDv+.8-2O?hP$6]oў8/,Ύ3mӳ-Ύ3E\'ڝnc+S{Z{ZUᵆW@`e1TIpUg3M3͓gn+_z/ؾe9۲|VBzlVqWe;\?d_eު IiyMfw E1?*jO Wb!fes,Y捊Z$;|oE+™ >KFJ%푻6KL1@Tm BЙ &,gez*p.eV9: $`Me'ˆ f*o!^&F-#_̯dsxE2sm7orv'5ph,czmv.KH4צps-3Z3n2I]q8gH Bne/KwNp2@%J6k? "k* 2$Qx^zee˳a3һkMH̾"PiכCe5xn$mlLϮ96Dv_fj%QQs5!zozr-$k窱ּ( g}xjW 1']LV^Ciy6|-LlryZiO2(Cp؝!G[ Q ,/a_m&x%N%S<8񬬏(5N63ӊezhW֬Sv%4=mz2HvyD='xƧ4L3zI o.-2׳]" 3W6Yi6Kh;#^Wl}@͵{S -_֬qlDY9w,j4y ̌q?Y:̾A@MkXo -wpfmsfG`C#O+X;4x4IKo޻C,ӫsDEdzc"xZ|zo(1W3)SA&mQ67B.YSW ~kbP1uń_<>ȖU|DP6Ѻ n3߲u݉u ]7On6KAWὀ ɘM8Z,YiMwg5xJ,/g 71AL/rGP$ ?Snɔ7ezNLo>f(Ik5@Z$-kbalUZ~y֒]Mtd: KGce'i7KY`)#bBֲzbғX}~CW瓛2cЩNL9 @$"*y xHE*k M'~YK[&$ 2=W3=AK:BQ<-`[y0nzE  6BfVz-`[篭 3=a?$z$ _%{I͡JنTDYo#{|˽ңZsJ/rho~t 8v&{1zH(2b)JXs^h$> + vY{¡XfBr)X|qw9L)ӳ٦Ph|eCnz (rGiWGvjqCh+>DŜ;S|.DأLPJO5XMYOV~3uuu7Q* $AWeNОZ IDAT &l>scH@ thS xr<9 Bvʯongs7x% Zґ<#Qo~"9M.6fĜk y 1m_MO vx(fl'Դ0Xsu5@XZ, oYtQh+10Յmf'_2Bhbv&ܭS[rѦd *9& &;0qV'Ip,q7 -6XG O^52ӵ8x+%qezԠUb79Ln hI͞*bt[ es&v[$Ua,oW>i o'a~T CJ/ѳ$L)Ed.d_ BiGX "ʊҢ(HRtlhsjGՎڦ{˛X LG\}y6I#&Yq(QeX*~qWYaY{Eb7hU\qЋ+k~4!JQi )5pghEMoL4jsV8Q!bI |[-۩z9/!ܫD ަSĖ7Qd_EвAa^&̚bqGK,p- +y!( 'MSbFH?m6%i@S֒bl#KxNS \ $Xx9c˘ BU 4F3,\JA0v{&N^Ypsvh9gAע7}69)4F^s]8tfvGKKJ/L c gC({`eKӂ pu_W_!pf8Y,ӛgMb݊1zuNC '4{[zKuBmn62;ˊ)(ʪ3,6!8Sx5 2 Cqy YIPz$Pٙ j1=9^7* &켲?gT ZN' }3;1,lzm0K$~$qp|AM F awvL63vm$@+K$)B̘Mkzt91=k(`:{^{r1$8MS:Mh`S-U ![mƈǶŵ{>ĖWCzhЍjݰMol%},kB&2Ko$T8槹uf^L1Df~.NqvƄp_%U4vFuM ]v]\׬m@2x-r2{zrO`}S,iUYLӳN̦t6hڸ,EIB88Cܣ3>Vĉf+ _t=fXS,6IZI,%x\v#TezB!RUfbޔAh^@#acGQg[07T_Y¨'I4=˿8lTV^W ï47yp./) 5EMM(@xyysp+# msMD[ u/Lv<19l11*QIK(k ^YP*̚'M n ]RR mt.KҎiL7 2*lLg2=cP)r]O/bzpZ9JF (B}`ѱH&zɈ Xic'gOqS šnBCÏi‡i?>/[bmz|?{f>eǎ,M3=~^4+ӳ * Q Y{dvxN}CFnۘ94 yA™YYnwltigLOqV53Lq-,2^ &Yr<95f 8<]=|==QMՔ}na6,zgA@u^aS 5=9Lo5D6m&4A^`[_LC@t,1{*HqI?0 ) p)>!E[dI#Kq":IgAE ^Xit ?np-K8[, lꦻnmKNklڑm=#Kw۲Fr CjHC|>(Ry4C^>yQt -^Pep` Y{B|tbޜ zi#= Rhh-xҪ,X碆tP޽[O$!NF*F0 R{cYR2JxJ$dR#&ɠ2ܸUۀܹ?FO/Ǡg=0Ԡ,bQ*T:5DAzȚBg)I@-Q;H`hEb{ #sŔ@XŖ{SCv$XR09e܃^ Pݒ:%ZVY(@ $P Vx^mDm'%/bd|t$cvT2JӁ_&WB%DCn_wSzFyHx3Zk=ٶ&**˔Sim=(dXݺx^9u=%{ u8ت`{Er%ԇhmԳM2u ~> ze z\UN:բ¬N zy|,$Vw jH8u:j!`k{V!cp-!sgp8XIzz eЛStւa4c[BѵEdb t$=B@-Qے 's9EB^4ʗ$ o#_<8$DyH6>rB&DnzNPxi.KwA =\s]5@jŔpΉL '#NcSƈis6/F{t!3sX!a' g=::8,T%zzBUY`UU$ ]X_J™익z/B$?+V 6BŷR!DT @ wOI|6lYMzj:+=܊̮%*)^CHzw%ͩ'VۼJ"]䅿U 5>ŷmb;.48iT̽[֏ x:ps dY6v 5(StqxMZ[z(O #Ȯ]b7/=qDۅ yuuvNM%~5O8611!BWWױc>9pb-?4M,{֝۝@jYm%j+ Ԭl"s*/ol67{FRB&w2<{OR;NyJO?ݻw/^zΝsh4pĉ˗/bCogv!yf7b6i.@h%sOiX Ksψ =Rw')[ 655=zѣǎ;}tcc?f;vz/ƱwolYG}{ソ\o|<88H)$)Gѝ;wԐ )'_&?M?l_`O#V2JuL'S}MELqOC-Y^TH]U.־XGQ,$IlOF/k O>\xXRj LW.Capb,ezI~N;Ey9Lbسgfffaa!9sFU^x}QuuÇ].r)qODޢtJ=ec{juf/e%!d}'EQXa/ \tm۶mh4:;;{i׻{G=x`aa۟=#)9<伿씾{FLjɸ-~Tr"ې3sCC6tjjv}>qt;܌BK{r0VnɩR"=>0>G-}dScU{>Ŏ{XX2:F=uSB$!>fcLM5M[ZZb_lJ{.rP|ߒC [|3m}𵒞V4P($Yꪱ֒SEpO't˽ԩwEY75P{c jIDAT*v ;?׷s4z4 %^ÂdIzdw B UUS1tH)KL{h^:ڟCI2 M9GBH>6KEYV9Qd}O1='D t@,,o{:XC.~WO;w TyiK" !Ghj;*rl-q`IQX$L57!H<wC,Hd҉/^[{oX+]{b;J{` TH-0 !.p-{T11/<仸$"^oݍQ"4ܕQ4p(y"!2̤ vy'vJZ& zƜG6/M QnwPwE<P[';/vAn$!4r]};00{vp=@AEh++krw0Jh6;T*uj]=-qrBP}=#&d2\(KIhY@{J=8<䳙 DH+uŢSq"HcsFp@zQP]Ils=|^xm-ʞ:(!R49h$ӟǻ ._H0}'ݳe=gSjaA$wt iE-)DU˰sѽt9qCTruψک;کAWcVH-YvP8AߊwkWBrw+DU`=|N;3-i.opNS'вHiaҽ_Zc'mI뺧9 3˕P(>ۧ)>mv㧎.iAÃBO:-!liTQ입眂][ŝrC\¾d5ͅ2, FB%D"@ӭO0Cg{S Ra_qC-,#D+rKd֝ED[}N_f$,ȅ1Hz/5dzpom*)t9^|"X;3%L6g~nW{w$"dCI |Uv=!oZn C .amݡds⛖{L<"|F|P|BH w< ~oյ+\5 .RkY^g?R0w/!iv"cݷ_?PJ70VaE}C=ߎR)J8^)D"h"YÚH;)DMX+ \B3EN|!@iK=+\=BXZ^d--~@scpzE}o p7Yz(Er" mh}%%^ i$BF#dY+5\Bk,4y/wm{b"lm 8ie%+ЧƃLp 3b*찔K{M@vvC6zeYf)R:0"M}{hΏO9/ci顓i'rؐϘv$v[/) bm#Ը~)B◞WEӴB=Cxi }KaJ TXv!̲]iybnhxڹ"eF$bɈsv>vp ma]`a: y*.eLp s4+80~ kw5ܐW\5x!?9>:+8y6zJȗ_i P607E9jLQg4|yٛ 9z %S@oWigWfig&`,6=_*!l.aXi6mS~C>'N>!F)fC~p ĺHasN`DVy04y-;E*v;/)B)P꡹0SŅ>\ "A_bkQ6g9is)@׶d[g|nn6KQLE;ِ-OT+ѷ^$Ut!?He١^N/t)F2Gf" %2Ide)g83{/C6Rq\T ff_(iggF" ea^q~37d㋋!d(ۣ LNOc Rrp@Ac+)<n&E 0ZpXܤ3 p,lk.;6NYC ٰ)p 7 EyGWptd0Bxд l\'}&ﯯÿt.gh Mi'xp q& lhSoJUcNgw.a0iXvX }%, "MnzŅlp.aYw&ɣǛ %[bC93??,+W\rEfB;Y>s)$ `1 o``_)HXpr-d"N%t,E;oVPp Tїf F+鞽\x᱃KD1) >wJqKOQkD2q͆.4| ܩEdfc_OX ,]do)Xĺ#:/{4u;őP}\X%N,U\"`*,}\4pL6AlHK%Iĉ9u͆2KX4"~dp=fC%,&]~9|KXd. lEF/GxK[a g?g#aI(  Wp K>ͥbq8JK.!SdNr8EK.!SdNr8EK.!SdNr8EK.!SdNr8EK.!SdNr8EK.!SdNr8EK.!SdN3ᱞh7IENDB`mayavi-4.1.0/integrationtests/mayavi/images/test_streamline_1.png0000644000175100001440000004076511674464502026301 0ustar ischnellusers00000000000000PNG  IHDR,," IDATxyLw *$.q("(]G%FZ͛qyh eQP@E0w\+bP?jtO0CL3OMMUu139so5#"H$+ H$ŎP")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%H P")0RBH %#%DcFoXP }B:0RTAn/YZǰkGAP5:K`DT{"['B_&]rC^ʩaNh!#aG/um4CN7rRRFJ'xn7;Asg8`]I[{s1t6H:L^*uK/Z?TtH.5I2UnlCM?Q=3/=;˫R)1̈́igOzsY NJ)a $Sq t0oͣgi- M94 syزsI;,Vmf&n/mi7/OK4}TY gsh\cí7Lf#DŽm 3$ӻRr45,U|5yj6 Z{l^ԁDK2낕 cH?p@A]Xϭj1E{H5[<?RI@s粙czy]"%HCI_3"9/O>[ ^l65h>Ĉ_q&_MK8 is.ﶫ_tŃWc1p<h>y]C"DŽL2I*ZDNѰS%]|}fo%=4-XP%Tir07 fI0ʹ}HO@]`[h%Vss%t3'  ddSN}txU^wBk5B wB_+.k~Ǖ fnpl'|z~grPb4وAS2(a$3t/B~K nɈ2M{T{g` %8*mbmR?BYboʹۋ>7y5 bET~S.waDM >>3f*kIf:ڹh`nbE񜏳b{49d ?;+tSM19/II gIt걄r<Б\S$Wx Ѵ[]Gj*xbs :quOO|_\mptMy][58` EcL7')˶S:~$ @Nz^L-N=-7)@-K|ǠhP_kɖMG执}ױNc,b֐]܄ J ֩/ì0ڭ:*%*.+UuMNRmMń:]40'P6Iubz)e]f:dXs6|q;"^QMw#G`$ *NJ9T#@bM"^0}/N}?7zV95[iyI` U$Q>؍רyMwv6t튆s-*|۪>Q| D"^E3o:=X7`4ȼ.EU7Ou/Vv[#<~5'zc9_SO7"%4І&;8wEbN"ΩT0R4r0$""[ʢAl=..vf %kƽ/@*|XIh^ޙoxu5s=%Y$6z+^9ĴSx3p,;\%Kx活5D̸Hg7g>]ߩkw9dfZiTxү2xEeJxK/Nk?q*p {b)vLK87\IY.bGKh!DkRVs\y ]p}EPbaPxb[XI';C/& kZ`-?89/kTN@Oc1K/ecɽ WfWpVQBBS6rildǴPΩiP5u]50+TMneN-ӏ<9̔\ wOا+&p:] I[9| GP+áZbegx*Vgf͋^ZWEtYѢpΟamڌ㮤n r' a7oxkMB8x5^4r_!/\x;q'Js.ZOjrZ4PQd{i[5aœ3589::<Vk/n %Mo_YY8;ME,<[ǖG<Ǻ:s 'ktgԌ۵E*!2LfC9GNwh\#Py5*& .;50|ݛY=4/Ԡy"k(YjQi8GX`rE2{>˨A(Pc6Ӄ*<)^p++Z 0Y:Z. akU?ܬ:jΈ.TތiM4yp +˼BLn1ɿўvjPdn[[y^T,t]5A|ڧyIW|O]9b?f0/r-Fwim-Tݮh J)F?ģZ'hĸ.BcMm5sċNk?ѽuDx8}C&#vMzU $1/\KJ< ŰKz?Vk{PYݳ65TvZ!uNqEM+K$r:\m{xv] .$ƻ7Ơ2h/л\BB؟VcV6Oߏŀ7 {Rh$Y{XNUJS\duz?Ojv:aڊGc⁋GBUH羈rϱa\0yR.9(Ơ1 -;KoqTd'7>Ϟ}>8d: uVui$h[l4(e۾6?KȺHx]0u{M<:V֔xan]P8|UH5+@agErV*2 j s^1/ }\|S*t| -rJ5Dj آs3V\=T_gz3fRl!Ą7Z\xY91f:'<ӊxJYhPt3Jp~˽:JBr/QٔxTtҊ/_g1R}-Ti{?,$3cd]B휰Gw*M|1-x*GWMW}rf㞆z4ffYg} #]IIU 1IHύc7J=, zD=4 }6+S(;a7"7ziۡ.!ftGd9v")^ i=d ~ 2$\{~}ѫܹs>^xBj]Щ+4>x>|K+"{ߙfp dLoCS5y9/Ye>6V95~~iqҎx13oaTpMCdMR\j+**-5>q1Է95kָoq(:YB ''E K'f#ӠpĝI뾹5v}/ yBл=ߧAժm%FetP~Td j6X$O@;[ ^).,} OAO"Ju;M`0մ#DS zw'X 'OLH250_ߋFиn-N.%"go|@w&n!p y`CBskʧXiOAS a /abPz_t=}TW7#n}<:׆RM{3kLO -w{8V7q: U욍S<~驶x:?pYیAe9l]bUoo]toG`ovr?`VpL1-I`5IxrήecI1g&S8bd]kjHsd +{b}EsMƕӻֳtRlӮyϮ_v;;cg`9q4^Æ9}x}_0v8q߁Ӵ[Z@kY?xlқ'>X-cZc3jfH*BK0ܩa^ye傱yJ̗mx٦DJcw,/JL`>LzLB [3W㧯Y;yǠ5f ;s;g{ů21۬v'([gS?_A+x=T:?n`w[,!wC '&LÝmlih?Rwt6.f>y){h*2ý-`` cfO)m OpOS^x w"S_[٦Te%p=0Oeh*ĜK^ kls^R8b:GIͻH% K}c Ecրkl_ͫng3F8y^X 3ޕk1mi/K:4 \džѨ@+erp[٦sԞ8%%0/rҦK'mRu'7w~n5)nq:9jɟq{%?մ'*?]npk쟓A3̫3K+Zոƴ#a'Al, b ;TS6ǖ[1Oqg& /ty -V]wqh,|Jpa(ΩgZx̜8:;Y{bw=EG IDAT*!0Z Sͫ@4p:ZD) q'q8y9?ϼ6r(b 7ڼh"KeEflӊ7RŠǡ8u{ qF<"4azK+֌ljK)l~`^ Yń_xvFqRDzQaMMZy|${ٽuO3Rüb G{x;oCdž(ͫà)lqky.Va=7fqg@8{0yp]\Ql-9h4 lM`g=Wt/ܩNiT vnGVs-ݛsB&C.9lٸ.QG< N^0/E'.('|엦wIgo}}ԅPkn %~ۺ@}śju=is3u2ݜ2]u í] _z"4θǛ-27wLu)1o5bg O6N Քo/9cX0ҋq`oxLIh9a"' 3|3LǑ0 ^ÂHyZx <Rި#]_^}9^z<{q0 ,q̿0fWě RǂMׅ+B_aLN()h&3 GYLK %>4Z4}#i`v/^>>ҨirMycR=gF@B_C NP8+*cFST3ټ 3^b1 .v"4'bxxwz9`ͱ/̩ǂ>X4uJx1+Pͪ@[4V*4Lݻ_s5u~QߤPYʔQu7=*) =6B#7ٳٿ]4Xt*玉Xz^xY%۬.q[Y%v'}A_tr ls4.X)zx߹fhv& \ )z{ְǙbP@Y5~PYfX<m֖Xf'Ȫ.o9#J-gs{:+=L:fˠ=JزxϹxƚM+@wJs8_|Z .s[޶pZOΘ謎Q)<f-AWX6m9޿Ze6;gxbE'WhnX+i|")3 SI{h٦T27SM!<4Nm&#M1ivc>x@3z= '\{ \^jHOq?J,H>|vRPճv;1Q M)=@j+a0?p'z'*nkf emZP5_e?g"GGzG`@"s=!!'pg$sO'}~y)e4ո9sxu|dxem֔Zפv7$18 sX~Xt!;{ W ^`gGA+4"pҥK.55W}&eIhP~#@uyΙa3!L5$ Qeh,2.78M+%q:[^f zDu˗vHB&Ofi/i4\6kķ9OrOQ- l@ a/Xξ8808i-<ߓ#^~XeӹpʛGɠs!)2'ם&-fŽ>"7Wa (rT](+.Vy#ަsyQ`_!Zr~m @s^-y=@阳m8 m ^(F !L&<{wJ; J0'=yB ,|}JtɦA4nW := +=3{46M`oj3&P_TB Ӥv``[.0l+bجtK-uVi{*|4Ֆ9I65exGЅp@I8y-L,c,3fzA`;m5hpk/sg ?W۫|٦)4Jpͱf$)1{ow] b:1pHP.W46ٛ7 :b`iπKZM*f:dԻҟs&ڌ/L A/O8j{V g[vZA6{OtCژ&g~JAK1lйxeX?j阽t-d(R qZLAk4*I3){G7U:H Rh*ylœRWh 'Fz,8$WHv*~[ NY3&f眨'km6gFwU-{p>{%JϬ:)ZTWp޼6n=Nڔ!9K8)XUq- zEJ-W_%8b~iBA^-+10B<9*% t\ w/60Uŷ7OjTiN!%vșBϼ<ЫBA/Wd+6L{[ܫN/[Z*\VHDJ} Ua2~8Е@3ީ{N zᑞnG:QMqPN#X`-iSi{0H Ѓ瘒>kg!SЫϜr֧ ݑ;؛ߠܣ (d.)a$4-,fXVZy{q-)>;8W11[O}J8N&-0lMD=bsOɠ,ճzz>뜍0}tShRcs wao_6c`{AJCS? tvG!cR9h"V-yA-cv#ݻ7OJZ"0$3G z <6 ]f')" _~יMФdZ VOOupVQ8n UY"^ >MEHx~ȽJZ )aӤR)ӸoveiH97)UY\a`17@LA/O\|,N0e}A!)9aOA xLAoR|o" U8#ݣQ%~=IK!%vL`wvFOМ܊RW8^r[ …)@?;+^w[R RP dEJ R}eCv̛8Ʈp@&T8;(ǦRܝK8[=` ~JBJR)ľEߑ̕sa0{ٝDԟz9"ѝ5^x B{#یm S>v[l |I[*onc/[#z0D -;>Jeq;m^FЭ[5kTTT|J;F^4pLl# OnK 'rp~KR잋58(yA/^|1XΝ;wĈ}/~&W_ظdɒw}>+2 W8"NX'р6/G?題~?ýC;}]w7ocǎuuu➇~#;=#Ž(蔨E&.X}H6Puυ16x)SeEO<__=nIcmuqa^ )QBn;s]KBXXZOгgKϟ>}飏>:b;vsε5442@@z9 'Z=~p PPe~>`k{'tMa;v{l{k(|{ ^ 2'(]ao~衇~K,ٴiӭ޺hѢnlrKgW?!z5b )nRݙ 2'"=̎ݻ}lSO}~ UqNg놪z}zbpZwV悇pVvO${7ߢSۛӔ鰎fÆ 4xrSCw BK-Azw6ýp r [<ڍ{"n^`gXNW_wPՉ45UUzi"QYyH騧bnj^qR+Ũ/pɆ*_xUIeZ~:ݧOUVe~^eQ:깿;ǮwE{S]| lGТP,{ {&!>믿0sN;M·~ 6^kBnE(: E6mڴy[n-| tmv?#<W9[,~Gfxx*Z{.( ?=z}Bߥmܸ@IIIz}~kekS{-_r/2YΣ͋bׯ_di@@~֯_ߧO>nhZ&,%;LvTBv_xFɡ-591ݻ@V#RB ǥ{ ;{wOO"%Uk\,X~Wz/R 'KH':v7_JAM=~9N;Ð}XPJ;a~Dk!{"Ϊi/H kF[=JFºseV};˦cyĮGJfxHurRVs/渇(Dۗ{{F`g[PJ=͙K#[=w[4-_vO$´eMC [XDnOG/_GewY!%̅]V+oưKkL7}d({ (_?mg!%̏T*e`f/{;ҹ/> .hH s"4&^xi}3KE:H s%CJ9WdY#0ЮAC8ErO$0M8ҿ{gL{h , ׾rQH G z(,vl~aТ 줇 gXVu:r#C_"DŽZbJv Rf3KeweWQ\m=3RӤ,_+=a|:# }Yc%bpXϐ!vΖc{tHdzL[=kea|:#Ey!%lBV0 xh 9.7̟dkR–'!z%H [SzU=_ڙ )aɅ~R–sB)aahɥi ]pdb'ig;EJKi2lH w5-L;; R3{ij{ )a0:O"ߘT@)aKoU;4j 5RBٴ˰H1 ?p۷{w'xQ"ֆt!IDAT#aanݺvڽ{we~I8V .|)a/nY܃ԯ#6լY,Yry{[(H:29^}U0/_0P)a"Х#6GC^GF6Gkx5bIBJ()DR`IJ$FJ()DR`IJ$FJ()DR`IJ$FJ()DR`IJ$FJ()DR`IJ$FJ()DR`IJ$FJ()DR`IJ$FJ()DR`IJ$FJ()DR`IJ$FJ()DR`IJ$FJ()DR`IJ$FJ()DR`IJ$FJ(o`NLIENDB`mayavi-4.1.0/integrationtests/mayavi/images/test_streamline.png0000644000175100001440000004722611674464502026060 0ustar ischnellusers00000000000000PNG  IHDR,," IDATxyսߪ:`Ј/ QTq}I,_4F%8=신(aE [pL=KOᙧ?S AJJ*wRs:$RR9PJ*ǒJIXB)KB(%cIr, T%!ʱ$RR9PJ*ǒJIXB)KB(%cIr, T%!ʱ$RR9PJ*ǒJIXB)KB(%cIr, T%!ʱ$RR9PJ*ǒJIXB)KB(%cIr, T%!ʱ$RR9PJ*ǒJIXB)KB(%cIr, T%!ʱX_TU,9y%- +u"rX,8ߕT=_aAJBةPqx8@k23w%v)D ~ʔ:y-41h_Ym;Z9ÞƔG߅ƍ_b %!,4eCV:cfgshB|*L /QT2{eKs>}L!!lAظctq祺sZo<޺7tJ?/ 2YW'7qt(yʚ0ZJʰ15XPaxk!^FdpL}/oA> cԺ L}=BlY&tOg[ﰞWE]ᴈ//%sBڏVՆW| :&ӗGeXx4BLm xYqf/.wc_|:*ρv10gxmSʷFbg`os#]r7SN[rŻF-ivҭ V#o*GI%: %hGUc75s4f" D _y 0n3tOdBd2)b'p|- K]r |U$6`\F~\[CZl!{4erj6y !}~(4fҘ|zdT͐? <8~~44uv:LGTO/jU2M{!L_Xq݋x n ᨴW\$U$ęs6[gaT2vS9 O^5Vs{*ь'דٔ2b0/!4Ed9's 1+ U﵃LbdOÇᡉ8VN;}Nk!"r>̍_7]C~lf}<74N`L\| S*rgjL{ƌP& i 'äF,JrͦK=NݏzRעxh]kzeGF1/! XL`̸%n -^Ih]jq{xph?ЙQT0Ђԉ}:<)ƙj8S3\3U}[W韟:&yF,,x)+0]ɍt̄@OB)ↆ&<4c9޳m:fЬ+7BS0y7kpQрL 3͍0܄k$?u\!8^ˡ e҄"cl BSƙ^HjbcqtR:#EYMYAfw0B{ !@ U-vɋ =͸s+Ք`8G&c/`wkG|z蟩k_vLbhN Dj2<}fh(fjq8)^[%SQ (n4\ÿ =@(UL{'0(TTH<@Sd+nFG05?5s꙾݊'1(U \>C+ۦ/b@h*EQx3mqx2һ 7(oPěyR;?_s0a<}!ã kkAg 9{gΛ|#]Jqfm{H#:ELj*Od;׬#0)#qR3պF)0*Mж;oΡa+?ůc*q5}{c،JęmZcs;# uסOp++z }}^תQ9YOiܢF!iʟ@y$i` &np$t??E.y/AK~ y7~5tզg2ݩ|zi^`]{++a޳vE9CL96!)ӳVP̿L3)ϏF17I3Nz+Dn}Cai!_ew%݉; , f)zub+ G]QFW*kM.\Cs1a3hX4? kf*'?ʉjϡ6Gm*HY;IC(u?_D;xh`xM\<oH7ߘŻ~ݙ`, r|.y2WstNRCmS' H>ԭ7X1.7 !ֽb/`^ @w4h ʉP{@@:C;Tu~[z@?ύL|ݎ35oNVVi  TRu`q^ssn2-iR tFvx^E569;2xPSfv KMƲ3N*!Ƈo< K9kbE(RTeHc =orL]N<}.Ŭ\Uk%o t)gwkQI.CL%W.w8N[g!gy僷ެg@mU:&xj4OcV9nBa?V0+S^2OWԙ)6NhTn;WNԝgqbf립nOG3-Oos.:u3MF =S8μƢNq[ׁCg}xtJ2GƆu O]o\6] m%<їФ.讓eXK.}_k%{8TX\)6 Z641n F}Uv5pkBfa 5}'sM/_}GiUFOLQUϸយ3gm+:_peJ0487$ "ݎ:/$X\QH>F Y}'ʶ;n: /V5]'Ŝ;ulUGkwnť[ZCEѦni^F*@Co{ZƔ,ѷ *Mo(Ojܶ^QDқ#HEf|g@g}^ojg'{JTSFtr=/Vkq,!Ge/Jp3ZB 9AW!z8DKIh4 8<y9WC4ߢs?:=Zw;x֭{dʫ -3Qq Gg~:}/MOc[Q9Ju<,'3G]<^Oг&-<ڤ-oIi= M W抅M1y)!L+O'fl~ǝ}S>3ҔLEQIm8x.i[s>ҏHwkJ7<愚7[s`<ݞW8g&{\ A >`*5\h2y *"~۬9{`켭7v1OϳtZ0T MCb7؝ Qު黛WR]ج;k*ŏ 9S1b 3Ro!Ҽz۸叛1740>/b.)i ۟U&6S\n,҃m ;ەnISXgnQmd봀%/NZYgXnY3޲1th:}r ,*"ͥ9{ZsqcXFgz: .x^Cca(_!tUgq* F6`acSưKeESbfkrLU^n @Yq5Р{I է_ޮtR<0vxv]4ZZs37v+wWǞ [yac1F,<:1y"0TCюŧōDg؊ BM <o$xPj҃'}UYQUT{LOӌKYMlݙ]8 8\}qɣ6H"P36O'Kk_ Q*cuS_4n yg3ʋUi>MYQcIh` _4PS[Pb*Lt+ԴMsage~NG!_/uq㹵}O!8Z0nyfKМdW1>*]1KAzK`QXȟM7_Ӄ7riBH )ͤA[tqT2c B~k2Vo]tG{s2p|UMZ銙Ɣ+7/{Vyn0[yO L{bRݹ,o}8 X/XbA~R^B(2q=BQV'mKǬ:O?hΜh 8-=t1PNg SF<-s_P4%!DŽIO[s9G}"8YwϿ,[z܏k%yb,/ͩ-.p(BeE,uj:G!=^ nI歗6gVbwPuu:\a6% 7 gO2i4X5>m*"oOjx+.m:weL1%) $M3 EλN6X@E8:Wy_X$&uLs6AǡX>~4] !\[&MқœMoܢ 93~R [+\ǖ߻(8(BX46Ðzs)qlH3=]5;^6"4]6SWX0*LiMM uBT1U/Y7q<Т~0Byo!.eW6sg\+c;\'RE.`Nj_qͩnO-<.`μ'X5gpئ{aN#tTZvwn,wXܰxַ!tjLĂWX @`o(ׅu m7=w݅&LK"6&@Ȅ&0gn/RUbQs`_j`LO ŦWIi7!g3 BK&'ԄcPy<7)ssNfݥM&N|ʽJ}1q~o}{;0~Z6czުIwLw{i=f*_KPmg+{FQ%,Db;17Bs@ωY}/Y_eƨ%[ë],xc:>Kxy)j%83yrpN^sJ,Nd`_4=R 3jx~=$i'TVkCw`BMgI jN= ¯j( ~oq9iXW{bΏ<,)T#Gyr51K,E/fJ`f_!<(0`Hk{BdBpʕ[l1&X ?i\̱o%&zus1PQ=wqsT? wLP[P}Cn#YȄm-N:A% =ToThr⏙Q5g~=:m;ƅw F>Уsp~ 'Yau,4%km46Z lUYPC(*0NL0b+βvWxnk7 IOfHUaNXlOq\BUAHJ= =`zn3=5xXqδWWY;w% Hmj}Wb*Sظa25(aXhAd`\ۼm:?%ή"ep8wlR@_i$0 U(Z*tZnMhX,t54}xUEb~BI<_cX~:w3=z 06г.4vopfFM$1n{Sue e„+,NGGv3f m;qsvCoN_I@ K9{BUkXxb )ozN7?ћ~5KYC !e.?!hΓ5|-sC8/{JO$v =IϵAw\ 1{*K̟N) BX,_/T]Lcjbǽ6Zbd5|$ldvYktdLO짋cnη,9'lje6hF* BD^ Q\;ipOmP (yXQYe4E`̎bR|AM"M3Ok԰ǀTGCۓ99 1ĂN*KK|þgHt3P'qpFs"Kcٯ("B0b)L#N3O9mu,tV8-\Pv2;;:_v?M7!j03wgt]C,<>[' 0;u:Gr&N3{K,1LJȥ^ϴ^VYZp-N;75NZXE}oF<rm17Ifu:Me?L% n;^酜sOZ4oV8}^>XOiu"\=Gɡ TF"Jzc CX==1eZbZ^҂.axf-LzMٳŔ70 9>k+=0:/H2+z}`gzf3=Phqy7 C왭b3'Ob @F<0#Y7<eN !|>3=%"~Rބ9kAG 1c}!U%D={fFY V!l)%쵦:;biz5jӫ$.=eX^e [E-Ŕ5 %^3HZCBABsOqq,h%gӹסؓz ^;Ue+GaO$-`z!^kJBMpv-^4YfLBF_ vdg {ɞ=G W fr=Ch.doR59qpnLLZ\ =ȶDT  7HmzzY84|):14Mv 8t|2bʪ=ČH?o82Jn_M%f?=#-؋lzHɞ{p &{Ca2ujފ_AdjI,o*^n7| ˎ̒=8ÖxD1N 6}~|*+4Ngs}m`OP{孞%?G='ET6ly!ѣ֭[6aӦM.rv{|Gf~myqfǜ ?i)I!,++[~}yybŊ]vuM<&wq۶me1e 1Ӄ^*KnNDLBٻ@pA#%{!|^}A͛7>ٳs×JKKM'4}oN{~n0rVDT92v^ף3B?۷gгg-[ڵkF W_-Y}wq2bH|[ՀpHrY, ,..ޱcǬY<# (wŸf_Ϟfĩ^ Nr~ h+xCڛ=ȄginTju.tsN}Mșl~c,&;SZjzypeI B1t%U8-rpZ934A}jƧ*v-//ߵk Y-~Imq Ɖ/dO=^s&gϧ$<*ӪZQ{ݳgO,}͘ݷ=O[j}yl#NDAF{sHu_ ŵZ¦cn߾{|};n.8=MMM>|8:>#2]L4t $˖PJ";;fUUg̘qW{Ŋ/isMMMpx8qkqŽ\nEIDAT_}UIMoi`Y1غE`ʞ}d~9?aְFH $1hnǏ5p뭷>Cݻw/**ڵkWҰȰ9 v|`ϧxڽ{3Lgݿ_WNzoTl>& ~4a');wT4Cٷoo{~ 0o*i/[SOuγm۶-[ΟU%=yg8,HL8ܹsXh'/655zoGmj۶mZh3(1#͟џ:>{ɬ>F B.]Ը$oرwܦM:ʏ>h׮]m^$(n~aBh`9蠃ꫮ]=.񷒽=Q7rp-ߖ1t ?nݺ9^S4-X-{؛fۯ'/u#)11 BuDsX쉊aH;2!CL{8Z/D;&aFfC̞={BK51-S@ %Ͳrʞ(+DIt|2`i$B$P4:{JKn)_t]F6z!8~4ˢĔvxmƻ}ƻ "ה (:{9t"ڃ=:-{p3hG:>8l?2;㱇v˽WI雗M;&JLnKKϞxU DXPI+){!Zc$MZe"g+Ȟ[FB60{E6{ɲH4F:&^:/>cζd|gϧ,BӺfB 5+fN a󇎙qX`쉊'k& hi7vRGKPyHE:*j[X펉_)ٟաO ڇN턦'T@΢}O [R{ mPSDR}PBB 0 /VTaϧx4!-(jW=S͉̎ZvįU{HB*%1aC:R2<{s9TBQ09d=Jyi}m٬M/ _v=QmtQ54jʈ9L&Oe>\?L V}ݨ?/NS*aAyK' W[)SԊgJ:a*e1-BK=t3 ~=G4jS^dr)i}=G2@i:r /~ %=$!LБ49T:jNP r؞sXOXQ7Mˡe}_jD9vdˋKDI#)4:J[2sETc"dV ;o瓄09ܨ -9OR1-Tg>$YⰏC0Ԟ/C>W\"JB‹4I8AFįi}$!XI BZv@' a6 )1h/JY_W\"JBa!}Q:$!l=t2LIW#"Y)r*.%!lZ}aǯN# tSZA-6lTcl0QcrY2R>agdI[Gqh_DJu̾bp(/#p69M5&,9Jx)i}I:a)P) B應{廤nڡ$֝v(+.& a;Ev$aZd$LC=GV0%/$!lW%POy&Փm,%{# aǫ;/ٳg]Mܧ-D+ an$ܧOnݺUTTF8~ @œᰶoٽ{]4MSEt^ }0r8d}ǚqi}@Ab"$p^~^Iv 92Ș0 MI:$B"2l9aįsJ:aįsJ JIXB)KB(%cIr, T%!ʱ$RR9PJ*ǒJIXB)KB(%cIr, T%!ʱ$RR9PJ*ǒJIXB)KB(%cIr, T%!ʱ$RR9PJ*ǒJIXB)KB(%cIr#5kIENDB`mayavi-4.1.0/integrationtests/mayavi/test_array_source.py0000644000175100001440000001546411674464502025011 0ustar ischnellusers00000000000000"""Tests for the ArraySource data source for MayaVi. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy # Enthought library imports. from traits.api import TraitError # Local imports. from common import TestCase class TestArraySource(TestCase): def check_input_validation(self, obj): """Tests if only the correct forms of input arrays are supported.""" # These should work. obj.scalar_data = numpy.zeros((2,2), 'd') obj.scalar_data = numpy.zeros((2,2,2), 'd') obj.scalar_data = None obj.vector_data = numpy.zeros((2,2,3), 'd') obj.vector_data = numpy.zeros((2,2,2,3), 'd') obj.vector_data = None # These should not. self.assertRaises(TraitError, setattr, obj, 'scalar_data', [1,2,3]) self.assertRaises(TraitError, setattr, obj, 'scalar_data', numpy.zeros((2,2,2,3), 'd')) obj.scalar_data = None self.assertRaises(TraitError, setattr, obj, 'vector_data', [[1,2,3]]) self.assertRaises(TraitError, setattr, obj, 'vector_data', numpy.zeros((2,2,2,1), 'd')) obj.vector_data = None obj.scalar_data = numpy.zeros((2,2), 'd') self.assertRaises(TraitError, setattr, obj, 'vector_data', numpy.zeros((4,4,3), 'd')) obj.vector_data = numpy.zeros((2,2,3), 'd') self.assertRaises(TraitError, setattr, obj, 'scalar_data', numpy.zeros((4,3), 'i')) self.assertRaises(TraitError, setattr, obj, 'scalar_data', numpy.zeros((2,2,2), 'i')) obj.scalar_data = numpy.zeros((2,2), 'f') # Clean up the object so it can be used for further testing. obj.scalar_data = obj.vector_data = None def make_2d_data(self): s = numpy.array([[0, 1],[2, 3]], 'd') v = numpy.array([[[1,1,1], [1,0,0]],[[0,1,0], [0,0,1]]], 'd') tps = numpy.transpose s, v = tps(s), tps(v, (1, 0, 2)) return s, v def make_3d_data(self): s = numpy.array([[[0, 1],[2, 3]], [[4, 5],[6, 7]]], 'd') v = numpy.array([[[[0,0,0], [1,0,0]], [[0,1,0], [1,1,0]]], [[[0,0,1], [1,0,1]], [[0,1,1], [1,1,1]]]], 'd') tps = numpy.transpose s, v = tps(s), tps(v, (2, 1, 0, 3)) return s, v def check(self): script = self.script s = script.engine.current_scene d1, d2 = s.children s1, v1 = d1.children[0].children[1:] expect = list(self.make_2d_data()) tps = numpy.transpose expect[0] = tps(expect[0]) expect[1] = tps(expect[1], (1, 0, 2)) sc1 = s1.actor.mapper.input.point_data.scalars.to_array() assert numpy.allclose(sc1.flatten(), expect[0].flatten()) vec1 = s1.actor.mapper.input.point_data.vectors.to_array() assert numpy.allclose(vec1.flatten(), expect[1].flatten()) s2, v2 = d2.children[0].children[1:] expect = list(self.make_3d_data()) tps = numpy.transpose expect[0] = tps(expect[0]) expect[1] = tps(expect[1], (2, 1, 0, 3)) sc2 = s2.actor.mapper.input.point_data.scalars.to_array() assert numpy.allclose(sc2.flatten(), expect[0].flatten()) vec2 = s2.actor.mapper.input.point_data.vectors.to_array() assert numpy.allclose(vec2.flatten(), expect[1].flatten()) def test(self): self.main() def do(self): ############################################################ # Imports. script = self.script from mayavi.sources.array_source import ArraySource from mayavi.modules.outline import Outline from mayavi.modules.surface import Surface from mayavi.modules.vectors import Vectors ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() d = ArraySource() self.check_input_validation(d) sc, vec = self.make_2d_data() d.origin = (-1, -1, 0) d.scalar_data = sc d.vector_data = vec script.add_source(d) # Create an outline for the data. o = Outline() script.add_module(o) # View the data. s = Surface() script.add_module(s) v = Vectors() script.add_module(v) # Add a 3D data source d = ArraySource() sc, vec = self.make_3d_data() d.scalar_data = sc d.vector_data = vec script.add_source(d) # Create an outline for the data. o = Outline() script.add_module(o) # View a slice. s = Surface() script.add_module(s) v = Vectors() script.add_module(v) # Set the scene to a suitable view. s.scene.z_plus_view() c = s.scene.camera c.azimuth(-30) c.elevation(30) self.check() ############################################################ # Test if saving a visualization and restoring it works. bg = s.scene.background # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene # Set the scene to a suitable view. s.scene.z_plus_view() c = s.scene.camera c.azimuth(-30) c.elevation(30) s.scene.background = bg self.check() ############################################################ # Test if the MayaVi2 visualization can be deepcopied. # Pop the source object. sources = s.children s.children = [] # Add it back to see if that works without error. s.children.extend(sources) s.scene.reset_zoom() self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. sources1 = copy.deepcopy(sources) s.children[:] = sources s.scene.reset_zoom() self.check() # If we have come this far, we are golden! if __name__ == "__main__": t = TestArraySource() t.test() mayavi-4.1.0/integrationtests/mayavi/test_optional_collection.py0000644000175100001440000001016311674464502026342 0ustar ischnellusers00000000000000"""Simple test for the Optional and Collection filters. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Local imports. from common import TestCase, get_example_data class TestOptionalCollection(TestCase): def test(self): self.main() def do(self): ############################################################ # Imports. script = self.script from mayavi.sources.vtk_file_reader import VTKFileReader from mayavi.filters.contour import Contour from mayavi.filters.optional import Optional from mayavi.filters.collection import Collection from mayavi.filters.api import PolyDataNormals from mayavi.modules.api import Surface ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() # Read a VTK (old style) data file. r = VTKFileReader() r.initialize(get_example_data('heart.vtk')) script.add_source(r) c = Contour() # `name` is used for the notebook tabs. n = PolyDataNormals(name='Normals') o = Optional(filter=n, label_text='Compute normals') coll = Collection(filters=[c, o], name='IsoSurface') script.add_filter(coll) s = Surface() script.add_module(s) ######################################## # do the testing. def check(coll): """Check if test status is OK given the collection.""" c, o = coll.filters c = c.filter n = o.filter assert coll.outputs[0].point_data.scalars.range == (127.5, 127.5) # Adding a contour should create the appropriate output in # the collection. c.contours.append(200) assert coll.outputs[0].point_data.scalars.range == (127.5, 200.0) # the collection's output should be that of the normals. assert coll.outputs[0] is n.outputs[0] # disable the optional filter and check. o.enabled = False assert 'disabled' in o.name assert coll.outputs[0] is c.outputs[0] # Set back everything to original state. c.contours.pop() o.enabled = True assert coll.outputs[0].point_data.scalars.range == (127.5, 127.5) assert coll.outputs[0] is n.outputs[0] assert 'disabled' not in o.name check(coll) ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene # Now do the check. coll = s.children[0].children[0] check(coll) ############################################################ # Test if the Mayavi2 visualization can be deep-copied. # Pop the source object. source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) # Now do the check. coll = s.children[0].children[0] check(coll) # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 # Now do the check. coll = s.children[0].children[0] check(coll) # If we have come this far, we are golden! if __name__ == "__main__": t = TestOptionalCollection() t.test() mayavi-4.1.0/integrationtests/mayavi/test_vtk_data_source.py0000644000175100001440000001010111674464502025447 0ustar ischnellusers00000000000000"""Simple test for the VTKDataSource source. This is basically a copy of test_contour.py with the data source alone modified. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Local imports. from common import get_example_data from test_contour import TestContour class TestVTKDataSource(TestContour): def make_data(self): script = self.script from mayavi.sources.vtk_data_source import VTKDataSource from tvtk.api import tvtk ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() # Read a VTK (old style) data file. r = tvtk.StructuredPointsReader() r.file_name = get_example_data('heart.vtk') r.update() d = VTKDataSource(data=r.output) script.add_source(d) def test(self): self.main() def do(self): # Setup the source. self.make_data() from mayavi.modules.outline import Outline from mayavi.modules.iso_surface import IsoSurface from mayavi.modules.contour_grid_plane \ import ContourGridPlane from mayavi.modules.scalar_cut_plane import ScalarCutPlane script = self.script s = script.engine.current_scene # Create an outline for the data. o = Outline() script.add_module(o) # Create one ContourGridPlane normal to the 'x' axis. cgp = ContourGridPlane() script.add_module(cgp) # Set the position to the middle of the data. cgp.grid_plane.position = 15 # Another with filled contours normal to 'y' axis. cgp = ContourGridPlane() cgp.contour.filled_contours = True # Set the axis and position to the middle of the data. cgp.grid_plane.axis = 'y' cgp.grid_plane.position = 15 script.add_module(cgp) # An isosurface module. iso = IsoSurface(compute_normals=True) script.add_module(iso) iso.contour.contours = [200.0] # An interactive scalar cut plane. cp = ScalarCutPlane() script.add_module(cp) ip = cp.implicit_plane ip.normal = 0,0,1 ip.origin = 0,0,5 ip.widget.enabled = False # Set the scene to an isometric view. s.scene.isometric_view() self.check() ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene self.check() ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) # Now set the enabled status of the widget, this is impossible # to get correctly. cp = source.children[0].children[-1] cp.implicit_plane.widget.enabled = False self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 cp = source1.children[0].children[-1] cp.implicit_plane.widget.enabled = False self.check() # If we have come this far, we are golden! if __name__ == "__main__": t = TestVTKDataSource() t.test() mayavi-4.1.0/integrationtests/mayavi/test_mlab_envisage.py0000644000175100001440000000072311674464502025077 0ustar ischnellusers00000000000000from mayavi import mlab from pyface.api import GUI def close(): """Close the scene.""" f = mlab.gcf() e = mlab.get_engine() e.window.close() def test_mlab_envisage(): """Test if mlab runs correctly when the backend is set to 'envisage'.""" @mlab.show def f(): mlab.options.backend = 'envisage' mlab.test_contour3d() GUI.invoke_after(3000, close) f() if __name__ == '__main__': test_mlab_envisage() mayavi-4.1.0/integrationtests/mayavi/test_user_defined.py0000644000175100001440000001254711674464502024746 0ustar ischnellusers00000000000000"""Simple test for the UsedDefined filter. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Enthought library imports. from tvtk.api import tvtk # Local imports. from common import TestCase, get_example_data class TestUserDefined(TestCase): def check(self, saved=False): """Does the checking, if saved is True it does not change the properties at first to see how those behave and only tests the final unpickled state.""" script = self.script e = script.engine scene = e.current_scene src = scene.children[0] ud = src.children[0] o = ud.children[0].children[0].children[0] mm = o.children[0] if not saved: assert ud.filter.vector_mode == 'compute_gradient' assert src.outputs[0].point_data.scalars.name == 't' assert src.outputs[0].point_data.vectors.name == 'uvw' expect = ['ScalarGradient', 'Vorticity'] expect1 = [x +'-y' for x in expect] expect2 = [x + ' magnitude' for x in expect] # FIXME: This is really a bug in VTK, the name of the scalar # should really be ScalarGradient-y. This is fixed in # 5.2 but earlier versions fail. assert o.outputs[0].point_data.scalars.name in expect1 assert o.outputs[0].point_data.vectors.name in expect assert mm.scalar_lut_manager.data_name in expect1 # Turn of extraction. o.enabled = False assert o.outputs[0].point_data.scalars.name in expect2 assert o.outputs[0].point_data.vectors.name in expect assert mm.scalar_lut_manager.data_name in expect2 # Compute the vorticity ud.filter.vector_mode = 'compute_vorticity' assert o.outputs[0].point_data.scalars.name == 'Vorticity magnitude' assert o.outputs[0].point_data.vectors.name == 'Vorticity' assert mm.scalar_lut_manager.data_name == 'Vorticity magnitude' # Turn on extraction. o.enabled = True assert o.outputs[0].point_data.scalars.name == 'Vorticity-y' assert o.outputs[0].point_data.vectors.name == 'Vorticity' assert mm.scalar_lut_manager.data_name == 'Vorticity-y' # Turn off extraction. o.enabled = False def test(self): self.main() def do(self): ############################################################ # Imports. from mayavi.filters.optional import Optional from mayavi.filters.user_defined import UserDefined from mayavi.filters.api import (CellToPointData, ExtractVectorNorm, ExtractVectorComponents) from mayavi.modules.api import ScalarCutPlane from mayavi.sources.vtk_xml_file_reader import VTKXMLFileReader ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() script = mayavi = self.script # Read a VTK (old style) data file. r = VTKXMLFileReader() r.initialize(get_example_data('fire_ug.vtu')) script.add_source(r) # Create the filters. # CellDerivatives cd = tvtk.CellDerivatives() ud = UserDefined(filter=cd) script.add_filter(ud) ctp = CellToPointData() ctp.filter.pass_cell_data = False script.add_filter(ctp) evn = ExtractVectorNorm() script.add_filter(evn) evc = ExtractVectorComponents(component='y-component') o = Optional(filter=evc) script.add_filter(o) script.add_module(ScalarCutPlane()) s.scene.isometric_view() # Check. self.check(saved=False) ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene s.scene.isometric_view() # Now do the check. self.check(saved=True) ############################################################ # Test if the Mayavi2 visualization can be deep-copied. # Pop the source object. source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) # Now do the check. s.scene.isometric_view() self.check(saved=True) # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 # Now do the check. s.scene.isometric_view() self.check(saved=True) # If we have come this far, we are golden! if __name__ == "__main__": t = TestUserDefined() t.test() mayavi-4.1.0/integrationtests/mayavi/run.py0000644000175100001440000000406411674464502022052 0ustar ischnellusers00000000000000#!/usr/bin/env python """Script to run all the tests. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. import sys import os from os.path import splitext import glob from common import test, TestCase def get_tests(): """Get all the tests to run. """ files = glob.glob('test_*.py') return files def run_all(tests): """Run the given tests. """ args = ' '.join(sys.argv[1:]) for test in tests: cmd = 'python %s %s'%(test, args) print cmd os.system(cmd) class RunAllTests(TestCase): """Runs all the tests in one go, instead of running each test separately. This speeds up the testing. """ def get_tests(self): tests = get_tests() tests = [splitext(t)[0] for t in tests] klasses = [] for test in tests: # Find test. m = __import__(test) m.mayavi = self.script m.application = self.application for name in dir(m): klass = getattr(m, name) try: if issubclass(klass, TestCase) and klass is not TestCase: mod_name = '%s.%s'%(test, name) klasses.append((mod_name, klass)) break except TypeError: continue return klasses def do(self): klasses = self.get_tests() for name, klass in klasses: # Close existing scenes. e = self.script.engine for scene in e.scenes: e.close_scene(scene) print '*'*80 print name obj = klass() obj.set(script=self.script) obj.test() def main(): argv = ' '.join(sys.argv) if '--one-shot' in argv: argv = argv.replace('--one-shot', '') sys.argv = argv.split() t = RunAllTests() t.main() else: tests = get_tests() run_all(tests) if __name__ == "__main__": main() mayavi-4.1.0/integrationtests/mayavi/test_generic_module.py0000644000175100001440000001261511674464502025267 0ustar ischnellusers00000000000000"""Simple test for the GenericModule. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Local imports. from common import TestCase, get_example_data class TestGenericModule(TestCase): def test(self): self.main() def do(self): ############################################################ # Imports. script = self.script from mayavi.filters.optional import Optional from mayavi.filters.warp_scalar import WarpScalar from mayavi.filters.cut_plane import CutPlane from mayavi.components.poly_data_normals import PolyDataNormals from mayavi.components.contour import Contour from mayavi.components.actor import Actor from mayavi.modules.generic_module import GenericModule from mayavi.sources.vtk_xml_file_reader import VTKXMLFileReader ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() # Read a VTK (old style) data file. r = VTKXMLFileReader() r.initialize(get_example_data('fire_ug.vtu')) script.add_source(r) # We now create the complete equivalent of a ScalarCutPlane in # the next block! cp = CutPlane() w = WarpScalar() warper = Optional(filter=w, label_text='Enable warping', enabled=False) c = Contour() ctr = Optional(filter=c, label_text='Enable contours', enabled=False) p = PolyDataNormals(name='Normals') normals = Optional(filter=p, label_text='Compute normals', enabled=False) a = Actor() components = [cp, warper, ctr, normals, a] m = GenericModule(name='ScalarCutPlane', components=components, contour=c, actor=a) script.add_module(m) s.scene.isometric_view() ######################################## # do the testing. def check(mod): """Check if test status is OK for the given module.""" cut, warper, ctr, normals, a = mod.components c = ctr.filter # The intermediate ones are disabled. assert normals.outputs[0] is cut.outputs[0] # Enable the contours. ctr.enabled = True assert ctr.outputs[0] is c.outputs[0] assert ctr.outputs[0] is normals.outputs[0] rng = normals.outputs[0].point_data.scalars.range assert (rng[1] - rng[0]) < 1e-4 # Turn on auto-contours c.auto_contours = True assert len(normals.outputs[0].points) == 0 # Increase number of contours and the range should change. c.number_of_contours = 10 assert len(normals.outputs[0].points) != 0 rng = normals.outputs[0].point_data.scalars.range assert rng[0] < rng[1] # Check if pipeline_changed is correctly propagated. old = normals.outputs[0] assert a.mapper.scalar_mode == 'default' c.filled_contours = True assert normals.outputs[0] != old assert normals.outputs[0] is c.outputs[0] # Check if the actor responds correctly to the # filled_contour change. assert a.mapper.scalar_mode == 'use_cell_data' # Set back everything to original state. c.filled_contours = False assert a.mapper.scalar_mode == 'default' c.number_of_contours = 1 c.auto_contours = False ctr.enabled = False assert normals.outputs[0] is cut.outputs[0] check(m) ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene s.scene.isometric_view() # Now do the check. m = s.children[0].children[0].children[0] check(m) ############################################################ # Test if the Mayavi2 visualization can be deep-copied. # Pop the source object. source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) # Now do the check. m = s.children[0].children[0].children[0] s.scene.isometric_view() check(m) # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 # Now do the check. m = s.children[0].children[0].children[0] s.scene.isometric_view() check(m) # If we have come this far, we are golden! if __name__ == "__main__": t = TestGenericModule() t.test() mayavi-4.1.0/integrationtests/mayavi/test_image_plane_widget.py0000644000175100001440000001116411674464502026110 0ustar ischnellusers00000000000000"""Tests for the ImagePlaneWidget module. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy # Enthought library imports. from traits.api import TraitError # Local imports. from common import TestCase class TestImagePlaneWidget(TestCase): def make_data(self): """Creates suitable data for the test.""" dims = numpy.array((64, 64, 64), 'i') # Create some scalars to render. dx, dy, dz = 10.0/(dims - 1) x = numpy.reshape(numpy.arange(-5.0, 5.0+dx*0.5, dx, 'f'), (dims[0], 1, 1)) y = numpy.reshape(numpy.arange(-5.0, 5.0+dy*0.5, dy, 'f'), (1, dims[1], 1)) z = numpy.reshape(numpy.arange(-5.0, 5.0+dz*0.5, dz, 'f'), (1, 1, dims[0])) scalars = numpy.sin(x*y*z)/(x*y*z) return scalars def set_view(self, s): """Sets the view correctly.""" #s.scene.reset_zoom() s.scene.z_plus_view() c = s.scene.camera c.azimuth(30) c.elevation(30) s.render() def check(self): script = self.script src = script.engine.current_scene.children[0] i1, i2, i3 = src.children[0].children[1:] assert i1.ipw.plane_orientation == 'x_axes' assert numpy.allclose(i1.ipw.center, (0, 31.5, 31.5)) rng = i1.ipw.reslice_output.point_data.scalars.range assert numpy.allclose(rng, (-0.2, 1.0), atol=0.1) assert i2.ipw.plane_orientation == 'y_axes' assert numpy.allclose(i2.ipw.center, (31.5, 0, 31.5)) rng = i2.ipw.reslice_output.point_data.scalars.range assert numpy.allclose(rng, (-0.2, 1.0), atol=0.1) assert i3.ipw.plane_orientation == 'z_axes' assert numpy.allclose(i3.ipw.center, (31.5, 31.5, 0)) rng = i3.ipw.reslice_output.point_data.scalars.range assert numpy.allclose(rng, (-0.2, 1.0), atol=0.1) def test(self): self.main() def do(self): ############################################################ # Imports. script = self.script from mayavi.sources.array_source import ArraySource from mayavi.modules.outline import Outline from mayavi.modules.image_plane_widget import ImagePlaneWidget ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() d = ArraySource() sc = self.make_data() d.scalar_data = sc script.add_source(d) # Create an outline for the data. o = Outline() script.add_module(o) # ImagePlaneWidgets for the scalars ipw = ImagePlaneWidget() script.add_module(ipw) ipw_y = ImagePlaneWidget() script.add_module(ipw_y) ipw_y.ipw.plane_orientation = 'y_axes' ipw_z = ImagePlaneWidget() script.add_module(ipw_z) ipw_z.ipw.plane_orientation = 'z_axes' # Set the scene to a suitable view. self.set_view(s) self.check() ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene # Set the scene to a suitable view. self.set_view(s) self.check() ############################################################ # Test if the MayaVi2 visualization can be deepcopied. # Pop the source object. sources = s.children s.children = [] # Add it back to see if that works without error. s.children.extend(sources) self.set_view(s) self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. sources1 = copy.deepcopy(sources) s.children[:] = sources self.set_view(s) self.check() # If we have come this far, we are golden! if __name__ == "__main__": t = TestImagePlaneWidget() t.test() mayavi-4.1.0/integrationtests/mayavi/test_streamline.py0000644000175100001440000001264411674464502024453 0ustar ischnellusers00000000000000"""Tests for the Streamline module. This module uses the compare_image function to perform image based testing. This is so just to illustrate how you could use image based tests. However, we recommend that one does not use image based tests since they are not always reliable and a pain. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy # Enthought library imports. from traits.api import TraitError # Local imports. from common import TestCase class TestStreamline(TestCase): def make_data(self): """Trivial data -- creates an elementatry scalar field and a constant vector field along the 'x' axis.""" s = numpy.arange(0.0, 10.0, 0.01) s = numpy.reshape(s, (10,10,10)) s = numpy.transpose(s) v = numpy.zeros(3000, 'd') v[::3] = 1.0 v = numpy.reshape(v, (10,10,10,3)) return s, v def test(self): self.main() def do(self): ############################################################ # Imports. script = self.script from mayavi.sources.array_source import ArraySource from mayavi.modules.outline import Outline from mayavi.modules.streamline import Streamline ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() d = ArraySource() sc, vec = self.make_data() d.origin = (-5, -5, -5) d.scalar_data = sc d.vector_data = vec script.add_source(d) # Create an outline for the data. o = Outline() script.add_module(o) # View the data. st = Streamline() script.add_module(st) widget = st.seed.widget widget.set(radius=1.0, center=(-4.0, -4.0, -4.0), theta_resolution=4, phi_resolution=4) st = Streamline(streamline_type='ribbon') seed = st.seed seed.widget = seed.widget_list[1] script.add_module(st) seed.widget.set(point1=(-5.0, -4.5, -4.0), point2=(-5.0, -4.5, 4.0)) st.ribbon_filter.width = 0.25 st = Streamline(streamline_type='tube') seed = st.seed seed.widget = seed.widget_list[2] script.add_module(st) seed.widget.set(center=(-5.0, 1.5, -2.5)) st.tube_filter.radius = 0.15 st = Streamline(streamline_type='tube') seed = st.seed seed.widget = seed.widget_list[3] script.add_module(st) seed.widget.position=(-5.0, 3.75, 3.75) st.tube_filter.radius = 0.2 # Set the scene to a suitable view. s.scene.z_plus_view() c = s.scene.camera c.azimuth(-30) c.elevation(30) s.render() # Now compare the image. self.compare_image(s, 'images/test_streamline.png') ############################################################ # Test if the modules respond correctly when the components # are changed. tf = st.tube_filter st.tube_filter = tf.__class__() st.tube_filter = tf st.ribbon_filter = st.ribbon_filter.__class__() seed = st.seed st.seed = seed.__class__() st.seed = seed st.actor = st.actor.__class__() tracer = st.stream_tracer st.stream_tracer = tracer.__class__() st.stream_tracer = tracer s.render() # Now compare the image. self.compare_image(s, 'images/test_streamline.png') s.render() ############################################################ # Test if saving a visualization and restoring it works. bg = s.scene.background # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene # Set the scene to a suitable view. s.scene.z_plus_view() c = s.scene.camera c.azimuth(-30) c.elevation(30) s.render() s.scene.background = bg # Now compare the image. self.compare_image(s, 'images/test_streamline.png') ############################################################ # Test if the MayaVi2 visualization can be deepcopied. # Pop the source object. sources = s.children s.children = [] # Add it back to see if that works without error. s.children.extend(sources) s.scene.reset_zoom() # Now compare the image. self.compare_image(s, 'images/test_streamline.png') # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. sources1 = copy.deepcopy(sources) s.children[:] = sources s.scene.reset_zoom() self.compare_image(s, 'images/test_streamline.png') # If we have come this far, we are golden! if __name__ == "__main__": t = TestStreamline() t.test() mayavi-4.1.0/integrationtests/mayavi/README.txt0000644000175100001440000000604511674464502022373 0ustar ischnellusers00000000000000======================= Notes on testing Mayavi ======================= This directory contains integration tests for mayavi. The unit tests are in `mayavi/tests` and are distributed with the package. The tvtk unit tests are in `enthought/tvtk/tests`. Running the tests ================= The best way to run the tests in this directory is to do:: $ ./run.py This will run all of the tests in the mayavi directory by running them each in a separate Python interpreter. You may also run each test individually. For example:: $ python test_contour.py To see the supported command line options you may do:: $ python test_contour.py -h # or --help Usage: test_contour.py [options] Options: -h, --help show this help message and exit -v, --verbose Print verbose output -i, --interact Allow interaction after test (default: False) -s, --nostandalone Run test using envisage without standalone (default: False) -o, --offscreen Always use offscreen rendering when generating images (default: False) You should be able to run the tests as:: $ nosetests However, nosetests changes the Python environment when it runs the tests and this does not always work cleanly with Mayavi. So if this does run, fine if not use `run.py`. Writing tests ============= Mayavi tests are not typical tests. The easiest way to get started writing a test is to look at the existing ones and copy one of them then modify it suitably for your test. Image based tests ================= Image based tests (like the ones VTK uses) are possible but a pain since there are almost always complications. The image tests may fail due to hardware/driver issues and this becomes a red-herring for new users. **Therefore, in general we do not recommend the use of image based tests**. There is one test that is image based for your reference -- `test_streamline.py`, the images for this test are in the `images` directory. The image based tests use the same approach that VTK uses. A visualization is created and the rendered scene is saved to a PNG image. The test code will check to see if a rendered scene is similar to the image saved. If the pictures are nearly-identical then the test passes. If not one gets a failure with an error message and a bunch of images that indicate the difference between the saved and rendered images. In addition no error or warning messages should be shown if a test passes. VTK and the `mayavi` image based tests support the notion of multiple valid test images. For example between VTK-4.4 and 5.0 there were changes made to the way the camera was reset. Thus, an image produced with 4.4 is different from that with 5.0. In order to be able to test for these VTK tests allow the user to save a number of test images. These are all tested in sequence to check for a valid image. If a default test image is called `test_outline_image.png` then its various alternatives can be called `test_outline_image_%d.png` (for example, `test_outline_image_1.png`). mayavi-4.1.0/integrationtests/mayavi/test_grid_plane.py0000644000175100001440000000731711674464502024415 0ustar ischnellusers00000000000000"""Simple test for the grid plane and outline module. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Local imports. from common import TestCase, get_example_data class TestGridPlane(TestCase): def check(self): script = self.script s = script.engine.current_scene mm = s.children[0].children[0] gp1, gp2, gp3 = mm.children[1:] assert gp1.grid_plane.axis == 'x' assert gp1.grid_plane.position == 0 assert gp1.actor.property.ambient == 1.0 assert gp2.grid_plane.axis == 'y' assert gp2.grid_plane.position == 16 assert gp2.actor.property.ambient == 1.0 assert gp3.grid_plane.axis == 'z' assert gp3.grid_plane.position == 6 assert gp3.actor.property.ambient == 1.0 def test(self): self.main() def do(self): ############################################################ # Imports. script = self.script from mayavi.sources.vtk_file_reader import VTKFileReader from mayavi.modules.outline import Outline from mayavi.modules.grid_plane import GridPlane ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() # Read a VTK (old style) data file. r = VTKFileReader() r.initialize(get_example_data('heart.vtk')) script.add_source(r) # Create an outline for the data. o = Outline() script.add_module(o) # Create three simple grid plane modules. # First normal to 'x' axis. gp1 = GridPlane() script.add_module(gp1) # Second normal to 'y' axis. gp2 = GridPlane() # We'll test how robust things are by setting attributes # *before* we add it to the scene. gp2.grid_plane.axis = 'y' gp2.grid_plane.position = 16 script.add_module(gp2) # Third normal to 'z' axis. gp3 = GridPlane() script.add_module(gp3) gp3.grid_plane.axis = 'z' gp3.grid_plane.position = 6 for gp in (gp1, gp2, gp3): gp.actor.property.set(ambient=1.0) # Set the scene to an isometric view. s.scene.isometric_view() self.check() ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene self.check() ############################################################ # Test if the MayaVi2 visualization can be deepcopied. # Pop the source object. source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 self.check() # If we have come this far, we are golden! if __name__ == "__main__": t = TestGridPlane() t.test() mayavi-4.1.0/integrationtests/mayavi/test_ipw_multiple_scalars.py0000644000175100001440000000505111674464502026524 0ustar ischnellusers00000000000000 # Author: Prabhu Ramachandran # Copyright (c) 2009, Prabhu Ramachandran # License: BSD Style. # Local imports. from common import TestCase from numpy import zeros, random from tvtk.api import tvtk class TestIPWMultipleScalars(TestCase): """Tests the IPW with multiple scalars. We have this in addition to the unittests since some of these errors only show up when the view is active.""" def test(self): self.main() def do(self): ############################################################ # Imports. script = self.script from mayavi.sources.vtk_data_source import VTKDataSource from mayavi.modules.api import ImagePlaneWidget # Create dataset with multiple scalars. arr1 = zeros(27, 'f') for n in range(27): arr1[n] = (1+float(n))/10.0 arr2 = (arr1 + 1).astype('d') arr3 = arr1 + 2.0*(0.5 - random.random(27)) arr3 = arr3.astype('f') p = tvtk.ImageData(dimensions=[3,3,3],spacing=[1,1,1], scalar_type='int') p.point_data.scalars = arr1 p.point_data.scalars.name = 'first' j2 = p.point_data.add_array(arr2) p.point_data.get_array(j2).name='second' j3 = p.point_data.add_array(arr3) p.point_data.get_array(j3).name='third' p.update() # Make the pipeline. self.new_scene() src = VTKDataSource(data=p) script.add_source(src) ipw = ImagePlaneWidget() script.add_module(ipw) # Test. ipw = ipw.ipw scalars = ipw.input.point_data.scalars r = scalars.range expect = min(arr1), max(arr1) assert r == expect o = src.outputs[0] o.update_traits() st = ipw.input.scalar_type assert scalars.data_type == 10 assert st == 'float' src.point_scalars_name = 'second' scalars = ipw.input.point_data.scalars r = scalars.range expect = min(arr2), max(arr2) assert r == expect o.update_traits() st = ipw.input.scalar_type assert scalars.data_type == 11 assert st == 'double' src.point_scalars_name = 'third' scalars = ipw.input.point_data.scalars r = scalars.range expect = min(arr3), max(arr3) assert r == expect o.update_traits() st = ipw.input.scalar_type assert scalars.data_type == 10 assert st == 'float' if __name__ == '__main__': t = TestIPWMultipleScalars() t.test() mayavi-4.1.0/integrationtests/mayavi/test_glyph.py0000644000175100001440000001304411674464502023426 0ustar ischnellusers00000000000000"""Tests for the Glyph module. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy # Enthought library imports. from traits.api import TraitError # Local imports. from common import TestCase class TestGlyph(TestCase): def make_data(self): """Trivial data -- creates an elementatry scalar field and a constant vector field along the 'x' axis.""" s = numpy.arange(0.0, 10.0, 0.01) s = numpy.reshape(s, (10,10,10)) s = numpy.transpose(s) v = numpy.zeros(3000, 'd') v[1::3] = 1.0 v = numpy.reshape(v, (10,10,10,3)) return s, v def set_view(self, s): """Sets the view correctly.""" #s.scene.reset_zoom() s.scene.z_plus_view() c = s.scene.camera c.azimuth(-30) c.elevation(20) s.render() def check(self): script = self.script s = script.engine.current_scene src = s.children[0] g = src.children[0].children[1] assert g.glyph.glyph_source.glyph_position == 'center' assert g.glyph.glyph.vector_mode == 'use_normal' assert g.glyph.glyph.scale_factor == 0.5 assert g.actor.property.line_width == 1.0 v = src.children[0].children[2] glyph = v.glyph gs = glyph.glyph_source assert gs.glyph_position == 'tail' assert gs.glyph_source == gs.glyph_list[1] assert numpy.allclose(v.implicit_plane.normal, (0., 1., 0.)) v = src.children[0].children[3] glyph = v.glyph gs = glyph.glyph_source assert gs.glyph_source == gs.glyph_list[2] assert gs.glyph_position == 'head' assert numpy.allclose(v.implicit_plane.normal, (0., 1., 0.)) def test(self): self.main() def do(self): ############################################################ # Imports. script = self.script from mayavi.sources.array_source import ArraySource from mayavi.modules.outline import Outline from mayavi.modules.glyph import Glyph from mayavi.modules.vector_cut_plane import VectorCutPlane ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() d = ArraySource() sc, vec = self.make_data() d.origin = (-5, -5, -5) d.scalar_data = sc d.vector_data = vec script.add_source(d) # Create an outline for the data. o = Outline() script.add_module(o) # Glyphs for the scalars g = Glyph() script.add_module(g) g.glyph.glyph_source.glyph_position = 'center' g.glyph.glyph.vector_mode = 'use_normal' g.glyph.glyph.scale_factor = 0.5 g.actor.property.line_width = 1.0 v = VectorCutPlane() glyph = v.glyph gs = glyph.glyph_source gs.glyph_position = 'tail' gs.glyph_source = gs.glyph_list[1] script.add_module(v) v.implicit_plane.set(normal=(0, 1, 0), origin=(0, 3, 0)) v = VectorCutPlane() glyph = v.glyph gs = glyph.glyph_source gs.glyph_source = gs.glyph_list[2] gs.glyph_position = 'head' script.add_module(v) v.implicit_plane.set(normal=(0, 1, 0), origin=(0, -2, 0)) # Set the scene to a suitable view. self.set_view(s) self.check() ############################################################ # Test if the modules respond correctly when the components # are changed. g.actor = g.actor.__class__() glyph = g.glyph g.glyph = glyph.__class__() g.glyph = glyph glyph = v.glyph v.glyph = glyph.__class__() v.glyph = glyph v.actor = v.actor.__class__() v.cutter = v.cutter.__class__() ip = v.implicit_plane v.implicit_plane = ip.__class__() v.implicit_plane = ip s.render() self.check() ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene # Set the scene to a suitable view. self.set_view(s) self.check() ############################################################ # Test if the MayaVi2 visualization can be deepcopied. # Pop the source object. sources = s.children s.children = [] # Add it back to see if that works without error. s.children.extend(sources) self.set_view(s) self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. sources1 = copy.deepcopy(sources) s.children[:] = sources self.set_view(s) self.check() # If we have come this far, we are golden! if __name__ == "__main__": t = TestGlyph() t.test() mayavi-4.1.0/integrationtests/mayavi/test_mlab_show.py0000644000175100001440000000176211674464502024262 0ustar ischnellusers00000000000000"""Test for the mlab.show function/decorator.""" from inspect import getmembers from mayavi import mlab from pyface.api import GUI def do_mlab(): ############################################################ # run all the "test_foobar" functions in the mlab module. for name, func in getmembers(mlab): if not callable(func) or not name[:4] in ('test', 'Test'): continue mlab.clf() func() def close(): """Close the scene.""" f = mlab.gcf() e = mlab.get_engine() v = e.get_viewer(f) v.close() def test_mlab_show(): """Test mlab.show()""" do_mlab() # Automatically close window in 2500 msecs. GUI.invoke_after(2500, close) mlab.show() def test_show_decorator(): """Test the @mlab.show decorator""" @mlab.show def f(): do_mlab() # Automatically close window in 2500 msecs. GUI.invoke_after(2500, close) f() if __name__ == '__main__': test_mlab_show() test_show_decorator() mayavi-4.1.0/integrationtests/mayavi/test_set_active_attribute.py0000644000175100001440000000704111674464502026514 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Local imports. from common import TestCase, get_example_data class TestSetActiveAttribute(TestCase): def check(self): """Check if the visualization is OK. Note that this is not an image based check, which is very convenient. """ e = self.script.engine scene = e.current_scene src = scene.children[0] assert src.point_scalars_name == 'u' c = src.children[1] sc = c.outputs[0].point_data.scalars assert sc.name == 'u' # It is an iso-contour! assert sc.range[0] == sc.range[1] aa = c.children[0].children[0] assert aa.point_scalars_name == 't' sc = aa.outputs[0].point_data.scalars assert sc.name == 't' assert abs(sc.range[0] - 308) < 1.0 assert abs(sc.range[1] - 631) < 1.0 s = aa.children[0].children[0] def test(self): self.main() def do(self): """Test for the SetActiveAttribute filter. """ from mayavi.sources.api import VTKXMLFileReader from mayavi.filters.contour import Contour from mayavi.filters.api import PolyDataNormals from mayavi.filters.set_active_attribute import SetActiveAttribute from mayavi.modules.api import Surface, Outline mayavi = script = self.script scene = self.new_scene() r = VTKXMLFileReader() r.initialize(get_example_data('fire_ug.vtu')) mayavi.add_source(r) r.point_scalars_name = 'u' o = Outline() mayavi.add_module(o) c = Contour() mayavi.add_filter(c) n = PolyDataNormals() mayavi.add_filter(n) aa = SetActiveAttribute() mayavi.add_filter(aa) aa.point_scalars_name = 't' s = Surface() mayavi.add_module(s) scene.scene.isometric_view() # Check if things are OK. self.check() ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. mayavi.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = mayavi.engine engine.close_scene(s) # Load visualization mayavi.load_visualization(f) s = engine.current_scene # Now do the check. s.scene.isometric_view() self.check() ############################################################ # Test if the Mayavi2 visualization can be deep-copied. # Pop the source object. source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) # Now do the check. self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 # Now do the check. s.scene.isometric_view() self.check() # If we have come this far, we are golden! if __name__ == '__main__': t = TestSetActiveAttribute() t.test() mayavi-4.1.0/integrationtests/mayavi/test_contour.py0000644000175100001440000001305211674464502023773 0ustar ischnellusers00000000000000"""Simple test for the contour related modules. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy # Local imports. from common import TestCase, get_example_data class TestContour(TestCase): def check(self): """Do the actual testing.""" script = self.script e = script.engine scene = e.current_scene src = scene.children[0] mm = src.children[0] cgp1 = mm.children[1] assert cgp1.grid_plane.position == 15 cgp2 = mm.children[2] assert cgp2.contour.filled_contours == True assert cgp2.grid_plane.axis == 'y' assert cgp2.grid_plane.position == 15 iso = mm.children[3] ctr = iso.contour.contours assert iso.compute_normals == True assert ctr == [200.0] rng = iso.actor.mapper.input.point_data.scalars.range assert rng[0] == 200.0 assert rng[1] == 200.0 cp = mm.children[4] ip = cp.implicit_plane assert abs(numpy.sum(ip.normal - (0,0,1))) < 1e-16 assert abs(numpy.sum(ip.origin - (0,0,5))) < 1e-16 assert ip.widget.enabled == False def test(self): self.main() def do(self): ############################################################ # Imports. script = self.script from mayavi.sources.vtk_file_reader import VTKFileReader from mayavi.modules.outline import Outline from mayavi.modules.iso_surface import IsoSurface from mayavi.modules.contour_grid_plane \ import ContourGridPlane from mayavi.modules.scalar_cut_plane import ScalarCutPlane ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() # Read a VTK (old style) data file. r = VTKFileReader() r.initialize(get_example_data('heart.vtk')) script.add_source(r) # Create an outline for the data. o = Outline() script.add_module(o) # Create one ContourGridPlane normal to the 'x' axis. cgp1 = ContourGridPlane() script.add_module(cgp1) # Set the position to the middle of the data. cgp1.grid_plane.position = 15 # Another with filled contours normal to 'y' axis. cgp2 = ContourGridPlane() cgp2.contour.filled_contours = True # Set the axis and position to the middle of the data. cgp2.grid_plane.axis = 'y' cgp2.grid_plane.position = 15 script.add_module(cgp2) # An isosurface module. iso = IsoSurface(compute_normals=True) script.add_module(iso) iso.contour.contours = [200.0] # An interactive scalar cut plane. cp = ScalarCutPlane() script.add_module(cp) ip = cp.implicit_plane ip.normal = 0,0,1 ip.origin = 0,0,5 ip.widget.enabled = False # Set the scene to an isometric view. s.scene.isometric_view() # Now test. self.check() ############################################################ # Test if the modules respond correctly when the components # are changed. ctr = cgp2.contour cgp2.contour = ctr.__class__() cgp2.contour = ctr cgp2.actor = cgp2.actor.__class__() iso.contour = iso.contour.__class__() iso.contour.contours = [200.0] iso.actor = iso.actor.__class__() iso.normals = iso.normals.__class__() ip = cp.implicit_plane cp.implicit_plane = cp.implicit_plane.__class__() cp.implicit_plane = ip ip.widget.enabled = False cp.contour = cp.contour.__class__() cp.cutter = cp.cutter.__class__() cp.actor = cp.actor.__class__() s.render() # Now check. self.check() ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene self.check() ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) # Now set the enabled status of the widget, this is impossible # to get correctly. cp = source.children[0].children[-1] cp.implicit_plane.widget.enabled = False self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 cp = source1.children[0].children[-1] cp.implicit_plane.widget.enabled = False self.check() # If we have come this far, we are golden! if __name__ == "__main__": t = TestContour() t.test() mayavi-4.1.0/integrationtests/mayavi/test_mlab.py0000644000175100001440000000354011674464502023216 0ustar ischnellusers00000000000000"""Tests for the mlab interface to Mayavi """ # Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from inspect import getmembers from time import sleep # Local imports. from common import TestCase class TestMlab(TestCase): def test(self): self.main() def do(self): ############################################################ # Imports. from mayavi import mlab ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() ############################################################ # run all the "test_foobar" functions in the mlab module. for name, func in getmembers(mlab): if not callable(func) or not name[:4] in ('test', 'Test'): continue mlab.clf() func() # Mayavi has become too fast: the operator cannot see if the # Test function was succesful. sleep(0.1) ############################################################ # Test some specific corner-cases import numpy x, y, z = numpy.mgrid[1:10, 1:10, 1:10] u, v, w = numpy.mgrid[1:10, 1:10, 1:10] s = numpy.sqrt(u**2 + v**2) mlab.clf() # Test the extra argument "scalars" mlab.quiver3d(x, y, z, u, v, w, scalars=s) # Test surf with strange-shaped inputs X, Y = numpy.ogrid[-10:10, -10:10] Z = X**2 + Y**2 mlab.surf(X, Y, Z) mlab.surf(X.ravel(), Y.ravel(), Z) x, y, z = numpy.mgrid[-10:10, -10:10, -3:2] mlab.flow(x, y, z) # Test glyphs with number-only coordinnates mlab.points3d(0, 0, 0, resolution=50) if __name__ == "__main__": t = TestMlab() t.test() mayavi-4.1.0/integrationtests/mayavi/test_hide_show.py0000644000175100001440000001112711674464502024254 0ustar ischnellusers00000000000000"""Test for the hide-show feature. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Local imports. from common import TestCase, get_example_data class TestHideShow(TestCase): def check(self, saved=False): """Does the checking, if saved is True it does not change the properties at first to see how those behave and only tests the final unpickled state.""" script = self.script e = script.engine scene = e.current_scene wrl = scene.children[0] src = scene.children[1] mm = src.children[0] scp = mm.children[0] iso = mm.children[1] if not saved: assert scp.actor.actor.visibility == True assert scp.implicit_plane.widget.enabled == True for a in wrl.actors: assert a.visibility == True assert iso.actor.actor.visibility == True # Check if widget state is remembered. scp.implicit_plane.widget.enabled = False scp.visible = False assert scp.actor.actor.visibility == False assert scp.implicit_plane.widget.enabled == False assert scp.name == 'ScalarCutPlane [Hidden]' # Reenable it and check widget state. scp.visible = True assert scp.actor.actor.visibility == True assert scp.implicit_plane.widget.enabled == False # Reset the visible state. wrl.visible = False scp.visible = False iso.visible = False # Check final state. for a in wrl.actors: assert a.visibility == False assert wrl.name.find('[Hidden]') > -1 assert scp.actor.actor.visibility == False assert scp.implicit_plane.widget.enabled == False assert scp.name == 'ScalarCutPlane [Hidden]' assert iso.name == 'IsoSurface [Hidden]' assert iso.actor.actor.visibility == False def test(self): self.main() def do(self): ############################################################ # Imports. from mayavi.sources.api import VTKXMLFileReader,\ VRMLImporter from mayavi.modules.api import ScalarCutPlane,\ IsoSurface ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() script = mayavi = self.script # Read a VRML file. w = VRMLImporter() w.initialize(get_example_data('room_vis.wrl')) script.add_source(w) # Read a VTK data file. r = VTKXMLFileReader() r.initialize(get_example_data('fire_ug.vtu')) script.add_source(r) # Create the modules. scp = ScalarCutPlane() script.add_module(scp) iso = IsoSurface() script.add_module(iso) # Check. self.check(saved=False) ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene s.scene.isometric_view() # Now do the check. self.check(saved=True) ############################################################ # Test if the Mayavi2 visualization can be deep-copied. # Pop the source object. sources = s.children s.children = [] # Add it back to see if that works without error. s.children.extend(sources) # Now do the check. s.scene.isometric_view() self.check(saved=True) # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. sources1 = copy.deepcopy(sources) s.children[:] = sources1 # Now do the check. s.scene.isometric_view() self.check(saved=True) # If we have come this far, we are golden! if __name__ == "__main__": t = TestHideShow() t.test() mayavi-4.1.0/integrationtests/mayavi/test_image_data_probe.py0000644000175100001440000001003011674464502025535 0ustar ischnellusers00000000000000"""Simple test for the ImageDataProbe filter. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Local imports. from common import TestCase, get_example_data class TestImageDataProbe(TestCase): def check(self, saved=False): """Does the checking, if saved is True it does not change the properties at first to see how those behave and only tests the final unpickled state.""" script = self.script e = script.engine scene = e.current_scene src = scene.children[0] idp = src.children[0] mm = idp.children[0] if not saved: assert src.outputs[0].is_a('vtkUnstructuredGrid') assert idp.outputs[0].is_a('vtkImageData') sc = idp.outputs[0].point_data.scalars assert sc.name == 't' assert mm.scalar_lut_manager.data_name == 't' assert abs(sc.range[0]) < 1.0 assert abs(sc.range[1] - 626.0) < 1.0 idp.rescale_scalars = True idp.dimensions = (41, 19, 19) sc = idp.outputs[0].point_data.scalars assert sc.name == idp.rescaled_scalar_name assert mm.scalar_lut_manager.data_name == idp.rescaled_scalar_name assert abs(sc.range[0]) < 1e-2 assert abs(sc.range[1] - 65535.0) < 1.e-2 assert (idp.outputs[0].dimensions == (41, 19, 19)).all() def test(self): self.main() def do(self): ############################################################ # Imports. from mayavi.filters.image_data_probe import ImageDataProbe from mayavi.modules.api import ContourGridPlane from mayavi.sources.vtk_xml_file_reader import VTKXMLFileReader ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() script = mayavi = self.script # Read a VTK (old style) data file. r = VTKXMLFileReader() r.initialize(get_example_data('fire_ug.vtu')) script.add_source(r) # Create the filters. idp = ImageDataProbe() script.add_filter(idp) cgp = ContourGridPlane(enable_contours=False) script.add_module(cgp) cgp.grid_plane.axis = 'z' cgp.grid_plane.position = 2 s.scene.isometric_view() # Check. self.check(saved=False) ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene s.scene.isometric_view() # Now do the check. self.check(saved=True) ############################################################ # Test if the Mayavi2 visualization can be deep-copied. # Pop the source object. source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) # Now do the check. s.scene.isometric_view() self.check(saved=True) # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 # Now do the check. s.scene.isometric_view() self.check(saved=True) # If we have come this far, we are golden! if __name__ == "__main__": t = TestImageDataProbe() t.test() mayavi-4.1.0/integrationtests/mayavi/test_labels.py0000644000175100001440000000762211674464502023552 0ustar ischnellusers00000000000000"""Simple test for the ImageDataProbe filter. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Local imports. from common import TestCase, get_example_data class TestLabels(TestCase): def check(self, saved=False): """Does the checking, if saved is True it does not change the properties at first to see how those behave and only tests the final unpickled state.""" script = self.script e = script.engine scene = e.current_scene src = scene.children[0] mm = src.children[0] l = mm.children[1] if not saved: np = l.visible_points.outputs[0].number_of_points assert np < 35 and np > 20 l.visible_points.enabled = True l.mapper.label_mode = 'label_scalars' l.label_format = '%.1f' l.number_of_labels = 45 l.property.color = (0,0,0) l.property.italic = False np = l.visible_points.outputs[0].number_of_points assert np < 60 and np > 35 assert l.visible_points.enabled == True assert l.visible_points.outputs[0] == \ l.visible_points.filter.filter.output assert l.property.color == (0,0,0) assert l.property.italic == False assert l.mapper.label_mode == 'label_scalars' assert l.label_format == '%.1f' def test(self): self.main() def do(self): ############################################################ # Imports. from mayavi.modules.api import ScalarCutPlane from mayavi.modules.labels import Labels from mayavi.sources.vtk_xml_file_reader import VTKXMLFileReader ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() script = mayavi = self.script # Read a VTK (old style) data file. r = VTKXMLFileReader() r.initialize(get_example_data('fire_ug.vtu')) script.add_source(r) # Create the filters. cp = ScalarCutPlane() script.add_module(cp) l = Labels(object=cp) script.add_module(l) s.scene.isometric_view() self.check(saved=False) ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene s.scene.isometric_view() # Check. # Now do the check. self.check(saved=True) ############################################################ # Test if the Mayavi2 visualization can be deep-copied. # Pop the source object. source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) # Now do the check. s.scene.isometric_view() self.check(saved=True) # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 # Now do the check. s.scene.isometric_view() self.check(saved=True) # If we have come this far, we are golden! if __name__ == "__main__": t = TestLabels() t.test() mayavi-4.1.0/integrationtests/mayavi/test_plot3d_mb_reader.py0000644000175100001440000000561211674464502025512 0ustar ischnellusers00000000000000"""Test for the PLOT3D reader and its multiblock capabilities. This also tests the SelectOutput filter. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy # Local imports. from common import TestCase, get_example_data class TestPLOT3DReader(TestCase): def test(self): self.main() def do(self): ############################################################ # Imports. script = self.script from mayavi.sources.plot3d_reader import PLOT3DReader from mayavi.filters.select_output import SelectOutput from mayavi.modules.outline import Outline ############################################################ # Create a new scene and set up the visualization. s = self.new_scene() # Read the multi-block plot3d file. r = PLOT3DReader() r.reader.set(has_byte_count=True, multi_grid=True, byte_order='little_endian') r.initialize(get_example_data('tiny.xyz'), get_example_data('tiny.q'), configure=False) script.add_source(r) # Add the filter. f = SelectOutput() script.add_filter(f) # Create an outline for the data. o = Outline() script.add_module(o) # Check the bounds of the outline. assert o.outline_filter.output.bounds == (1.0, 2.0, 1.0, 2.0, 1.0, 2.0) # Copy the reader to see if it does not pop up the UI. r1 = copy.deepcopy(r) script.add_source(r1) s.render() o1 = r1.children[0].children[0].children[0] assert o1.outline_filter.output.bounds == (1.0, 2.0, 1.0, 2.0, 1.0, 2.0) r1.children[0].output_index = 1 assert o1.outline_filter.output.bounds == (2.0, 3.0, 1.0, 2.0, 1.0, 2.0) ############################################################ # Test if saving a visualization and restoring it works. # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. script.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine = script.engine engine.close_scene(s) # Load visualization script.load_visualization(f) s = engine.current_scene o = s.children[0].children[0].children[0].children[0] o1 = s.children[1].children[0].children[0].children[0] assert o.outline_filter.output.bounds == (1.0, 2.0, 1.0, 2.0, 1.0, 2.0) assert o1.outline_filter.output.bounds == (2.0, 3.0, 1.0, 2.0, 1.0, 2.0) # If we have come this far, we are golden! return if __name__ == "__main__": t = TestPLOT3DReader() t.test() mayavi-4.1.0/LICENSE.txt0000644000175100001440000000312011674464502015613 0ustar ischnellusers00000000000000This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative. Copyright (c) 2006, Enthought, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Enthought, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mayavi-4.1.0/image_LICENSE_Eclipse.txt0000644000175100001440000002604311674464502020432 0ustar ischnellusers00000000000000Eclipse Public License - v 1.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENTS ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i)changes to the Program, and ii)additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributors behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipients responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributors responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipients patent(s), then such Recipients rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipients rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipients rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipients obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. mayavi-4.1.0/LICENSE_YORICK.txt0000644000175100001440000000445011674464502016702 0ustar ischnellusers00000000000000BSD-style license for gist/yorick colormaps. Copyright: Copyright (c) 1996. The Regents of the University of California. All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose without fee is hereby granted, provided that this entire notice is included in all copies of any software which is or includes a copy or modification of this software and in all copies of the supporting documentation for such software. This work was produced at the University of California, Lawrence Livermore National Laboratory under contract no. W-7405-ENG-48 between the U.S. Department of Energy and The Regents of the University of California for the operation of UC LLNL. DISCLAIMER This software was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the University of California nor any of their employees, makes any warranty, express or implied, or assumes any liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately-owned rights. Reference herein to any specific commercial products, process, or service by trade name, trademark, manufacturer, or otherwise, does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or the University of California. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or the University of California, and shall not be used for advertising or product endorsement purposes. AUTHOR David H. Munro wrote Yorick and Gist. Berkeley Yacc (byacc) generated the Yorick parser. The routines in Math are from LAPACK and FFTPACK; MathC contains C translations by David H. Munro. The algorithms for Yorick's random number generator and several special functions in Yorick/include were taken from Numerical Recipes by Press, et. al., although the Yorick implementations are unrelated to those in Numerical Recipes. A small amount of code in Gist was adapted from the X11R4 release, copyright M.I.T. -- the complete copyright notice may be found in the (unused) file Gist/host.c. mayavi-4.1.0/artwork/0000755000175100001440000000000011674464502015465 5ustar ischnellusers00000000000000mayavi-4.1.0/artwork/dataset_diagram/0000755000175100001440000000000011674464502020576 5ustar ischnellusers00000000000000mayavi-4.1.0/artwork/dataset_diagram/structured_points_connected.jpg0000644000175100001440000005060511674464502027130 0ustar ischnellusers00000000000000JFIFHHC  !"$"$ ^< Q !1A"2Qa#3BRq$Cb47HSru 6DETcsvFG?ܺR)JR)JRw˜+-ub I6)Z5W[u+$$gZs4!7ڥڏCCB=]Ӝg^W,`9[^|-' ^@|HjLY-%iAHq JGAJR)JR)JR)JR)JU׮?"5)$[dBj%V\yڶN1!+Q}*!ik?q %I8HcB!:hVM\V?rnȤq|CDϚҔ)JR)JR)J:,tȮ[XX>u:OFuWәs!]k>#1=Gq1n۷CqkCӿ $k$ ۬y۱Z̾?p]cFk~1kɹd)ۭ%yOw: νN)JR{RьE'c`4G+;%(1!!)M0 =x)Q\23&m_[2T+`%%[a 1O$4SJ<썣Ǎ TiC2c5  ɜwQIYPHPnqb+ԝJRx,`%yA?ԯdGT>m")P~vޜ_k;{;lRS8Ғ$+ y.}im:ڊmi)RT WzWAζ4&|in<ﺖ$[AHBJIGR)JR`sL-ߏʲ }%9!y$U-Ǭy_:[ \" e'-G: ˃w_f$zn ;DdT,v"Dh4-6Rp+VuSOJ^W Wzw*0 έ:ޭVյmyq B}WrΘe1|N<\>?!GkW jyL=VJI mCT>YzR[N+9A/65S:{-|#ҕͳ[ r1\w1Nԭ[^g_ 0;"͠[=AzecMH_W˷kD'}@'x*t鯆@SR]i:!#+MoӞ;&#SM%1\BVvWۿ$ lb M˘*;aq&sTO+jS:nMc}LO-t~/O'ݯJԚJRFa نA)d:I[k}H*V7u5CL/IuM*7VHRBAlPnoo֗rYn &DF7U8,6}<9jT?-[ c[.&sL^(>MJRκ~FQIJ2 uZt@.exlcJJ (>! x'vF1dy_ 5<*07͏᪲a3O}aB/7Q:K^?DO<E?L?//G| ;P =8űI+DüȺp-}aJJo-u h(?t2!-1[HW%Ylm ߝ ޢV#qOe$=f{jPLnqvJ RbCiq|I<~6JJRVQ=K/:*LF;C(% y&UYԼ8@fXbսٲYR%[(Γϡ7~؂ zZ2Cu[Rꤔ)JUY}>pmQ̫Nb2jɕZWl@j[)Cezm=4jO+.fQηߡ#o z<9^W# Y={t^vuU}TfXh~0p28xosXil Ros.l0Fx-m[)~mx#5/w:b.>DDžoJ䟧p)JRj8TP%~&Kg:Ri!AE\ʱ|s*%v\]ƺajbG>̄C=QqHCV&w[G-xD78~9>@?63\1.0;Ѥ¹};I#d}AץX4)JUYM] p?, ^t$!<}*JRRqfbmjy[V}6MͯuE<=-)BPS{xFxGGi50`)BMJC2[OU::!)BSvA#*۪=zM9rڻC|-+fP[Sn!+BJ|*޺w) M˓yːI-W!22.y։(u3n$A)JT aKͨɴC9 7)=j*gnVM~-{!^cls>_̂ v4qLqE댰|!i@$$d1S1nqLNqCi~,k]QG XXGOlAKk]~q\!]*_bOrLSdvZup+~C1 .rh;^G6|ƫ4z b]TLjm?NIᵑP6NK$rӷ1QrV51PgKG$M `0ַܨs AnLW8КRέYJ6Q{1‘d:RNeԫ?q 2/jmGxB44k37f͂>#X>Fߚ%7l[bOUͳWI2 T cHr:TN8RTG*jUѮ6k$u7Y.NS!Cĭ{ lkUJ}t#KR[Z\@椔* ஛2Rm0o$ymx;XUsf/]!9d!e$qxqrC19".C<9 5v<c -(:P=Ef."H9G<:KhH%J'&?k3ˬ!͗jǖ M[w7xQ>N#^G@ ^,ܨLMRXO_?( k[Gb+(}/IjkIH>yK[`K*0 W rmI̋C~))UgY0/+ݳV+z[oVmxCxi_l- G\~:c7*S9[8ֶ ˲}M2 _-{d0姛Vmc[~ٳ?rgdx+BB\#_ 'z< ?M'IG= Q|Nqc8xm;p/KD}+7y `_A Į|~c'Ia9ۿZڹuiμZԳ$vX{M6gtA._nmͥ<-J[VOORz}}~|_)JU9e6dYToLm<9m] % :<"~|S6ldʐyVTNʔO$\[5%!qDy/0|I;^;-Fu ;x-خ+N>HF%IA)AZr.po:i6W^/ *ՂcfZ#G2Wsn4` fJRQ?]t h|ή"V)JzWE(PJO>[VՖ[r;uwy!7W 돘*O}on'z"aD*Ҏ;X JAII ֣{.9w"OWRwo`5R[&b% $bC*rC_q>J?5oY7A2xIW G ~7P~DW\ul\׆V#Jp~yqT;p{-W?h}UJR6nYK,-r|i wd>Dl}e)J-ݽsH;:Yqknڮ k(T*şz;rj"VV݂_Ġ}'Jpdȓ)aj*JN\/+/rEu-HZKZGxK<Ӽ& dYnDS*) 葽V^F9}\fe]娮f8{DN1 ѕYt.IC=q'' f)JUY'=~>?n0Y@/:RPl|eM2V1bƈ#Vu5Z#dR28SC]%zW-`(Hg{4ʽ1ԟ.D܊RKh~'qޛXkorsPus|6 pMiJR lT^U6k׻|VR'.]*˭̤)V9 05R6Z&wy#BΔNco~doM# ʈ~fseqW3O:?cH5'frdw\ȑ\u7$ǒ=~n/aΜfE&2_ZTkJ@pAVȩ`3OW]ӑ |) xN5X{n bde|2" A*Fԅ.0 lv!ԏڂe^WRezǖ*46 vua73aco^X!ҰuG *gJUY'=͂?h?V)_v}P- lOV|6LBnܖݲ*{My{ߡ6:tćFM{Y y,?-pFh ֔u.$Hv"eD^)-!攲ANގHN[>>9q$XmS)-_qN~4%,A!]Qya㷻NChfd1> mʶY RV +KMۤqϟl)] c.uտ6Bhl @ je+N*CoE#W pIp[[ . O\b"VKڭ-|mGqQz5u:K3}~O%IFTqBrbgjYk;_C8É C)P#{hg>8Q.7ճvtUPT19 Y&bxR>tȨ/6WfwAib )2/lGxR#hmbٔ½9SknG8"v\XzdPQftxiS'ՓJ\ù߷b.$e!i>AVIWd;$%=2ԑJu К;EhPt$zhegL{&<!Z!{),񽝀>"k O:yغif]%mm (4R{}+gm74ο36zHXH>%S+Nkb FؿN]eʄ2O<R;>+IJm TTuͺ<la$}t{\Z%J ? UJV 4,}[QmamEGpx[kC?*5L7oY*z"Gm\>-^ͼÁhXkJ{$ u"efmuQꁵ^4yX6d LHI*G Ay()z+ R֔!%JQl2CǬӤ2wv2!(qgliIEl)JUYL簇\P}F=O:UYphሙɖIVEn~so^@RVo+ h%%ٶGN;}1 -XRTWٍ^m[IRqA)HI'+G/iBVതRy<[]?[7+X\m {HWopvA#r-ջdiLР#7v_oē(-] ;|[Ƭ86zyCQQMeJUO䴾Jmn V)JZR) HRT4A}*aNsΙ:(YR~>+=g|m}b{]z-E`$W.yWБϚz˱'Lj?PXnYHgTu&|_6|oZݪP['zRH$P]õ"KG Sܖ[IA- VUq{=WsA:qá]㭈7Ҧ@)]? k=Ckq+) /p|%nF5ϒٟmπӌ8?BF7K~ órl)-E}~tvY6 c!aahX85ڥ*GXm?r)?s65j)JUqo[ewffdaO[EQnC/a*hnK{ww̷%ϖ{ά쓭>{[G`P`Q5U)1qPPmD@gJzٵRaW5|Icӭh$µV6mym0)qYp=[*z+qA>G1du ^Y=zֶˆ=RΙ~O]? ~Seq@ sV*蜆#gL𿙩owJOOUP Vh7U9YnD$(r<)'T A,޻cO72!<Ƽ~mn)+BV%C`GֿjF |(mH?ǟZV}:p9=[q!q!A{ʂQYS ln6ed "2R[T8?GDԊ" ,4 ?qigJ:gsaޘ- {b p 57M=sDI!/<A OZ)/ܸ^Si|V(pjӥ)Q2p)|!njDl|K$qk?ayv)bј孋|r{ [J?rMW6;M@mCԥ|_ Sw[ tÜS)g<-RT>wTR>i-XUCL۳{d~V WjԂ "φ)ړ:Ө B|Uj=&p˹j;qNe\p5Ppv1qLbd7,- PGf'ڽ'D)./ӹi?9P1|o|lӊ6bBIe (?Sg, BMښaR$l-P^u% i>!#߼J(BGq$n}6ˮ8>enȭY1^ yԾ߅rFtN+ѻ5% IېskHRNp\&Dzl,ŊJyՄ V.rtިJ\L1:/+Nw+=͏Us_Wy~p'%:ft-RQ'w(EcmϏ}5['JRNh]jwЈHLOYmѭ-!CG~E}ҕ x ֵ{Adr;PW0-AZM Z kdY-@K{Hv|Z"bd=Eq&?! >֗^^Ԥr~E&Tf;>tQu`ҪθLVUgէJR-c˭~LwDžc}EBXȯ8ysf獨yA.0'η\GQ\Ű9N#ܐmт˭ohN~*o3s!(q3|!jiGJt>kpբ6l6Rd8 $<%x vF#|zeu68-DGmR[! % G#b~Ff{ ;d lxn,D;yWt5Mfr -;Kvj#xsҬKm(BJR<+ꔨWOw~\nz%UNuT)P~f+JcZRJf;p-5*QSԚʲ[-kU!ENעyR%ϳb¿v`C>1)v6QS&\y(Q*RIF_fU|w2WReJpqG$u*נcrk$?gjl'ۃyr*۷^1E"%=gIj^<^ja0:Yq!m%I#`<=j8tyQ UiY'Zt*%u ğf)קޥv-H?dy/ͺ_z2G'~ GBY&6oaй'ץ0yʊvڶ5+X:l9D3f5Kɷ2%]JhrO$oU=״nIlFKvi-Se`~XOnty|9NΨܔ v4&⤪{ZHH^{zw۱)҄;N+gzV)JT37kk{0\.='ލWjN9)JBYۃyjOE[($TO+ZzK=O!} h+)m)*T{;8I?@W[ep_; ?NO\*͉*Z+dwmx%[t eq^D^G%rAK_#@cSڎu ۖ,:݌qv+GGDhkUgv-V9!A*$q[ ;nw쑇.oyǐ~ wMpLޤH^ɠ$~6 ұ<ʏҼoGҕ-HaiJmi JF zUd MƣzÊlڤہ;*IhU|s3`GKR;JycDG:Q?ӥb2ڕtȮm[QԤrP/#S%?O ?m|-p\k adD'\dWq\p6N5P^Һaof,+%=ǎ) Vk֙W-e(%@ڔO<f ǽJ5nRx6$yre NMd~zP$RG!I#A"ifEL$IHuJ>a⬛tw(,Ϸf\G̬- IA]RΩO[:t/ڢ $z )JR/Y[K[pS^ݴ4PVI'd%5P6~ar\C77 ;{? PPJI?Q[8zW)UgHԴ+o5_ s:ig;-lx}V5BsȌ^>;V'fļxdb4@!ɮȝq1DvVS@#gRSS:퇧V][s?Mt|W^?J'>ꬢjCqВ(ACD2+<>9g>4|+A2*JE)(;hRh6UkqoT篝6KKӱ^pOp*SfVL q#/̅!C6u 3<7\ }rn2 [@mX(v&AnxO$[Wod%! ))JU_Y]C@>0էJeݛ)9jBDO icС=桖\cʷjّ) %xKRu;¯<ՕJUY?ʥ=.o8YDLgWlKe6KB=8 J ]{\k\l'{~w&Fθ\mn)niŭ)wj$|jn XDh4 $!@JP8\3(H[ ď[L{oH_3S]lKqd}S㓿׮ƲZmKsVD!4c޻VuۦyI=4?ӥ)Jg8${Xrxs!']">q{ݭ: h b!*CjT4\H=)(;P)JR]UlQɖD I>?`)>)Nұ̆eV?o*VXʵ2zRjSлc9 - 'Z)!@AG~ҺHVCm]OrVURo},q1 "z+hIA9H2,%nLW}Rۭ)+IAZo-L` q3uÝ j䄏M U&FzQxb=*SڕG'aC[ޫ[ݸkV鋜AT] ۭ[I콏5{U$Z/Y )EcBVc~@Wj:h.5hr~GBo: HTO&ӹP I1Li s$`PrWxi'?̛?u\4>(*uPN'ĚJR8sz۹A>+JIObue:pzrO)kWR'@fep Ֆٔ?ox,WI%?mr+,V$-y 8"\1xv8}tEdJRgTY!V̂KI>Ryh GU1M73ݟ,p1=DeGiA nn:+(mGmád"]PvN)sJW˨C`IPP>ATߘZd!$9:JwW}v{yФ\,S]<,-$\^#G>H#]F:X(|ޥV̻OwK|w(? t>\E 4$4ڈێh@sl7P"0m~TQl߮ ]Λp}M~^aliى{|8- 8* AV@:a0:Yq!m%I#`<=kUBhN4ĸxxB ?*ӥ)JUu,}Uç6; -3&TkCd9QӼM&_b$!*1 w4P҇oDdz#+TV S.K ?ݒVA'VH ꍎ ef3q4 =!)N<w)JRF:YܽyQDJ?QCU]z b5)*5ѭo;#iKj%V#y{KIP -@K*P+OKq9()nYadzI_RP-j JF'@V^ȼOvJ2y*s _t~k{$z%,uc"ko̫~?,k[$x5| m& ŧqg8G/%n' ʹ\3ih~n3m~z[PPϠcgv+w.]Φb:{BtcFAַFKLn-SK?JtѤ-a[ 3qOcL0BHW;C`IPP>AXht.ݰU"'jr!ĞI-| vjl]Y :Ҷ> RV^?QأKA8l էJRvOb*_ Cj[>j?@ /T WpnoO9)$ }5Ȋ뽯G¬ X{S-%KW-GI +eEC-|>ZjǏVa %# )JRGzXs}vke_~+u(Xv Q!sTQfn`fԍrQ\Q_OrHLӡKJXHDN7 K  :dǧ" W}͢؏zӹDhlH2iے"w9h}H`:;Slynj d(2;[b;a }8ڭ\[X*z#mv?A*j4f\yVmKQ: rIx"}8 wo+.+2>ZEgi?GgH>)JR)Jxŏ0eX2 ͷ- $VRy  ם]bT^fr&mn;Izz7hm!Ivx몹 )^0_r>} [޼Fg\E#^K z%*VXHPPފ0;à&?lj t>*u~v>O)Qb6^q \iߨ?JQn"nŹOfԕ6|!!I py٫/ 6[&'Pag˅Jj|[JRpfS,lYZR)JR)JW?+̯ \b-CS> 5od轭 k㒣lˇZ:;ե)J=x*?*s;iQk:R+ܲֈK䰰)ѕ"Í֬iL܁?Kro{?$oZJ_92e?:e ȑ!}^Xp;A{{ࡽ$9van$e{-|O؍ u=)JR)JR*G W͵ޖs`).;ҠR47sB:{oX.Y㒻Af3o]{u"]㣯<7~ZnRrMg}@VƍLy 6\H[n!AIRH Z)UgÌN޵}uէJR+/hh3kʙ>ysݦm'ԁM}*[Z-{0c&'r-R,}~θR)JR)JR|o4m.6  Be8vhKzzB ݱv\k\>(#2- jLWiܕ5JUY9t/o::Rto׋]><L'ǞXJGOgS#q5jlz| :دޥ&.7 \%'Rn)YJTBv 㡮*{Oy7)iqc*Q㍫_Sw+ŬV!mŬ )utTvrTJ*Q[.)+WK)!CT=A沵V{P㦭:R~Saĭf {[j[J>%+-`x{qWez)FA6 늺0SlmYqLkle<^ZGDYR)JR)JR)JR*vty=vL,w{oD>W< IJ+vMhMާڲ:ǣ:ۉ<@EA=9!\6+R@ȫN ZNZ37qۙB%^$Z}rFz9#'%;f x=c>R=;yY)JR)JR)JR)JRW[\\خ˒nA,I!(X$4s=js1={BR]#C*wi =.CG.6D]t-"pkXBŠ''葡g`l֫eͲoo{Z% ]R)JR)JR)JR)JR%pawmx_RCcwҼu h(T|鯽z" `o# ޑlPOR)JR)JR)JR)JRnSbdL}0jRA JЎ)ް,ʳdTж_liJBRE|=*]_w9q%t8ZmujXNF;x:;VR)JR)JRmayavi-4.1.0/artwork/dataset_diagram/unstructured_points_vector_data.jpg0000644000175100001440000004335011674464502030023 0ustar ischnellusers00000000000000JFIFHHC  !"$"$CW" K!1AQa"q2BR#3b$Cr%4S56DcTt@!1AQaq"2R#34Br$bC ?Z" """ """ """ """ """ ""ۮ/K7b`)(dʥ{/}[]=|b7q49{ۧ%ϦF}^t}4b98[3}wΪekծn8#C=q䵑^\N+و5tl8DDD@DDD@DDD@DDD@DDD@DDD@DDG[& {ih)Q.9yaH/=6u{z6IRH5C-N 3B׫꩹ *^S{<[v:vjkM{9,-9]{q'9yHn]cwqc?O\V7݇pɘnFnfϢo4so_${""" """ """ """ {ź*(%UD!:0yq~3K'Zj1\[܏c'gp96q*Dn2.s/|NP$Eum =u]=,_i+e)(Pw\Gє,})xis?5Sh=\6 <^,aCߪ7]˂6/<{h!ͣeOtp{H?)dkdMc?u <as흥 r8JXYci7ŞEW_a'".@dy ү֣gNGYϱ[!ГDE(""WZvSe█n=N@Apkxxi:5t}1ܗe|ɫSxh!7.BOEwMM*4]vfWG;ţ9=wTD:Vz#[Nj1Z>V׬W˥wSG6@rjz >>̟]JUmk{3d19|r&{IoqVULnr:Gw''̕X:4 t|#9m./ڭr~ViJ0pfOI SBFp،}qPv}7ECIGdt`wVuwe\2 _qs6Sc:6]C>=V&ʄo!GMX%:9X{@P|B=d{TTAys M]CqҽdN[vtda~HSc0рenW^m݉gep{62=<_%kP\~F:Iv+{Rxc[ ƻIZj媨1.ll#8,9dU5ԖzfSEoxpG]>->EnEkDwu,qA^-@q A{~|v;jrՓ7 2Q,qaQ=RSܹ}9Eָeǀ|9zyWSN)OsLx>^"E""@" """ _`j,~!I?>˚`s.i[EN3ǨqsN*U^q^%Ƹ9 }Y" "! I\NtϤkpYL!aK坛Wf~;)D-kqYO$-T(\ۓɈh*K{/W5 +&;Zwi:V8EUQVՆиʯkoڑ{EڗKĸ"ש@=|4;?7>5pj*6`ˇZjYRܦyU5{XpyFX4wԱ9kĕuџ~cǀ澷Cj Ue%7j8p'dc>+q_KYK^I?*~f߯I߭CpZta{i1<֗{$Qꙸtc2}WteV5`o1lUf$)g{72e:؎8%^|iE}] 2S^S=Fi>(i*IFo+$" 낡Kf}BCFIdp#U;WmU l&i>Ds:R:rN9!~^;[^&IFGEe.}ED\NI$֏[~)7sROcZ ctq7k2?+~1?n̪qsMiiU? uS>G:6?]fT>k>FEoWSF\ 0$HWR^5V_F:Jo1|DEt~mІw\1 C~G!S7%[Ʊ7N >nGqNn097qҚ^_uA|5k󃃝 mUv1' H1*sfSU>2@Bbo -+۹u&i!4Lhk90n7;um4I(46? $v`sVcؕTnJYkxs7~*kAvmYQml|sFs(zn6VۓU=6+|H[wfʥmhǫ,{B8nzi) ǎVE7X.{=!J?mMU\pǹ#䬶?b=٭4`I;Z{Yu0R@elQdWknu,GIm,>akoiw$fӷO܊^Z& R?v粀:he];B# <'T]_3 {êOIfcy=MTOyamM_a!Y27Lv5+qFNi'#%{U^UY]LOns6>-jt7?gW]ǡ.*۝#J=VWoQ}ܽ{{KDSs (Wt#VꉢL2IT˵zQ)hG p8I=}aRI1k^QVjyK /H2" .[ğ:8{T%VVSO~6iVku\Uln6!;PmmKZUK`{ >{2C 8k@;#qFOy*;K;'K//ߢCձ7i1޸~`}{"L`\K̕t[HhAv/ng9ɄDY""" """ <10+!%]?X;=v$^+vzSt֧ URw27]vuEUL[KpH#8=Ksk{'Vu+SkRZZX<ϭ֝9q8uH Ia9|}ĴbZZ#W;bsn:gݺjKt.,a+ߎyMŔ%kD[tPZl'Iԣq{F\|VC}p?cdGZ8|w.Ԯ.=K,hy9ť xf`k7{?̧*&M<68-.> ]UsQW[4ZvzUf34xGVݪgp: u>!tA<},ns$2y1l覩T%Ӗ4 2?E¢h䨝F ՗Y-L*3yhEZKF\;iXKsTN8icw8w3RH+,nF1<<ƓX`h9MTtwyL\Fy춭TR x6!jөj)$Դ!KÝۜp9sڭ 7@&R_/mjRr鬩.LN֌\+kN[Zeo3|GDKURw$}z =FLC ~ɥ[2zϧE~Ogh#c8dzy-d0鋬)=+\a{i.75S#ZutUTNdy~ ,!v,'cUBO>k$ȱ(4fjZ=f;D@ Jj5&r\49`V`>q੪;=/Z ROzKuu ->\l'lco8ܼlkI\nJo; xrk?4Gg?V;OmB6ʜXMmyBx#\ukNDDD@DDD@;c.ҷjT|j㺔- ek#vG,J;oG|ՊoZm6Zzr4v=w<oI&lgW pB0z/OtÏs!%^+Ks5QX5 u$)T>ae}:8 )lg̨KEeq2M!$w;a6""G =CAf;nY浖}L4v782i3R@.1πDS+Nw >J_`vCNet5$} ]5Z/j?α8> ںRzZLW8 | ̯q]Z|1hjۯrZG^;Bv `yUo6RZ:ߌ>ɍ^?M *wqkH遒?5LU 0Oi4A} % q˞W-!w҉襐"DĞX;gledֹk,5׶FU 4E'5.gApGr[ &npr 5422~C[GI!(s\8DG:jm7}1|KOChi %7""K},tQiCvڊCIx֖lxyG}K{Ֆ(4xW j)8\c n3+w'[c$7g8LixQb)4iЯS&L4}m? F+,dD[DDD@DDD@Tִxպm \ | dX?7 zMӨjWe3G7|f9bv)D淂wZۭ-08a@mwWd#i꧌ON̵y̑#EEtz.pWQ+OMzmwgWu/1)b5u7: V8H63Ԭdi< ֪{rcG_o-æ8y<얞vf]U4UakxrH`efX;HCݧDpZ##Vk*- kĎvZǑ{ڽ ti: +9vyd32 dnZ%h>Y gݜczxy#dSݎnKYѸ[9|QY(.2=$pk p䪺­gsDiݱgߗ*iz("@ѝd#ߒ_ֱc֌5`ྠGNg1$7֗8'/Ka{:Ao_[av^K< a8blQ01(YjmU@@/s88*z1NTN/(:Bj:"pe.bNmF[5l7n֚zNҰޭwP|0VF;#+}aCt;xxʺh/Z$w 5ff<8ٛiz]:?[3XZ[}A8uLkͧ>NͬiqSwW;D7 rNY.!K1i |mYx#9쪴m ݸI1%iZaMj2iYOވhⲌ$2z]9ﵹ|ENo}È{V;펂fg~X o"w*M׌]sػ* >M*R∇a I-s{I !YhU=>Jj$N=_}}z7C )tTCH֙g؉ϙhuuENѻ|XS4ͩNc?35ʖ_I}PG?&-DDҭ[ilќ O,Ct-e3ndpC?5~燌 }pw[GCڣQ:U5r,\y?qox,KC&xd,qi1꫚UP\aj}?`\QkSd)"ɧQ4sM4{KǨ)UhOia}L,r7Wѻ]i¿e^~`BJx9"¢&콥oi.G<;meW躎<ʖ l.h ]eugiO3tsp*&IZG )Yo<8 "hl; X裙R-s9Gsᒹh9Ec>DbcO;*=hj*[Q)ݱ>.qlv܆owF2vUkE$jw{Q=<6Re2风0BƆ4j}6W<ݧ`KpqBaaĵpH :ZzEGJpd3WsqDad '9+1DDD@k֪/SP[LKą v}PvCEStEҚGc RA_E5K╥-?V=.9aC\k4Шtxvwu| h㣍,s^ƽ-p>JFQ1.cØT |CG{OCJTW5q xϠ\E+]V>++>[͕SlD_Kp" 6~wv"{:.wYd{ӸiӇl,"Y M c@sDDA%$ԏ3[YăЩ@EUSFe"zgWD7pG^il ^⨺:v[s&31}:wgEll0#{Cg3GOej fNN7.r.Iq6r A(AD@DDbݭwZW١6r>#TTp|QmR}vXcj8Y赬ZӎW;w#==-S7?㿴QM%9jz*B6iJާ^|9déBtW}٣Ԝ#kc=c ִdpS_LTgzoPeɔ\qC sb&'G iy[2q[&2(mcz*O0ϻ[uhA!muh <sy}d.n_OTڏ*A6tC}Vt1 m&1 UCMRxn4Q)ý_H" %*IK!FK58 !q(挲V5< .}9}ǗT(Mں,jzqA+dGq@v"uou-Hp)@wGU$z0MӨ^tZZ iռ0WD3C5^ѕrU1R;7ĶH*S_ۙq|MmY[8dgu"zzZ'nSwvUZq@"+uU5;>~KF7#֖kFԌTOoFj"<#$ |O,pDew ̬-I%ewN/m{^.IE=J֝iɴ麚g) @z-ȪUXlK}ED;+xZZכ5; nS_*=j8k"i^_X>#y+.vQM{=Ĭo$n2=8-n6Ia8'v/4#I^Y&J=ka…eŖU=8FMyo]j?#xsiִ*tj/e\zܠ,Z*U3 8в)_}ubcLK2eD@DDu $Ua=1)¤\f&z[DKSvGMe`TpW ԰F~z;_~}?qNQ-艢5&awLSUAP?e 'U6ᢪ @7&y4堨)-꼌QMy_Es.5)j v*Dm#.F-v0zymDNz/l1OONz3dJcfz}R)mƙ՝)jt_-Óy1j[N޷Z[ mA Yq=֏3qJ9$g}y+\ؚ??>j"TSSDIq_ tzECړ3ɣ[-jr<,ˉ i5ť9Q""" M;Lr~8 Z #s9ՓOrԜPKՒ]D3h#|@v" t`rOY K t 򑇉OuV w{CySqechg25iGR >5(5TYXZ!w H]iW&ݬVcB8eox×e+@Nu9ږ7f=ǰ+} ӥQQO`Rh"./Wp$ƒQ#ZcsWbȣD=V {|-)__PM8[ѿY" llsִd[]ED*ʺzvFYsP yiS[/5<N' 3X75(UJ\nP:V\jD-ޞ'VLIu(v$ōʽrMz4-!?5m*I/ |wY#IddlEhUMdR?N&uu wMW(R}*o-zwdFN7}JUNW.L[S,Ėm+ND ^LG=qX;L!Ng"Lvc<j.$w6(.|ѹ;J qPVG{PYJzld`[Mi=.+u8t4wR;]78LvscjjQF1{˽5oݓLܮN} &A6޷P@*oBؚI'rI9y]H "8r{~U:aDDD@DDtRV@*h-{r9c]ȼT2ᡛu['&>~XQYc쳲i) yo5C5mҵ >Y]| ]N+S[oeL "N.#ƌ3Sc@9T2g8H+go zKIw#ׄ܀ɴcȂWQRїڮ 5tOTE:GP8˜\?9xAU5.h0\#V+fw fXUVCp!yRsmm jb0i#́u>o{K]CM` [4N8l'sR^0q0hnM{4,æ]Rو+Ӑ:f*,ԺԃWX2˹y| B<\͚[]L%x$@3i.:y9DxwW;/Y{hS?6?5^2N;\~@t)jזH9^uFD𽻂t D%a?Z+}# iY#\pL'XW0ޖMos+]rVՊ%`= gیge_+1Tk5Ґֺ]gAkzdҵEi/V[Yqd/P: *z܃6._P";1l>iGeKl5mBN4|KڌwF}NJAg^=]Uzpx`"&=I Mn!'NwʚӺ"WP%tSMI Yg> 7u8Uڇz 'ޟkVM@%y޳}%OQOZ3?((hYCEMJsl15Yvޏ7y*5Ufyn*ZV mtʰPvka}\յska 殨V-[{}{y\՗26a ]Ak6!qԯTicox3⥑mn,(W!$KqRRh%Uٸh)hpf"2A;"0\ ǧm[X7U'qdwvdZ-FZNK:}ڷ3ǁ{Mik֖2+uDO;Iyr*:y$q c GL*pQME['FVZ̴F3ϬG<փZ^Vft⹯pf AC 0`3q7gRhgtT1473Y׍9Bw>y2gD@DDD@DDD@[o_77MIϞXSI&U3;LUbJ8O\E]%-[8*ir"˛L j'w5s5Pb}aԶZY:f,hs\Pr" "" UPCrI;k#Xp~ʉD><%|[Xkk Ijo; t)AXf˩cSFي@+K] +)j)y(dW\4:2[LݪeyE=7Ҷ(?%ȣGe|M27Vp@]CaĜ ӄϯX+4gѴy'!²~$nz~kKc-MuIIFk')mg:i $mFq ^U |Zثij7ywQ*5KCH=# ;cf@O&ceI.qqFEmƲ}@ 9 s6{VHl$c9/̶:{P}+/UE3QJL/Ăo~*QhsH-# ZW6wCqs}ƾi㋬Rn6>j;zL^o\R}RuQvke O&Hq|^JQu+kJԢ]M>S;^S7Iイc/@ \ֳB\uedR(}RIqێ|UWeR4mc۟qM7sSM;Ovo]j} 67;,;0y\Pm7[-`:ZsIkiPAy r"'KV6؝ t绐qI 21/쮩[⟻42",DDD@DDD@DDD@p(gF9Sq})C`|9)@V%^u%5k ټ,G]M=#^̷|{Z< RG+x{^JJNqSqaXY.T;x],Yg8x f'q@v"6ONI(=mMr݅DU"3_!72zEFx\1MAVjX(ON7ORORPQCLzvnI9.=I=I]" øZW[j8"<Ϙ>j4[ 4c\]_TV+{hkA!8Z}cK{kR \i6糉LjDӖ1,VQ_0[Z7Oʇ ]l|}Tɓp F8g2+sNMٵ31tg<䑓MG4da8+2Kiwf;=',a:vjuUVAg5Ts;W  c&"w30V=L:{m.]:7QN]qM'˫<hZ תuF-ь5MQ Qww;v{VֆNlxȯFGb Q])[37w'OB: f_'(5"쒦?b* nwN{+tMiE b- #>ŶmM$5 ;n3`SiFr*O+ew^|`v"D@DDD@DDD@DDD@DDGWY-ZV5 EiڻPj}+`duuE[X ; v4]Y.,FˢScaw-dIqq.Wu>t#IJH{mI-L @^{c%N`~t[,/=#.ɪw6 {x0>gz =ώVյPEo%qG}ĭ}U56w` f-jk;ucÖxxz*4j(R""" """ ,qص c,Dݙgv<+5-jR[>cDDD@DDD@DDD@DDWum?AN"nkI>x_ }:Օ&Y&F^##" NZrR6>Ljn9Q=B)pq7Yx]h}=%FҤr`?tXēcajAڣIyҸ4{~K\N/q$LGdI㛚w9-s+}R10p9slev ;wg+>_P`aD@DDD@DDD@DDD@DDD@DDD@DDWֺ.l5 ǟ_Ebڕ:epif-oo%2vV;'ci]g6&]DQ[]RnyiKs65 $6{mg_iQ8a-ֶ<""" """ ?mayavi-4.1.0/artwork/dataset_diagram/unstructured_points_scalar_data_labels.jpg0000644000175100001440000004641211674464502031312 0ustar ischnellusers00000000000000JFIFHHC  !"$"$C9W" S !1A"2Qa#qBRb$3 4Cr%5ESTc&VWdst.!1"AQ2aB#Rq ?ܺR R R R R R R R R R R ԏϕ79{&)ZBgFl~&Y,˞CTz'z[ZG9ύkDG*_Q4>B&"!b=&"CY,~nQGlς^GrFE%s."2/y;IQ yZؽ ?n Pw*ǿenԎܧJ>h62_d,gL*rx!+BHBV׃#zd|~5*;hNt'%V E%QdZp ~ )ocy!2Chkj'u?:mNDа`a /b4}'w20+#ՙP1޻o )@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@; l-SY$|IDw;d=:\Y eP>JA`\)uTW&%dʎʜCJ#oI>INɳo^sE}p)?LUߜ?`fҸt!ȍ{ $Pq%kRV;+:'x;+0mWȝT^n4z.H^\ĥ(d[lvIOU7P)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP+U-0L]t-#[:XV\jn Rt)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@/3d 4qA)B@Q'wvT+Y<-q4fY5 %CZĽy#$QZ+qRχ!Ni֒qX$$VwRet 6aa@mHJP4 ?i(FE$vFG{G¥z8ܘҳ ja} "?K4+eN$hZAJF Un~lZ{ӮJqD3hj 7To:v3;CE@~&!\o1giCY8<ɚ5BT!="Z_jRE)JҔc2oܿWf-v֖J: JR |΀'@ j%RQ dS(K-vߧ{#ck(;q+VXR܆ٶYqCk):?=\9{Od^ gzsQa\!Kj:ۥ}u䙟,]ڟ\oQE│\q%ލ/]Aj:w~'6\JbEzL2gch뭡7Ea#"s7%kZfb~VHn[($yQOG~ [Rt䦕-GYZOR*V )PRIwT-ߘqۖ뭵* RοhH[<ȰnQ HΦ] --HYRt{vدoB!CȃA~W}[ۤ65hԞ"R= gKx56{y)iVՠ C@s~WɷN3v''Kq1ڛ:KZ:#j==s6[] q-p[J@(m;X7#Zx7.A d\y!|"NzL{rn!HBJTOe9F5e1c62:\S]^}$oC v`nm2ЄOJ>P{hvTXO٧$իph_hQol?ϽNXbp)c>ʹ!#'dl@+V=ɜmư#ڤJVҔ;b;|+u #-\X?(Z|xvwtk[ޜB);P޶_<7[I?k[|{\ܺv-FGwJl ]/w0'doll#@|:żZqe #LvWL1~P-}ȯ %Ip$VJu!šI1}Vy?nHȱLQHOTKPJcjc6h̥bB@yh)]Y?I(F7sEFkl_pKkZq AFL+(Gd]zp}eE\dkחH>7yCyB@DXN!ũ)+ny+>%2%ۧ5R;i0$zfxmLG'-9M1S/#mi?\RRRRRRRRRRRRgܯzsip_K.K+`w~c4)&b#rrܖÉYdX{?3ϯ@*\3[Lq0g;-J+#zd=VBU衯n9Rg^y&挛!GLopzXkcC[*f XD1wL[l1&KMvsvS͘3zhgp̦`\jź59"4ʝt{:@=:%&ku*R~B/1G纩wiy ; $vHbk_ФǺo2xZlyXɦd6Ըw#m;m'e=.IHؚ940rmp^.iUcKBB~0Od;Էllr岇㺏֐} \:zXok/@RDMaÏe$ I\ZrX=bvdi^=JV--Gs|o*&9hILWQ(OjmrmmB.)Y,ʀo*r)>mZ:P֎JVKg1[vM`Vۃ!~d褨AnIr|~d\-S(z%{%E[F񕔷v)W,)VlǬWmăά$ Ԝƌuy/nd6y_R|jTĕnnvhBWy#j~R^[ʜx2l;wFo o+O8ņmr5^#d`\%V-ޤ'D>zJRJRJRJRJRVgƬw{u* >7dXg"r gQjA܇ta dqm,KL UԮlm5e&U&V]o̭CciP= x&SӜZi?,*Z]<>PI=44*@5sT9m\dduTZIѕCRY|$Tg*>j|Jc% gw>5HZE`>cu_$x޺֊'דc̽z- WDvh{(/B҂%&@=+Huxs!N%>.[}0>!oq{kzd{Y 1='g^C}]9Wg+ dw[kzP@6{Gq0 2fE2[KP׿vȎ#Z=?:tGMHuf̱[aeU!56)Pq[JXZ4(w:$y*;wDtGpHT/yw3M~eW0HIw\j&REs -/VVVҙ}Ҵ(h^ZMF:a<+}T$IŸ/oI׳RJZڌQ?//uEgWe PpؠR{oZ{lV/Bx6_c]KHH;Gkv1HXxϔ&!OH:B?-R^7bAemv:*dNuBoDmVxʮJl[$ggOV;ƷY$q l1H{l$z(Ac,[tY9F>XĻIy 4B-_@F<z<*q%A ![AY}!敤(j$/c_Үh[X1{lGϭy'T6K.SeP\q hI2o{6Ju8?Y: o뒹[!dL-дa[^;na DZe P)JP)JP)Z⷟gspƒG%#ΎWU򛯊Jʜw"z).6TAQNUtіYi+[JAP5%lD9 kJ{JIH5e˸7α5UEi$^ѵyhv0[e1-l?Ԥ{UXZz[Ńak0,ǴW[!~q-_sY PvޡL8f%2?k@";/(鶐r(>T/Oo>^S:f쏢gI74iӓ1Xܮ|_O[^o77f>{i@Yq4&M_F;T5Ě\I&~~ק-z]7[+]SQbGA[%#X*#ܹ%aق]AlߺT3i>jN}cL#g^&\Ad-[zle^"#EQ$,{AuxՌYld'RT{I5Uטj釹qe=jD~߲t=R5R;wXoO /FqkN]ϗj+<ĩAUE%R#DZn8% ,⋳\pU2OhΑ#`w'ҸXY"+(m>9\SEH>i ;`eBlfzx ?OG9*kvn"K?PL<1{/ʽ\,r.ʺj:mBtktdٮO7Xrm "%k[>=WuzS^E N X3qȤ5"huwMlcT'ZZIW\]-~en8{O!*9X:]U"Vt^|ֽ>mIB֮|¸If\=GJʷnhWLk{Д?֪عYO:m,}/Hm-ƥSFe[qLb~Cvt%YBGtW1ifA91A:vAj le,ÆZi|8r rv2+c$z'[=DI]C6[G q&RvPen9irtxT!=N<БJ'y3,8 t sݔz%#̨gV_ݼ0yԤ5w^hl5zs]Zl7\4cBaauZRZXy;-Ϣ|R{g4y ڨ@ AA_8vo} )8G:N%D(!YGnݞߜZFA@:/֤@ @u]llE*ߎ^ d6[ P6=~yJUE5QKjR+m.8q}=.am} Oc^Uٲ.H9=ܳ-ҹJ?!'l]{Sb,mc[B{Cci W3:ޜphi[JdvX;PZvHBaE`xw!6S,/IV٘++#O4GMff,gdu 0 8 l}Bar`'[ ] G |J=UN06¥Mu>/P)JA f<^[+.,A\,b;qB)]'nލJb&t3_x>6"u R(BzR~p9q8aEgC°|bq95KvQƮT(6nd[_Qr[wҀf e ssΈou.6I$K[Va^1|̸j|@t70ç[/ʆ~jZ @@?R)?OPOlLKϹl Y5e3vx6ё)q́ ^5݇c1\f ؒ#Ch (ԵkJ$~nYߌj$VrE#ݝ "z\i(4f4J[Sz( aXSS':bۡ#%gM?s&'ϢufW_n$vޥZv";lv+v)G^gs\ׯTVuE{8rϙ[;`ţ.jC1" Z#ʈԟT0A_Xҩ\[|㲈KWn*)ARH ɸ78!lĩ]JQd! Q ]HzA\IӷHԐd_X =!@ƥ XU14ѓ*k#լo hzT0/ ~'䬀b-= ;j,Hךt b\q[#SWzP@aS+Ļ3gۅMz-  ԫQxmbfckW`ϲSiߙD,_p|OT׎IuIĨFOJmą%i>`؊.|)c9˷ݮXGԽqCW;;Jj y_,r\ci~ ,׋<DVϒ̖ +`A=58;\,+UOUA/!I!t w5]fiۦA.7H#1WSmw~]ϐ"j*)ay3e!$IJGrP;] [m ͂Xt{'  [Du=2[<쒞q[--J: ̓+rr--J-̻$;-CͨE9| :b];0WK_`*D2Im'IJG`b0X, U#q!4I>dܓܚqi( q5' i8]NJί|[ ;kwk}+'%A=JʏjE`h;e㍅d0@~9KBFcxyUԦZZ*-t:PvbxvR5MRqMU-j JFԢtQg1&d=Jm>A @ ιx5dc$2ËJ;ҷpO~uɔ ^=(Bu;wK¾UY>Yy< o*#SZʼJ>ٿƖ; 4Ag5guKB[N Z|iW0"*a*+JvwTa-\eo2m i˶)qh򒞡5dc$2ËJ;ҕuJFS#J B+ⱿаJG~eS]2t]=M Hڪ~4cy.핝RzOBOxA_7DXyZ_P$z; 0<:4ڜqiB &?[cGmklQJmD$GBk%)kXI×,LA65|Nh =Fq,:)ri)${W_^7#8.[f0N|)[QoLUm(·Ey}gZG* zr׍p>PBemydX,Vn0Xq?$kNw+DtWBج?βRC,2-ZJ'WiLOI65+g`8á`_N΄e^_hr{=oyfF`Tn#\KM՝daóLW08!=KCKӍlh_k ̸ɥo]uu8bB؎*{/ag. [G뵍Ui0RJf^ħSh䍘 eV~l*@(=ef-ɬI Su8aJTuRpMq5p`K##.pv{WwǓ-iXP1.q>?G.V;Dz:ꤼ$&q.1POe ҠR~׃qp~U^UfMCJ(!+%NDxrS:m *Rs X8wX2y"U~tK<$2m:H=V ZJbʏfOSOI(6~{#FjUj긚ZP\tyܑ!2I+qŨ%(H$=kEa ؝3.<6߼5uY$vU*PubB6JV'_H,ˬq'8IJ:{#iO6~s;xbֻ i>u:Z?Y-:u*1 O8 -İȷH[NPA$qe7 "d$). Z€{h z]XH0-S(nJ"O*N}k0((((qsɘ3 R%v.Z@~HNmJkM9ӞK>G̋L)VkқPC%.6plҽ؝JI{G)x&-0v*s_Q{Ž9aus mw.0W}4"/fed2˅[ +`zvn#9trx|k?Ǣ2|]r-V6zOIQBAʷ =e'"jq +|wef  $CcFJֽ Orǣ #o6Ё D* wcN@øfTջ$\{Vْq '$>fϏ݋|xRv!C^Ze61 D'.iWQ먫kc]T^.&T5D͐oZܢ(Ih8~~G*><_wƉw)Gsj *$,RI=R:IOkicE3 KZ#6N֤{F {dv,.qu.3}@wI2^(K[[^Fn6Gݓ}Ozsߣ>5_vݞs sC:R^qNSò'=h'A]ٓJ|Ҿ?dSJzwٱ U*.ĚxG7+]xrPkR{n a|bqIMҠ +ud뺒zS )a^ʭ/UX,ߙaԭĸKIP%y)kRTId{u]bh.C!%J?^#co\˜c#zCm ԢYZ!`w[7[ɶh|[a%z!)̫vYOi|Ȳ'eGq6 +i$u5W_;Tc9=H*WqBO#ycKv7ژK`YBG'dRM\ Xx# lhp1%ԁ䥠([-Үq0/[BJҬˮqk)FGRD ^ٱkR!m$}TkL*\yUIumBKV#z'~B x߱뽋)vsS܇)-{u@'cO}e5޺KU(TH j< e*3 [<{OL#:ݦ `{)]&%v+dXlVX緶@+8'mm6dFaŠZaPБe֪c.33Qi #A)@)J̼)@)@)@)@)Ax8W ,Xyb䏙ۋ_OЩ-N菛%J*Q$O}6񍆽%<8˓p\6lMSN#"cy f#˜kU{1RVuOR;먯@R|> v=)V {G-v[978מ^'cp!@mpo9k&/1vNA]?]1h@;0&:S18xFɘNAz- ,(0PvNvJ\yNOI[[#ʐ__JV쑳\/26SY q<}띹RP!j ۠Voq8o-d6=un/CkH $4R}kMm=E^vLݭs3urOs H7HC*^*Q!T%-זƽ6mS%!lH<=ɑ[7iJ呱c7wJV9qĔ*'H >}-5)q#psf䛵tE JmO59z#a[G/р *?CE'y"U~ecVvF,-MjIPݖd$7 ԑ"7.oS2zVQc6)a^%/.Dl}KJU^0g&kd Ļ8[t w#Y|<$Mx䢑BӳR+Eǹ1uh\J~m ]a>'=tG}ـ}5V5-{t⎥ ZaN2TA h=WFvxܬ6,cVwcmbEJCAgIJPVҳm-¬P1NaGC!EmБH?SaJiJRJRJRJRJRJRP_)xY [8ecwGԧ~ڠqglOrjt`81sn :ZDRFЄ˸Q +`R@J(f/9{v`78{zQq(űR (!x9ZLZI:.xFgIud|*oFmulZSVKyQr K6,+_(u^|猰 +iĭW'V!l>c(VXƼYYm78O6қpIձ 픍J^ɹr5k&br=GUwK*u^gx)PD՞gOwM[{a|CP#`ZM^R4THT <@.6Vض1=!3<Л^׸)tq7P+`5vƑu" 9!ԍ[?_PԏJ'+ >b(}5h<9Nwx$9O&4o;}\ ӽȚ7Af%IakiP-$)*D|?u5~mX1aZI{߷ wl+jrz̉v d{M=jBIވ?2۔Vm HzAIPw1uoJ'󔳖X5\ZT22!lGޒw8`Xtbz)%N:Zյ(gb^?~ّ|RCcm'̡ ̕ W>\Ggp~r\2rc#fDPY >̫_Geq+  U\o)tYhY-<' C/$\O[6ҥ:Wujj)/:|"t|:UW/5qů<\|q2Gtbr C RJ.8q.=Ĭۣ(:Jy_G=5Y=*^#e&mW PA%Ci=q:҂2idBlB'j=#س%٥wRڕ(U-]pgg&ّ\#J#֯_~ˉ>S bTgS̸PE_kE ^Pn12:liP"9p꺜UrçԂHHV<=$,U*3zF㜓 =֕6-S:?׽t3+da5܄=#-eEdSxlw5.z}9(yu$UJj;$- ay9keH=()>=vu\w1.X %e*W)H֝R5mu,qQ+lۦ;>E˅ KVP| εy'WWwH\EihbC%}ڊvyTė$\-ܒWqOSpzA$R#^RS 6B;%]73phX]dztǵ(I@N]JUkZ](Y[}}x1越(*$ J /L_n δ+yIBtށQ:A?[ײqLy h.)?*xe_FQ4-֯QJP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP+dXbdvdu=.4б "iAd^x3':nv;!PV?5d{ P=RP?ek?*T+o0\)8$Qo3Y3*gv6dI[2`liR4>TA ֬n.+l;k^:)aaujm=~/hoKYAkɳX% RZH:}UcH\WkP&;$Bqj!2>4^8IX]]FuD,DlzVw+J5 u\6Z_#Kˆ-4 e$CdzCs2sȳMT]#}GBOak3*5q뭪TZˏ] !'ҷO؃x3!mn:uֳBF3S":iL8 ZU;4 e/y NZUz#Ph_i |Fر($,.oӉķNJIBR=kOx0QZqDo@l|;)RRRRRRRRRRRRRRRRRU+[r|f.1B3Icxzd;!, DtRM}j#WWEAqճ1= Zν;'Il곚RJRJRJRJRJRJRJRJRmayavi-4.1.0/artwork/dataset_diagram/structured_points.jpg0000644000175100001440000002126011674464502025101 0ustar ischnellusers00000000000000JFIFHHC  !"$"$ ^= ;!1A"Q 2BaR#bq$Crs3c?h5 6IU_ԎQe|*Yg(t0C3N:[5ל]\_(-%Ҫ}ES3AkoVy̯i3* qpkv """""""""""""""""⫁4SH\+vUX.>uCsHl4!x*,cWlGq[(%X?zy&k]4#;2WLU%e=,POHacNIqo2oJKN1A+{O`|ɶ~(2kNACFwZր sNT\trlaEo}\?Q>EI͡m@f/ fֻٓȈ"%v{F7aCUUMRf} ;V}ͷ -fOJ_8{"@e޿oӽV/RYV_YqkUAU_E'LstH; tբvAX53yԷ7sQM kkz!Y ݖ[01Uujbm:Y"""""""""y gX]-}:Ck l ;(i$!"IZicd N00ɯ\~ғJLOn ]Sc2)k3Gc:/#!:kWᾉ,ԗ8rҦKƒC:B؆F;KZ*Zj*8(ᦥC , dlhZ֎x\ʲ;+eݩ-v\27gG aY9-,dZiCE<, Y,Ok}W""""""""""""""""""W|͹nhv4Eâ/i7CsIuڜ_g!wn:ԙ]j`y~g {F?ޑ-]E=,usOO$Fƍ8 $Cu=6d;$&vd|֖%LhҸ;oy#[%Cc[^ VqL'{el ǽ]#cY2""""""""*thr6 y3<]" Դ5TFnk١T#_,}ZbcAIw'Z-\gn|ԕ\dkȢHtosz>pEuuj2~sHwVɊir<^ӐжF](H4XGN DDDDDDE }A vcP\ =뫡>NmQeͤ{Eu,K{>6odlc-sN/;%X[SY B\ ñ:#ԞF۔x+k% xOpA"6Gִ _h%!o-ntn|֖ F;Lo2=eקnǯ+EedV[KE]T|TߧiV y-]mXYtF!uC4 [=hiMCE)nlhkZ7@iqU-,S;(X~ UWNT*̚^!v5;IfI$h MC \}[啓䍶#uhgL-:pBh`Zjc XY$r49i- Z߅WSsm̨$)%#|w{[l8rAhk8 q n  },5UQ:)Y:6;U'8qό"؎C-CSL}::`^jqW>`)/L&qm.W2&<# UQ|zܓ"}mʵr|xk@xyTuPO-=D/lK_v; !\9jVnǯoacΆy­Jl\1lt=eY9=Âǚ I'@i~CM :/zi& p=À>!lDDDDDE}Rr'l=%=rw`~xsn\Zw'|9Tmޜ{u^ӽt-V==H=$;$N)۹3Įs򌌝:]7b@7 &GG3zhଜ7m1=Qk~z=frM)R~N/k1o cmങt.K+Q`9&D+*PzLm#vA Ywa{ADSO$H̀u;Nlh]#Z$=2YrjȮ#28i壭伍c.VDDDDDDEY=S6!<]v/ | 明Yr[M0GV>^"㩂 iiax%ad潤h؂;iaT=Tnq/\dNkHvx?%gֱ5Q|$peƗ=: $V\5e{㦻o0ۨ\Fփi0HVo`<χ[3LګIu`b4duA#v1}/y'L:JˎKpՍ`'ƟAqk$X"""""".CvX.7ۜK-]Kأa{UW͜w@.E#C!si):5 bV]ΖnfOO4gNF89V#)e$10pB:46t?K۾>DDDDDD\Nm[hQ{ xsApowQIANd+CuڐV\ swW<8:LNtŲ[~Eeu5;gF;荴>A#YF!cNB1>DC }5k m=fl-0!E$ƀrOahE;ůuʒZ:t# tT)aw^<.TcF2Fkב *yjj%lPK#tִ@ZW=q%x}\M47AΖDDDDDEuŨq,ny).HXH}=3ta{W UL5hY 5=aA}Cnr2J'U*)OS%cI`A*I5M 6e>]{i${? Wnn)mvI+l4Dާ%Z]Sjjm3Hs?\@?:o 2DDD\5tUTKMܒ c>ǰ] k%2jijqzK%:I,$+DDDXk٥0+ƭw14tI {p1.GY &*ZFث]ʃx͘X蠹Acp;FFw}/k[jREx{L9Z\{>@6dx>;o_%Vv\ɹKg H#GDh h}ޑEf3% ίZtu}>5Gɮ:Θz=NihNU6bmKlvd•{Z4dQxDDDDDDUnW&]r]LZ!KC/phk>OA(1zJ>RӳI! hH xx-;l^#.{:d('S`q p cOdR>B%x 寇򁿶@~uO+wDDDDDDD]kŠk\O+h%'%W,z{Ir::GGITG4:Hζ5o6Fa[Mu}S$TR|zpq 5EFH84~BDDDDDEӽ]mvKdKʎAj Q: w>HXâϬ'tq<@6W>q'C#`.@ĬprwOn2P潧pFpF‰' ٮ3ƶ]SB3swq w6\$_*Y~zXBm=DlI}tDDDDDDEkZ7ONٽ:UU d #lͮm l.;|{>־u興 .Q)z!lW}lN#Uj2NU:zr9–A.' cG‘5 6IZ淝z^D:yΘjDRqpHYol`|5Hi湤?(xbH'#K$kF |(7rz[U\nvs㠩wiq!Ė4hu~+OG=X6KK>@͏_W۬;ƶ3R>Yw;V='%lf]nȯeEwSa G O%ñsݢ@ntDDDDDEr]h07\䍔U5S3:6ypR9}Pݘu1rdbwqSX+MFMlJ\"uu[ FoQDfyqQi4:HZ#z.f x+$DDDDDDDDDDZ#ַWr7Yau]3i`&Isu4L.=,pICUsmm* *^Y-5D.V8 8Z/dR=K.dF~ *`""""".*ژh竨xd0F$qkF`pnE#RE-3I:4 㡽,^vXoTw=\w )=47[t<Ǝ.TrERŒdk:?+*DDDDDDDDDD\.uckM cz1u]os""""""" dH#{C摢>A =Jq5ˊ u'hsK'/3`i`V1{rz,s[ dQvw8ց\{ VŘmχZ|:=ru9ކ;"""""""""9K7pKHϢ&O!8=Uy޶ݩije{oh{4@vO}IY? B˵tU"$b{S\p7ӿ#j jz9=5DMXvױmp?b+qVRVIYO M<)XǏ c٬TMZh-t2Z+'+c8x}%4oMl,kƃ[o "KWySCpF9=>5hDDDDDDDDDDDDDDQ7=;ak$s^Z\l8wA9< 痚9Ap@I|}'^79ݕ(i]O_M :s~Zk۲V|̱f[\>ʙPn:sN‚l]x]OFm$w6GtkOo 3HNkAD-.lC;~/rwVFȈ?WeCVb>54VS]xa0kpsT Mx $5='iU<cqK)j q,7A=$(OϚ2%=3\IImSk%imN]/ ڒcOqB'cn'8w興9pvqQ~K FZ͗8ZtFݢ6V}bul3]l͂ĺBA-:*Tۨm{} FʫVl^[ e9ce5p9μxbH'#K$kF |*%鎧} 0eL!=,m5~nv}Q  䒥69uh`{mayavi-4.1.0/artwork/dataset_diagram/unstructured_points_scalar_data.jpg0000644000175100001440000003402411674464502027764 0ustar ischnellusers00000000000000JFIFHHC  !"$"$C*," G!1"AQ2aBq#3Rb$C 4DSs&6ErT*!1A"2QaRBq ?ܺR R R R R R R R R R O5ķHe.ʝWȶJNXdi) vnU|Jr8&◒)m7Ke}Ar|u[2zVVJZJ$v:޶#+'gvG6ɹ%/LQu؏ 4VRHH ; P}b\p^qlJS=h((((((((((((((((7u2zptC[6r*tu+`;pw"'KUz} # `QR*do2mpal:^裾NJƘd<`4RuUYH2(OJ¢~*mnT%?ZÕxv}.uܸ3S2_eCDt?1WUh dNp+e={[@@hdh(t%:WQSEKSؐYj-vk\9IUATRIވo9Vi2m5o #յ:]ܐi׊R)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)^[h;qOoKM#d̥ wfجt⡇TO87t frF G e9#iV׊EE,N]|'H/mU m1jh"4F9Wq^Qډ$]Q'y]Д BF4҆ie(jJUS+Qr=M,+JҝB<*WK2*lז`LOJ*;KJjBaTwX,W"cu" \ HO5HމNd: r.Vf^qշ#_S*=H67} Ơ 2DTspg H;3J'm;+((((((((((>ui^w?ڥ:j (%>U;6zs׺#q9grk<W-OhXW8ɯ]BTt#ܪ'J|Z^P6kaKQ5Nñm0>Auw )=s9D^R2aZ^dVf#ѾJIz[-ÜG/7h]zeGU\1O'f)]YReag nm#zԀf)kИTU*& k,lWoi @5a~Ktd/W,p FGD,u٬V%FFOl:1$NP[y#kRӃȱwTwSmN+&q=sEۮTKm2t=R}RA h)JP)JP)JP)JP)JP)JP)JP)JP)J¸Ĭ <$]G;-!ُCɿ*=tߵ_:\Xݙ:K1c2^y!#R@>桩@8,cĖ岘 ;LkV9lo x1dZРYovD%H'_tZ3>Kw&Z1,9RVATC˾Vo e8ݞۿL[~0[QLs%pGE"2?X$ywW %su1㺠oPWzt#@޹F^KKcjSb"Бŏk ƆV~-ggrqK<\ii; Jb apm1 B9 zB:>zf^abjw.J\?BCyJOҤ6yH%]kC/6 D9lh?pzь c9O4HVݴ46tɫc2o[uѤddT[<Bΐ}݊mĂ:*ҁj)ncRl=DAd2/D$;:fb#ruJ`;0wf0nQ_O}nrjjF&fӹ p&h <~y [WeCDqiBJltUIrߍJrَ˸O%V{']zMm}{1NtY. bˈ#n%%[u^/@46zlFӈ[ޗ!~,"kǻ$7vH>kϱ1L266OTlS)QCbdGy ֜AV Am=]viWTݭ.HlM!<0ahX 5ϗpE]^ ȷ9[W[iGcC\Fb%c]fE3>IR6ZN}`L21\u)&-ǗnSc8{ a@o)yDzSs15 l'@_: U3UVs RD&ę3 lȽ%HIC P=~Z*?E0<֡ʽ Ώ{+K*DD,SIԥ(#  7e5I@q =FN*h+Rkqf[yC0ҹ<CG젤@"nSkl1>ê/:5dse 胭hobJ8ב'ݞ+7 =!ɵw]jUu_%iqZm-H$ eUk+-iHGbSp ⼊5)~?KSD(a:-%>uhXmlL'#!}k(wN RfU+}f^.oPS︮BFUo2[L\Rߖc[锍x vNZ[nxk4&Sr n~rH'םW*R??ח(-w )KM s:[mԢHkX.UŋkGʼK} tt;'~t_%(=rnU~T4UH1cGYmHKm@j5C]uKȏDjm6CDO~&›\yASmZ#}Il+6G'gCJ5ʻΛ'i5WL q:ȋ!˭( uyRTU*)l)A]U3,YVJTځ)qo]R .ޱ\9e~3(n<3Jzu(Aw{)Q J ԓD&2kv)N\a~HQD2OQCڵσdcߏ%LJrpו vI'[ڦ K|L3N7:+ *dN?"jX#4jF㍻ oֿHDۡ.G%]GNuǨ;|8 @ci(BF 8vq=z%1%)Cח^=AtvRG "'9U"Kw-sCJ``u?<6Xy ޵XHoǐve%|w oN˙Cmƭ*LADsVZaVȶt+gm%I\cRZ0L]TE"<Ԩ-2=Wi4aJ`'ӔZ7k5T(P%9XD5c'~y_[}ZϠ w8 ^EssmR㧺Ȅaw4ͭT)OLU}gkqGOO`R7(d J!!)HhT&jj&*6nY{L+tr Fzּyn\K1n /AB{hw;q7N}WK'QG/xlNa'7p-75O^m SjMV]C\MT' %%v,P kNҴo&7 u,/s)WZǗ\u[TcUն@y@sU q8/LqқM Mçӡtq;:Qǝ E]eJkϏ_vZR┥)JPJRp;FX.wC;m$gyCp.뛍3$awmV('$t" eV/]9d(U)D i=tgo:׆@q ~06 {k]2ͬv6{,+U3 f:(HY59hx`.ܯe[U4NgQaˬȻ&gRJ(wJt@=G_XUgX˸uB\m}M_>rpkK%<>-;BEwn_׿@g)J\Ym\IJq!IP=^;tzde4#6s!o{ R( /<-EQb]s_A /0W(;ԯQv'dRjkE*Ppy^[\= $Ÿ l}`t`Y&VClyN';+evq$y~:QG8׉`_2{z߻5E0N%6Y%iK:LFJ Xu*PP lVp-EKQ$k#Qio<z-\V ZDVmqW.FHȗst`mRd Zxi/։WuNi.uYvVL]q9sA]WB(j#;+-^7l3o))*iV:DWr5"sXqV@Rz n8t^.f؄}L6d76_BvH@#gDdlr]#Y-۬_;qOWgIֺ [W[M{'qa p,bihy, :ѥ`47^|`mݿx %h񠺯dIF} oIƺ&E2+ejCƝ@Rجlj["t&øDnd LKyyб:FSAl-ˎyaN|H玿!]k[gq.aZ諮TM]JkiBk\MjT̨kj w kl \#yI=RwW'w((A֏}K38v~i}ظ, ͥr$s=twSꪽg "˛%aLJ/HORI>=gPf#riR֢RIWᏁkQC[ )Z~t%PU <$nqO\T̋B( iҦ:iJRP'[f\BŚ~YR}oZVx,_zDge"AR(>ʽ(DZJ63ݥ8YcZԦVB@ H#ZS:B YOe6#Qp%UPR$>bGVjUeBg.OmcȗWP(Nr7dNneg-ն%+p$hhvBA]x#%1r'J6#@I21(c~O:;P?c@Z k`\Mr5Ċ%T5S\MI8 q5ʨjPKueOI nPCҎڱ\UA056意)yU)T4v]\Oqi>'jˠp˪%^.ܶZ$oڤ6]mN!6$5-x@:#[2KW5Q(rp)beGpO.NO@T۰D޿ߤk}8pQWchVkeY /pM$˛[{Ο'@+z۷i1z5@hj4K Z i@= DbyMDx\yJ;ۄHV ́Cll/89 ")YߠS{;>^MyfUntMWu}[#CҽfUq)ZmqXy^BnJ:!G ovbM]$&<L-Wd#gj:}?9u7q!)ts}umK׭Hخ6Ι~pN9i 1 JU)D~{5S\MkTZ&\p5W25ܹq2# %A7*aCWe/8TLt47=P|NK8~7eıؖ CO+m$*Q=TvJRI&.Ǐ}ڭ;<[=0`Dh5;)Ci++AJRJRtp.OPXYJpR}z_2y\QZֵ)J'd{}k62%?e ۰qJTZPRA yᄈ.0fP#a-^H#Ѫ=^%3Eq\M A#Kd~O(БK'YFzk7XZjFgvYǵ"ւ;wWޤh,ˤFD?GPn*,y Pzq~!Cj)猌q OTN1lo_~kF&POb~|Oӟ+o$C\MGFT~csRCRQ~q寯w<ߗ0FZT5,oH)Tgz}Oںzr;i]zT5?)|O *j3<`i*/ QҺl "GԧtRr~uq5ǎe6Gѯ@\  #7~RS=U߇x `g `|{I"htē׾ԞWx013 }/Rɕ#{7IүhJjG_/CYVfdj%Ǘ/kUnx1k6^Oz`trV4vGgRA"y.?d-ʷ_q.1O_ ¹O=~F~k">asB=AϢAZ ʋ6$ڣ#̋5pIԒ|FvI$4q6 fGCvndvQHkg jo+G,JbugdtomNtllzw3ff-l[m v<9 +oqaCG@m|ٮZn`I$H^H<^w1-Qw[c_}nd ckT) HJ߁l5bS9y%wmɨy^w{I^>lV*%\֪&T5e׻#Ӎ˕dZ# } r+6$- G;6ǡO6H?`w!p `v]p:tsț!_[ί(@Q˓hY.q;FDǬ)1T㇙Qq_j;$R)@)@)A.PI;"}zX3qNe?+o?6=Ys!b|}QVxnK*(sUq7pғͷ{ {Y)\_ mսaBVzI2O؀jشOg;/*H 2b0U`a@V2BJ_ls*C\Q2gpvKv? Y`Sռ?TJN/h”V{7r5JJ>k#U֯i=cfE~7i?50ӭ8_ߔ4LgCm}pC ?zq \Bo*WW`9nkP;h)=APFHz巯)W$>?\ ]ls\T]ThhoC`ٝ[,حsS/k> %CcMh5IU<}?F %x[gW7>^%r}a ZUkCdjl5~:Lq&)o #"]N1u|f8;aL+Eu)Lt ?>SY? Fp&On=ХGd h$dǴy1c|;_Bl$9>psn'i1h$vJG@?eԥel)JP)JP)JP)JP)JP)JP)JP)JP)JPQ@)%*4A1w 2F|Z4 ڑpÐ|2-DtnٖF}5 ļ8EH|A5=ҧ--a|J3.DcEd wÐ=j9 8u%m$,C^ oX;qp׈̛^@n1 :q?mU*.{/#8`2:$Yzq#M]q.0' Ǹr* b G#%J[[%د_0)@B#`ZTeSUZWےMD -_fw'ioG/Yc NWlTAN7`'~ϓ][~APl/OnEHqHeQ<@X(k^{ȱGr -{fCRᕗ\0{#˷}2% 8)mu%#D6VQ>$+X˷`1赲'q׈sOAVO܇vxT;XoOegN stR{bUS;_i^ZS1LYbvQt[NgZ4vfcIFQrO&!'~?΁;oRm3;Ȉ+(((((((((((((((3JBY[! ۚla*ׯ-dT Iֹ&0$x!uڈ}:ڭ(dGL,2;\jb7d+RX]o$yT=y\k!~6x͕|6(Ife\n=֙7ɡ2.ϡE\5zԥJP)JP)JP)JP)JP)JPmayavi-4.1.0/artwork/dataset_diagram/unstructured_points_connected.jpg0000644000175100001440000003050011674464502027463 0ustar ischnellusers00000000000000JFIFHHC  !"$"$ *, I !1A"2Qa#BRbq$3Cr 'Ss5DEc%8Tv?ܺR)JR)JRlԞCo ĞJ2I&'R<{sz"O\6RAu+Q6x<{+{EeoƳ[웞3={p\݃’N;֏JR)JR)JR)JR)JR\{W=h7T{l~: J_Y.?Xlۻ=9$GuJQi'DIIR)JR)JR)JR)JR!;Nܛ{  p($sgg\7J)qfz,Ζ͔-'AU(oRhp3GR8#!;AZI;e4im$%HH)JR)JR)JR)JRb̂ӊs+Ķ/HuCzHrI:I 5Ga.-ѕq`3lkk'`oM7pq:-XATP߮ǧ5919f:rdIvۧ8ԻW+pɯ Wi-- A7RΗ}kЌ ]F SXnDgSnvG"rCHqᅿ_W&:Nd4ގJR)JR)JR)JW>&qHQZSuA(m RO&zoQsz{{m1x$Q環w@49wJUY/뙷So Da=R㺭:RAIr 3mCn 1xg`pr+߃CO:6%Ē~n)JR)JR)JRP}R+;{w!}'$|MB'9]')B$%) >)Ug0̼uiҔFsXnRvEQn#ܨt9=zt #ֹ+Jέ'JJ 節=Ld$hvH?%V})JWj|K#"L)W%CGȯ7B{W[(HRv _h|+F&JR)JR)JR|X]2xwΜ+:K8?*Ae^TwK[lO%[HZ@3_Kq2-,ɉ)VZJU5+U^xo҂*) 0:#PC\R?UOJR0Cn&3nCjxK@mcZy'ЫY.dɍ:^m?I)KjT)JR)JRuK$g%K}4ua(BGIQޫdZHInF])&4u^>yFϝTO]:cKck/1!w [yG)[ϒYa!) ֶwVLxQƶOjO؂;ZZ;,Hec[i,8'EGh'vrPo ͇& }"3I1(;YP$Ak7լ&cdI#^[Y#JdZE II ήJR* y-{ѸCH^g'z*NӰ87ՂoޭRS&";Ip|W)JR)JRLV50W'^w?Y͘wj:GJ~Eê>= Cš+׾p~R{ߚ++^GfYfi%O:ph|NN5xCdFfDy\iCa@ 붕S{V8g[@,|DRׁQ>EK)]rfTgcIi0 n6+I PEc:sz.5%X궩;^W'^|Gɬ,!LWZR%CAyšlR)JRDk7*)j%^2=쩎m|ET79wT$n|]cX\l^$3\O] H! Vbe=a@ӔN㮁_YecH?=JkŌ٠V[seb4@>OQI?rk#JT+\]F4d_eŶ&޳P@8Zvt&R΄؏!K Jj%f cx!{ʥ#(^+^=GQe=wܢZBd\@ 46xI$H/vK50{@y tGt6ةnt%Kh 5a6^v{]aʊ_N);Djmy5exeW2زd'ݢ5͖yz4)JunMb&ڤ5Zq;Jǡr+i` =Kiuܔ)YRG:Vk{l%:#渔v΄krYv>G[Y`[87TɁ9H|-R~)JRˆJJ@lT] یgݦ0/6 [Um;\(=n!ó\n_2izX$|-68 5,cKͿ;]_ Aʞyz Ԑ*.sfgy E~C*;0om0>5RR!r<.,m*I PEW}3rs,H$,GIq.5 {TPW o0|Jىf2R[t< TtN|7aΊ̨'^l- BZ*Й[r2 T|o^xL$&ʆ*< ʬy}hV[y$[q(}ҔPyc^{Խ!'E ܑWxoZaJ?tu\4n+FnYA'|嶤<͞@ť)JR]Bq.Y,IDh͎ 69Z:j_Օ20՟mZ5>Rnq:+]=6eڔ$xWcg7qJq쩴kIfRGrZu<~+]O5OӲa53?h蚞RG }e0BzVd69 )PBIlVR_9ۚ&KSxJG )JR׬NKt|UޯT濻F#dr+`¼/'n/Kn3@!RyR7XX=KzsƏ=~o5ìqz5*NTv}J'^+q3+)qV&I!r[k(o W!<vuObG3q 4i)@+M>Ì<\iĔ-  I b:qvstꔫhek;.ZQ #V)JRqWfrϐ[ d+{ iP*:P ~%t7*wFm@R9yyq#v-7+}6yǜRP.a+RhNJWb!*Lb2ҵ%y>jƥh@krg-nP7d*m([z􏧗 Z!Sf'·lmV< WZf2!\m#A)#(곘V+7'%cW+UgƉ'SԐ #ֹD:CLkT?WWFOTCdž7`D7d*\TH}gkY?ǁԨY,0!uɌq}_s!WK?=ݶ=?KJR_SڙkM<4\cO*m'ErwλxgCb]me($])JRFe[q0ğU~͠Ǖr6#k)ƹQ;ޫUB_Ú"˖ܘ(ZA'S?(嶑rDl.0(XUqgQ0COLFGoDߪRR_mӭ֒Ca@AkA:=ruYħ-!MWvU~q`GaDǛh#ߩI <~O>jѬVMXrh-qDB I'4jzic\]r5s k'O$(:vHg Cx̀Mτ#^,˼fې)rY}*BFACUE{9̤uj-XlH Spx= Rwv=aIa-GOtxP']Jxvިy~\s]>Vz4)JW JV$)$h6Bzc)gTwNo7 QV)JR?+G1,FNO:=y ϨN:~۱1IOZOL%48q$;~1)xemd$—jGw4Fߨ]h0_h;FEo\d v1?=$-{~*ֈsz}uܑ^?9䓡އԬ$l"HRBAl\ҵԷ3 ;&\mS٥ߏZҕ€P AZ3uc,w5GLUB #}k;FãMYha9o@1Ehhx7,Zꢢ7BywX7K{!v19v>Ҋ&^\O;wu0+[6LFh|(@$$ɯ})JRC:lmřxK`o sHW= fw<5>(8e)JR=]YfE_ic7ҏ#[J1-MDm}V~ZozJژMƂc!J$R+ ^<Z&qũkY*RvI>I5nm])'F{J}QJz vMm*">ԆiƯ9؃+^=Twt'ҠXE]s~#ƇfnVsD]p)]ʱIѮ7:bT0}'ʾf pNzZr]')2a4U%ր!hy]u)[ȉXbffl?>#% N-aa2-qCW)JW |X8Ex霥v`a*+n4?OR~Exc) &T$}$ tqޢd7Al>~6UJR]{Qγ7؛:oZ΂^@:l;_=BqM%+I)P>W J H$o׳8z_oC1L=Zz JڊT_89M2YԠhXJT:S^dKR e:-G:W ! @OQgg-Q+lr=zHp/JרF<dn;kUmB[BGs,BG*Qͪw.,_6DX,9oahd S뺜ҿ쭗C, HRTA*q革\KVWo_,P WP2;Y$߬y>Ot*CMZn9 l*+umJzHJRoTb͉ߙ[+O1 kO~FU`¾Y!^maT67<= {JUYyi=G8~' }Nrí>a:R%@ "t+>AE&['tR!>UళXIΔIWVrK\[6DñHv+[a%OHsϪO8)P\'\eW?cCcgDW9e(v;`[a4(H'd$k!JRR6Cc]IQc ֚ 'g|_¼p=zm2>XṶbK$ H+H$Q{em(5^Blj=CT!<VAS ?jʷs7?=cE#7R:R)J$:+iЯջc˗_%Jڽ5^W?pFЯ1ƾdD؂m#궕<8Ud;YmMzqA)Qۧ$wypOrRl< K wÞ^lllr `BynZrsK"2q^TT0%WY=O\6X߾q-ϝG@ߥY4*uylDq×c[WGfˈcli0Sj ^?Sq3m8 +?gSPA>j;ٟju(w$JE+YXUVU7#p~&az ZJP JF@sJTG&^oxB/2rե|7؃x۝>,>/deaqW{ҞW.<+qGO R)T(PW*̴]Y#Ml:N-ӯ$_4iWCEEvJuIqM0mۥv~''}T!`}}Ww"&G!LǹCS}(J^d dC]1s ?W|jF:Ju1cS<5"ئκaqSےZ[#xN]Kj5WZW;tkyne{ˇI5/'u־n <+~aG_Vq}ߑ[Y{*Q`?XnJ(yymHz*o,$;U2 W%1E1.d5!*h AWj^!tuymlo(a%&hHleBW|MԂOy|ǿ/<ŤCi>nvd*l}5Eu*ʘEB۷ZCdn\-`BHz Pn %ř2ϓl}s]^q[AC@9A}T8Mlu)Q6m_>A5:I Ҕ)Jo4m.6  }T5r\ 耒8FǓzc0bk<)Km-PC-!\K+*!C^Hm[u= vDYaӤ= Zw*wøYژ]lUE\'0=zp[(p4@NV^!לWs#ɋ o!¤qZyR9]EؒAm$)JWLe7*3/r7'#n9gzk_T"oKq, omOܤk;}+r@rV4(V9g .޲~%ƹIPWR ξb^6>dR$b)JRB>Ns+Oڍ8OGaC0N !۳ JŅe˓N5u)͢*%i467ݯ" wo%9 - U7EG3\VAkGuZ:TR4-gMC' >)\.N{YGk5:)JR)JR)JN-{W,JyÔM#W)#P67Qz<ۖ;HO<̿Kn6}~Ғ-AԜ-R[$Q)V̍?Ҕ)J=~(w6-_MUi4ɣY ?lGL|(jON:9NP3ID;n# &+O'JiJR)JR)JR*!tǧTrJsyI 2TdUu3RVħmpAVMaɉe}4FC J?@i~zo%_zE`b>s[?$AA)J=>+>b.. Gr8Xu1ÎYi-GzlH0 '7=mŰڽVDfU}VxlR)JR)JR)JRabyC(mv{A+Z] ;EOt7p&;쟯؞fɲI1v}T˼)km&mWbwB T;ERukWi1Dz:ܗ[XR>R귴f[py돐yR=lF* Ҏ8$Eە H[nӽ=!iOU6Cdm6Q)P߂Bծ58t)JR)JR)JR)JR٭rT%Udt<҈JT5*wۆ[Gc]y[:"8ۻBˍRO#Vs)Kd\oqTO-{P>[{JR)JR)JR)JR)Jxշ2îصݰW8 (Vܕi@_*:>eX[c]e!j H<8I#GT~]{ FzT%YiKqj: HO}9[C])0e6ɻ8V#P H*+#V)JR)JR)JR)JR)^+ݞ|{Y̎WJ6-m6;^0z>t*QJR)JRmayavi-4.1.0/artwork/dataset_diagram/dataset_diagram.svg0000644000175100001440000004520711674464502024440 0ustar ischnellusers00000000000000 image/svg+xml Points in3D Connected? Uconnected Implicit Explicit Data? Vector Scalar On the points In the cells Datasets mayavi-4.1.0/artwork/dataset_diagram/unstructured_points_cell_data.jpg0000644000175100001440000003354411674464502027444 0ustar ischnellusers00000000000000JFIFHHC  !"$"$C*," K !1A"2Q#Baq3Rb $'4C%Drs6ESTc+1!Q"2ABRaq ?ܺR R R R R R R R R R l0\1䳐Ja/L;T`PRA*\۔y}((V}{NkPDn6Iu u=*L帷R~DV{&{Cd|+1f]xG֡ؿxAYA't6:P{1Jt^u>uyǧJDzC )kS)%G$ R R R R R R R R R R R R R R R R %dh. \'-Z+5WR6vj}+ksψ 1K$U($&{ΦfQ"@_"$|xλSN9-bK%QFz3:SnPWGJXʺGYlK""C%u[! ,!G𐭒<('SYjOfi{!Ni #P1h~:cϮL[\9H姒=JOnͼRR@JUDYU] e2*T6HM qh)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@*Kq6%1qw[-Dž%~TpHT N ^Wt'wvd)RAPtVui!Xe6y'<滵Si^c:yR|^TRyfЕ~DQVBaEoۺGS%=CwV3 ZߒX"L Plb+o/܋!ZhNo{_j{F3n"ۈ~ЭN}?+IA#}{n;k88MU*ZjQ%DMRӧw=2+'R}>˘q.RRSǯRezfsϼ1];Rz)JQ%@:_Z)o`1/0w=|*'E;#Ҧ'JޑxԥّXa_,.9xZO~TYm+Y,5v{y,| 5~V;ywu%)JJR R R R R R R R R Rujnumj)LRא RS߈u`FVa$*^>U%tqQgvøw;bw%$)!I 6+{KU:La ֢G!ՏP:i-twJ eTI?̝:X)D(15ۑ&Q*֋O6F(|t?+úKa17Zt!ͫS\vvk=Q.b;'e٦kwZSJnC~֪նg=vQgsM7TG0T;> < KgRRRRRRRRR gSq Ɯ7+Ծ!YOK->.5WKsWc|IQwlM,W|)܌y5.6?46y*[S/;ӿ~ײ'O-8aOf jmηrW$'uE!qN|(kZ- $;5:cˆi$zS$ILXuxO)R\ǝ2KѤ4m^ (Xog07'Dq~_X_zR u-qLhQ} m-Ks8 ^wrq.4Bд$?-T78nšzˤ}3 84689fw똴;.t_ 2B;)$H'gA(*doC/6)崢yJcǤȽn_ri tG>iI! dk?|X!tihDy)?P8G=Fq⠦ ])$( )J)Jbs vەcwT,ǒwWq +I=k-Jb\WO[]'ʉM~/.kiUv9Y6.qL@q]>qσɪea&ɂΗ =5 +e@jfٷTK}鮢6k˴ }%_ޟjW{LKCraLey%(<2vJP)JP)JP)J=ư+Jg_Dz湰l>%odD%%J 6Ic('#,\Vgl4 FI 0WK~ge{N㸚Ţ?R>UL}:'~}4òJNV0nH !@BHGW~ mI7vlԩ2oy, w i RVXBbƧ0`]u[:TB@$YJi7Qkd؜yD `)g{W</~1on0ծ"D&h})_^ɷ6G*NN#u2+gobƠ)P\ǝ2KѤ4m^ (\Ā ${wWI K̸!B\i+iZT6 }GKzJRjRݭnۜFfC<-'ЃUWK{bVKV>⹰U(9?v >j֥N5` ɖYuND,i9 5@>1'@SwN/[7kbh8tke11uJRJR\2­d.c^ v9'Ԩs0_csKuR~5 Q%+TN =5:yF+wԙW+uϦ7\yVjkA.6HGio%2 ^|f )@QԾPzO-,Jab4O t{P>CAZtdFP$6{{!Cܒ?"h=+iE}Ds1% kgWSρS;9!anrݒ3?跹:Oʀ}U%1:qa'V/xR~w›`u3 Ǝh̶ !HJP48~UJN)̆9LﶦmciZ4RGЃ>ܟ+\u5O&ʁ?1G(V -6ZYqV%\Zi$MgRBtUĂBA1"J[&Ĺ[\`܈ZK̺ڂ(lGk^[B-cm _ K_3Nk`+5J 7k?bI=Zl}<%[O+HShsVU|+nlL{}дBWiiJФ-!IPl]_`6U3yR}Ic;>GmtQ\;DFROLGB̟ӰjUEQ^vg2GOy߁ $zoV!re(|[aRqY Ošd|xsi}Bs7qkD[<.H=N#V[zc5==l;~_WVV%)B\TN^IGw4(lBce!$i)JGxy냧`F7|Bg-Chf^pF )$)!I 6+;ZpYOhSu8v)rMSAkQ$6T7% J@ V}tg[Xe% ZoA(oI^( >Owҵ0'1:bLw.HZNҠ} &Z/l%i^ƼM/Kmm[XRWr,4OtAZ!)<#omкӻ|ku=9a4Xm:pe@ۚHG›R&f=J)P,[xuw2X.[,G?)5=JFT)JUKst`On+ q?,f<)#4 JKcn%dWGD;8%^'ԯR%$r<[yRHRTA^ϽM6F mjLԅ?{IOv$ +v Pko(zf,RbEwNj6)oYH*J({V;>، _s1p;l|+-~$$(E`( ܱ9q|w),  (ү)?"_նY]6$E93"V&vXUc&?S1 6peʏ@F4vj}l,; VSJmДIߊɺ:ښu Z T rKV)(ddӭ)C1ObJ |#|UYZ: T'ˣN7Wh{x x^6AVLN^YaxLRyl ? GҳQKf5&6 rwѯ;BoSVdwkF5{ W+˃y>Rx ߕ']Hg!XaHR~T^#IŊnڣڭ1 :{ZiG|y'[iĔ- H)RH gӝǮZe.W }6kt!> Q㑪E6[wTkpFAMջpR$F[l6c`GAd4$UO$RWQﲠ9Ny^U\YA{!Hk?|C{Ԣf"{@p7._.q̤|WnΡ="k\5i#:BKl+΀#K}., u6&M-O/!CE* mZ 6+B]#ZӷW6W&< S_Jg )5ߕ?M!I IGx[TVnsv}j1te%BTvvJ6|=T((j& ΡF7ИˋWP*ud݋ פ>2[MfaS썞%xe`"$>ѯM-*+oUYJWT}+Q\;rCk>b[!Tc?:}=MK%98$p@qk=CΝڬ6{ B/_#;48Wb'U#[d"}dKQKϏ} a ~Ů)QˎiV4@RT?0x5MLt@|DK%1C,)*zZ]NYW}å%*tiocچrUVH)JUW)JPW~" xBl %*y@$ J*9y\pw^ľfm[1VRP,:9WȎ;Yٿ9kd3hJ̠a䤚ڑ13[[P A)tG)oĺG ԛp!Q}vwEC_Ip[+E2Tmc‚{vjRVt}E Zo_ob${@=cx~{Jۯ f~})qTQ<%j%Eo:[%7Yn_[O\-I{ aMM='2++[0e1ع*IJT䍒<δ a,g%W΢n*@u`֛G @%Jֻzy2F L<و[2ibg@qБHB?GN?}c-x>WoH(\zYTғHj{"f"ٖB]X#M||C(ykZ T>5׺[]a.y4ai?#c.o]h67sUi81o=ĖDZ xN4~6ǎyʱܢ aĜĖ$DU)fiJU|ZR-!IPlQNV3ZϺMT~x6>Sv{eAzlvT`ŷ c.8vNϪT}9CBj RPNdVclp] dN}L(c}TBG%}6˞5{Q_YPWiXGiU-Kb< l6eORI}GQ~bs-z+p2+$|MtVsg:#(}DDv⸿kn6pn";-inЕҼنj).1l{a>ҊX q͎BTdUgm-nL3}Myq).)2I;T>4'umu(5#(@DiԸ}nB+*>Nye ,hP؅ ;1m-2(ChHR\^lOmpnQs.:]G|USuwے\=\g) 2^\tbf:P,}78)P?gkГG>zeID,`tڐX_懐 H6uǏ.:Japim}<㬺)jy_!k+b}.$~Ls lI;KT5f0.Y2~ːG1)y}v=jNy՟4iUsZ(3Ԝ#ս},>y^4s [*`dS„wҥ'Og5L,)JPdĎ<xW.FH1koi_RC%trTTe OI$ 9[ƾn7BTUt*e;(vTY W \gGOƿ>Av<-u[>h'izz >ǎ+ tw88m=Rʠ>LwGw֯U-Q8i0y@GJ[̮W&ʻb̏- ~^tRÂwOųⷙtne+:H궋DǾ+RژLpW_1qNZ +:]z gk.閎KtH2uɊ,zJ#t9 ՍrlsJ{*#!☤6yu T8*Ai)˟NQ gW'-L-ZjZiREKzM_~|5Ai: O4{P}r?P|@V5((+1`MM"yM+[q:bt*yUn17ȭViޞNJu*w > 1-ur|mz_TYJwNdؕlԁF5,ޜF n$\#(#hNoD~f_y *os^نWs_B{GcN9-d2>!b oAk~ Rk0|wSWĐ$#־[(R{7|q |wG~%[uR{)It/:V*7lQ#3[iGn ˝01LmO:c…=&YiTA|YsSي]YKN%w3m}jjU]38v{FIf][ N@>;`1#AkWĭ?+w33;RRRRRRRRRRRRT{11 9c(ƭwqؕɎ8zJܞ~Tȥ?,!JNۀoܺv{*ȑ\ObYH-~Goÿ2蓭UJ%3dRif\2^/vjc*Wӹ Q%!#PPGcT"X;z"3#rS_`L\Ⱥ|ňLWp5nJ}k["8T?(B:U~ͷL[ڴS`p\H')')'Z'DBz; Ņl84 V,lkj'E'ϑT*H֜rnXӔ5 9U3+Yb/V`i#/Ud{Vٓ ED'_ZJFiܔ(Em x.`}L{yCllA*~R+?J!?"r vL* -ZRPizF[`̹#ۭ^2KiA[GIJR9$zZmW^|;Ų7Z̶;Nj]l,8bŏmAl!K6%#dAiTN=IeIBy[pWOVEZmayavi-4.1.0/artwork/dataset_diagram/unstructured_points.jpg0000644000175100001440000000777611674464502025464 0ustar ischnellusers00000000000000JFIFHHC  !"$"$ *, 4!1AQ"a #2bqBRS?`VX~ӻUøjoQrRzKOj_(nk2?Y.SU>kU}}5zL1ÉW`2.*Zד_Mʫ Wڏr{L`4QU\>vvvt75.n65+T{J&[mSscL7:y: ̥'&R*+NzSVR(O؝}*1~QkXִ65aZzn*ZO̥KOmSiӌa$?@5<-/op$^'R)=ד`VdmlGXӧM})4sSFi塌Pqܒ\EkuT~y"b2xŅ<#!iNִjӚڔ[L緭sowrуһjs^jϦ]Uxf% ,{iſ_NOmG5M9[_丿]9mJlrUM/Ry48 ̦Nu{wZ-[Jbmv mRag[жJ}kqO_f;LXloqg:73)פ)IIIo[;#ىXqWm_Q4_> sM9m͎W$ge)PZ䓕GoKKz ng^a,rάUXh(ՎӋIE5ǻM JWVTΝi()jQjrғzOdžJsy+zTRs~q⟺ca9;,3[_!qoUpi+Ǖh&3.U gj4A˻JQNQOkz[@u?N-SQW7PO[[휎Weq7)eqe{m7N7 ()=u;Yl5:ĺkpRJudr-uO1W*26T8kmrN/y5:TfaIN7T74d0،bq֨5Kw e5֬+\SZQMKN -8&/IJ5mx ɽ\]2f/?ehy<}GuFjЯFjtKqd4ѐ 7KpYrT9Tb\k{ϊ .wigVX|*jxR{oٕ v{ڎ JT8_K-hoߕJMu? aecoNޔ}NQWI#,+{{jt)\P JM? ~eyyzr9էGQ5"{3p:6VУ?QKo~MOާ_׸ zה6'#mn.*ˊ-7%u98ſSԛ |-kZ]Хqo^VX)B$$4iT/n.=>UEԷ5ck[S~>0?[;kkc̝ uXZ:qJ[I=E?E1uK;jQB(SF1KZF@庿etcg_#ukN:81KLe*Vq._|C鷘Xs%z[*;eʭYԦ/ݷ)(y\goN k߹Qz5NqzR\/Tw)Ey۰ J5m~R[UtaNԧEAv/+Y]CTtn?-QxR%Խ<7bo\%ƽ =m&IoROi6qg)U+V75in4*wwKJ VNl~SpKYhZIwRrK9\w8َ'&NT%>'J^ (~G)9']96Y0Mԗ5W,l\U*4iEu'')ymGN=-tJzqJv>{hi,yarT)թ wvBZRM&槣_8f& qۊtZ:3VܜRm/mayavi-4.1.0/artwork/mayavi-banner.jpg0000644000175100001440000031300211674464502020717 0ustar ischnellusers00000000000000JFIFyyExifMM*C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((()" f  !1AQa"2qR#34BSbrt67TUVcs$5C%8EDdu&'Fe? !1AQaq"23R4BrT#Cs$Scb ?J" """ """ """ "" t\.T7ѹsr22-;gf~tpdȹn͑i}O\33K?B#lU4UC+x摐}⡫hɄD@DDD@{m5J4x/d|ԛv@[?4'Ǯ]䵽d:H"-o Цڸv4ە=Dj8ڹ,=X .uN/feRH""" """ """ """ """ """ """ """ """ """ "|e 얫5#=1Pj$Vik2_;]dS+m:Wawt9I! aXI+ubE8oHkiLl*i6C40C:S{E*iyP`*.[JIID@DDD@DDD@ޗt;W,Zz:^_ܱi,ݩUx}7i w~]b;gp,۪w 7>ZcuhsG1.8#6=1cr_ư |ik=,5Lг}DEB" ""==uYU!펦O.69pk2\N[/6\3  Zoym8 S R2^T*nC"!D@^m鷥Jei,Th2:j vF N^Y3rHH)!t#RGbԙ^N<2gwf\M\$]d˵k&4K tu5sudo80"k@=šz^q;,S뽮gk#::wŐֹѹÉvu[w0pJCum=0hF$aޓI:A~5Ԑ՘pβh3.cNK^Z17(l׸FwmSG6(ji\9y]9!Ό4P݀11]Y:PMWEn\M+?0;eā*;ɹUQC )㡴s.{mc c#ǵP5n3GîQE;^Dݝ4e`ijq}Z*jiክ,YbxqivxQlXp M#=AKr&?@pgܣO[ZYIsoWRe$˷򋋳.M="(""" """ &Ogo"" zEğhK~I!Z̲B""""" """ """ """ """ """ """ """ OjJ1 99# v[zJ Ժi#u7|Ϸek/"/);W^id[̍pu\Sjt=|OgaP՟ eBǪ~n{AD@dȸ*1u]S{|3!ôe}::R<= #mnXe=,W[GQ5p<)0 wFI;n&Y#RW 8r-=[&TYI#\;Zn.`YI94]_ίt׻ԎX52 X'ǞHς} :;1RKncI5q$n @f|3jS)廋gѝ-4rq.KT?7(b|c]\ZGaii]I'+B6kh-YSkQӘb{D20%c-CmB͞k ߫]mzQ8[K4tjOGP Kǻk#S[U鸊jWAw{Zr6M\;K\S5@"6x w]gᢉ$F2-_Ww4-kxa2awRyAy;X~ jwv,#OC/u#&6= 6859DZ' qˣk=U%Ӄu}@lz k-uQUS8$dΛqderr"(""" "" y"˥t-yihZ"I2>q\} >ɯDE""}_-Dqf0sߕ }7F|QK\}AI_;mL;z*GT GQU%dM3&zo`Ãyu7l(.PNDll[\'w1_amm|KAKYjۣC=kq sk|e+ Z$mLl1c;6_8: (KEM[3\jLv;ʷ[xK 5c''7ldx/P|?sRUAo_?SϤh笒viPf[,QUE)E+ڇ1x{Oq)5{og\%mc a ;9v(6w\+351Sk]j%ۤi4nw#Gh H<7`m/UG7JWO:a;8`9;/*fkJ&T_ ݡaCƐ璳Ք[l5 2 9!w[.fFuY8.|%ABjiapwv'y;Vo8~D]o!U@]C[憜?p †UԵ4LVRdl1uu7a+j58C4-^͚9v;{ dUD==²k)w%ѕPd<TSw#-h vK{e=e3J5{5idw[RL""" ""%ÿ\u^KW!+DED@^ I u,+uD_z!D@M55ǑxCv.Vw/jH&GFa4{s^__K1c'"/0D@Uua.T{?qwlO](B" 9.5c5m&Fhۖϴ2OX|Q!TO~-:*y|N⌀H% k=/X'&T_L]3 Ljx6^A/;v.l WN]5q2ڎǨ-m×)YHѓܶ&;;WWO*E= j>W@hY4i'9IEfǜ5lTDtG=kHٗx}~p 1j6תߩDIZ˼`>)=h GǮy'|}GCiI#-T.k{=eEğzxG %n.6QF""ZV*+iiQSO#!4u}yԿGXʽvUtqvI׆HRF8VpFY^x-qD^?GduqZ)-wRApa$xVdzfϼ@A8sA+f]IQ3#aF'h ̫7>n21 ]~`T*p*gXS"C""?pN)|'Jhm<isp=-%=@~ }#59f];vm޹EMöL3LpWAhݙ3Ʌ kԡ(i3B}<˙۽w9uu5UIySM h6VWT|-h4۹ g#!oi-٢E7jjJjms=OD0;Iv3&WJASSKwܧmcҚպxt.cx=[t+f\MdmFC. 73e/RcXs42Ѩ(ux. !el-*V=OW#Fzwǖxqw=ۋᦦ~VYMuGi8-ӥaKh/t3-5v zJqu K$ .E\Kdws| :٨ʨ Zp ?8!-Vy|)~h$GG2Hn|r=lC8lʀ/THæ17;bsAGSIq In9c$O%eYIEiKuΦS IdSFkqՕIAMWEu{ +6*fquUKb\un\`vဓ>-U`ΠATnT3sܬJ*&KneUÇ+& dkk@Gent1qYGS]9]El;UTr[>LT\LJKKdq6v~hn;#b:H;P8Yk4{6!Vl*blXps2p޲7y8-@%lgrqPSɢ]7\z!V,nN(`n㙎p1n7\2#%tL?*3Pq 4טCQEPƒHÒ-ֽwX鼚}#.hoT$H.fwIMe[kT0J٩hsӱ JX 9L=XKK\OqAu8ed2X4ޱդh$DDD@:UZ>:-Y>3r?_ϻėf7xAۼV<+#+Tв [%˜$շhG=EI8fV+IO;22_\ߡ%Ng#Fc9۴o U*OSxDYɄEb9lpDnck\,41w GmZyvbJ])ns'8C_ F4/gRy{uUM ]d0SR={E]zK\||U7h2>W1XC"&)=Qj a?>?ab; "/=PJd J@v-È.lt {W7*Ҧ|Ӽў|T'ؚU"K#J>G9%$QhD[p Ӊ'f)m έ:G`s աF9;#5^tkÖ }I.i Ϸ,lmloƶVrũv[OvyI.W(m4KԑsӀyv E5MzPPslxn55HO 89kUZ᭠D湧=ҒΝ]/fgo"_ fYNñPV>-Ue TM-DG4{IUEp8G*h%D gPV7d.NzeվNë3NA`8#nG uöH 6{leI,`r^-^?F.ЋLp{(AqE56:1 97eZAqy4V/5#<,⮵9:GU=TU4psÂҨ) Hgnj9lAT/cI#>gnLk[3adD4mfI'+ FTbmvN9r}sڿc+IgU4 '>ToΰqyO]WNu3AL'eE\qJ0ӷrktO}ȾOTz[82"/<u$e 5RKڀoc!-?=xzMakp7D_zAt 1w[ZH?-Gj}ąOAaDEh(>Ԇkܼ@kN3ܟri'bWgQ~xlެϏV~^:uQ%#8z?T$zmgDDsJlvcRW([ͧ*gڋȈ$r߳wn.~8Irmjjy{\K3A#vEz8wjW{#Ʀ*+yjն~'3h6s_݁V[T^8)sb1[lMmh|. ˼i9sHyZ; ;w .!_-x[ll 2OY#+n{/[P)GW8q kpA8gᚊN94 s@#vOHvKKMQqu;Ex0] :b|@#Kjl|n;Sbtaݍa]L]+q(n812ӂFFoó:~ak"sCDwrBGpE YJm{7V긫(GBܶP$s OR9Y bzk1p MЯr62ä'ďz+ͩNYY;DP:DDD@rKg>W_m~>D^Aގ@wYÀ ӀAܮ I,!7u3 u"/^{#Ǵ9-# 掓,Mm\4:) 8CvvpLt/'[wu1TѪ"^3GkF ̯ayz?i/ A+Q{~F3 ʼgp??^^U?ۅr{OUua.ڸAAŲ00Q7w]|_X4O @DDpnn3y\ۅ Ep?.fz""3&Y?^^I(l_O{3tu NHH{og> -ZWPՖZ28UG ψi)'8`K# ^IoqM7'0xg|z.:z8Y ֱ|b8vF^ցrO`kḖ:X[}OV%N=\zZȮL0~cw6\ { yӥ$0*%No#>70[F AzD\IG .hh>?:/xzV%ѧp\ۧ}EW>ƒZ j'Hs6kKNfޭ~DEǒ!-?=yzZp$i!q?lIבxq5ɯiz"-{9_^Í3vJVG_ִ;׉, "/./>w˼ O}Ƽ9Wލ_Xtf9jY tR7$jkߑW|bm;1m˅-T`qv1-ÓQm\䨎͑WsNT#V(:YR$#ǟp E+fe"sAi dxoVV]Y akcVCu<Ùk`ֱ]I';*\t׺%6#t: ٓQN㨇w 4-u>~򈧬~$|fO!z`:'km;l#=cg@N}E;N_a'*Kcu)܏G[_N\ˍ3Vի̕as-Skh*)ORꮣ2 ;wǟK Y#R1 Č/̂_LCBJkd})NrVeh1ρQݨ+]Q^>z8kݻYOVCن ,}6sӦS3p2 #pryd]shm?\ J2tURLFwbǴgǵzO3m' eʜ;'gDZpZ%GAR"lu4nNsI_xr΂ZedFGனMVib,_ ( QIiv^[M;3@DEDD=? ڙ\tlJH~'w"/LF[7󕝮zJĔ# #-#dn -瘖yOb<eA@;`Ac\[= xHjsO##_syMϼ$B _ cEw9}SO4}$t%lLjqo*]؍}kanfWr#]aʫSNv- Ew'ZK߹+-sΜfOʊ`M s1n,1Po|nzmoB?]=22z/7}.zv$Eb*MF|mJǪyidi&G,/{r$ePtmŏc\-$ D!e(#9$=Rۥx{W֢Tt^-*:w=a}Iw̦A{ y-ӣ~:+!m#9^~?a燜)4&Z"/=" @m0#{i}VE xjѪB3Af\a3[Q5 h;d{z/r_hʰ}ln T^EYa٣ӆv)5EJ6}**oUq=ޢS$2M#Ú^HWe{WpD^^3,]NJо5MYDY " t×u6iN${].XJ$Ui]Jj0EojpS/U{ҽ<0VWQq$FX9HhDDWGF=ѿp_M UJx>YH4!F~9^?N4hE;6tƗ#vpDrQLֹ[uzXik4Z<=ii /S ] ~3k\Al w -5vOQs}gp#-k bWמX3"" O}Ƹ2DWx6>vorn;4^N7;"/=#7wT5(%\!6w姘ܕ_wk^tn?6]'_ۅg5֧qeXX""" )6K㑡x-p=^Flwk38]+ n9dvWטzRj?vmS:B׃<-ए.gڪkE%.ut#L0۝a*((.3`fpQqeՔu6V]i[ #x##ksI#MQopdS9${t4 cCrhί_*LDsYՀmS\QG[t,,pF9g KC}Xg y~uk=C%qf9v}˩؋vM__.;=D)Mtqo])uVCLմ1\[(";"͕.\u+a7̪UV9[FF2F2Tư Hps\sH }klhmEsĭ/цKG5UÌlH؍5tpujD_@|9\J ͺQ};$D$᷊XP `il^tL_#K@H;nN9Y*EtS9&9ߟ0HP7rՒC`R1਴tj*ʩheefGmH9#fy /(m@>YLupwؑ⢙;-{;)6c;8 8WZHiɪqs2x>*en,Pо{m#+$p-,=@XԕQS{$f5ݠKqZJ$mq?Ē,UK"]@v`D"x|okr^<5NQGRK9Csغ⿯p}nky9ʆ7 xNӝVvDD^QxDDD@q.Kt+PBqa^ jHw|&alVȴވ]Y:B1yr_b^iئax Wh8ÓHs=ii /`;ܮUQWh}+)*q##~73};2~ '39ZQ8.Ac&x]X +H|jPt=if`= ٟC_32T|W@.&7sq 7JmښڗOq=gXVSMQ#~[F iᩈSsDH%48A bl}z5-47R Ҧz=DHzU /\e`8.e33ݰ^y}=4<20ӎ?)F;%Qwճtq\G#m.k$ì6{K]PRLؼْN=ŲR.&]]Ni]T|Sm%DqG(h;yf`NJpvhՙE? 26ͬnt۟pf!.".8'l诖;=(-o Z6ޒ3eg?IܚIlDE;uKG8QAǸ3sY\ͅ G ߌ~5 Ζ=fڈ)0" EܢuF#S<㻗_*37\z_wҭrj%'ڸ\ȫ]G\E2֊8L $4,#ä۽¦rZ",eS83D03: }+ hهZY:8ւdQ*.wʩyU?\N[@dnjx1yq0KNu7>m+̭JTf"I]EKmv<;ɖ 9Bu$́yU?\\I$w$^_:S/04D\Y^qly-\ .R۲GMDE.gR^"3!p yq ׁ]TW)EIKr]'k])^W*bUҒ;F\::* bptoj5mxDDE%Lٟ Ò7TNU6"ccu=f1U-n$2Y> Dž #GϢ yt>> ⨏88{®TIXi\DEt:Jd"isgrN{ ~xnHˢ?)~pBCy[9mCV[];[+HlobTt1TׁOy+/|[]kv1K ;M;l=>o,5j:s{*:`ۈ#v7kddt>I\JoR=֒*i+X迅e2XH펢C0854>eFusEK$TSz s~pJzGu9t1=Ca'ޙ.Fџ &Ku .&$$ vwܽ*ᨸb :VM$ el+x7^>}Su ]u""K0z>0Zڛ|{zs\;I􊄿GNl./³yˤ pAF?,Q|D@DDy5yDӍ:N9^\;Sk8?Y"ZI[-8Oi-%i1Ejp0WY*rձ0 =vAY.ml5ksd$-ktiKyAb+Möˌ2C3/wh';H6'mjY8| XC!lD7St=X˶yvmP~Te:D<9[ö|4j)@+e `vݤrH+/tnto:FfdI\N{sU/VךV:UDn6 }¤9 c0xra՚Ʌuf]pZylpB:Do4t5[Y5LF&(0'a#>9W1;:;6:zFH1liΧ {}MEwR:CYPָ' p;bpsWQګ+:Ȍ dƟ_09wl"1PF#ᆐѧhk5b?[k/}ɐ "n#a;[!jpQlTX{f>?Α'2>yd`Y ELD#SanM'.%{qm8ZOK3$fFU W=Cm&S6I u|dX:ڐЩ]@KTM)k{Il[͢S<u" [1"FDrc%E;( I;x ,VKEY!tP+;pw{z̢jxjidu\v4 `9 qCrp0HiRXK9ssVmkbCN׺I1vH-3HܞX=-Us.n=ᢊY$ݗE!iqm=DZeھ 0{Ng5U妫S?wȏIQn4xƙpHqKK`9Q zRj̢[狩2h[ZGgUAD>A3dbZତx|0=xX#{:%""XDD"o|A(]Q (?cvyo=EVꪺ psÎG }DZ(Z|cJ $ BRrg&5H"nM`{1ض 8^+]yyF=n8dyec+D=c9ʷOtTȕkک53O5-E\El$F)`l>""Ћ}nMN+ؽ8yڵ߮yqU{pWKT<,BN;SkIq =M҆,p0@'X</~')x+.sxñĶx۩ёQ*x ZcY<K *RVUO̒84bc9{pDژ##q ojSGGG 49``33˱^څq֦EN9:&  cZWJACF u+]5x&5Vc䵭ETvX^: N;E{.m̓&{K_4ז9٨+};,`C9hr b;pꊵ 5]γ: Nz t E1#m˃;╒FCwr+8M0^ cp {zһI]uVV#yEn ⨌> #kNBV4EQ詜ښݹ֖X+22Kxot^@[q;π\JI#{{V+5z,lv _M,~|qgϧy\Zoɗߜg|h4OVFZD.WgT Q~]} (We=ډ@'S\ӻ]+rӏu)xnu)jl=!ðu+"24O9~#psOq p;Ž7VqO^*m5õK#bHtǸZKb{*GqijF>b?s Vy\g"6wrrTNy|9siVj[*.$-V.}. fsoYgxYd+7]3aQ˓u+g#`'!ii+␵ 6D_6m I%n0_ E,R5ucSa眑7 L|wXi{4o*,FG\@w՘3ޣⴎ}%>R1F)Npvly4^vݶ=U7>@ H;o+u)xK|##%F8d8Q*4s W3}mӆx$',x,UdUOPru9JOIͩ9Jpm];ڈKuypNW CᙓC#=i fďj^?sSw\Ä/W[bmuYi3!vݿ-O^^+ }9WFSI+^1%=-r9tIGj]'2 % j49G" e3)tiˑk @_KQCT2=YވV`⪙Nc_i%8Ib+jqEZN#4#%$yF" ,'6zS sfu:FL7n՛EFQRy{ɴ 4^59H o'7Qvf,pg_h/1ۚXdcFަ1gHF|4$N&<̵I9W*맾qlUy*1zj[(='[U[f.wX],w]t*5Si p$3j=OWZ{|n|rDC6$n>uN)+h h(fh|Žc7/lhp0r]sVvdE˥C%H|tb{s$;zy-֒gijYL",sL92QHK\y|/iceV@;C櫪kaHi<@easN 8d1MHpRw?C^[/!l͘Hd=;PNatn!W$WJuÔ4{Nu.c|ղ+2SnL!q ל@<; eqv$kf, 9ybw9aقTCY<  CHk@9\y3׋2ۍM--6q.wIl\m2t;qǬu<~A(ӊ[&AHTήRy]N|I˺DO4%{Z0#v?1aub IiC#K\>+VCMkd^ǚ2O'`|7uTx)wG$zZ'XFBzYziLcTn->Co F+]POYE3mlm[H89rP5aNv4hror^֬@j&lPFeyX\ItkG3Kw:^E 8Qg%F#K Q%=ZkQ:.KֺER*ڒkpq+t:_ 7Sœ귉vOD""D1gcdKBUfr0)1GwĶX/SL1#|ȱBMS^R78~֨j ٩jFqc9(b\5aNw /ZKU1SӴi\'`b$@:[Nz\exi9Ӽ89j3pO+Ӓʢ/RNRݞI+ 'B" 8WXlcK^ഃG#*_FBj<3q%,U ::?+%%l8ѹ:ŻtH\ISb0rʮm:W:"/=" ""=:ۼ$UӲlsAakP_1jGa:\GK|+uv\M< /nI=۰jpIwpU]yd9`t27ΰlxXYk7}bj,w6EҲq;+"{3Cd#FXY~m.ZՎɟ#y{^^#gk#G c q8 oԂֲYN7tE]M$նz|GͮlIL*KZb`|4 ZpHDԾ9rvO{6Z47kq1F\É,$ kW;UE:z92 `?l[ϵF:5p'ʻ}~*U\.ti2ߩ$V;eJ[} 6L ĺq6GظKig{#pnn(<G7G%)]9e)W3]+tal{])Ei2W YU ֏Yrm|bep<Gvz" M-DlLyvy S}]9>J75 =ac疆mhOUz(0q3;+j}D\,u0цNqվf Xhksv;\PqX sVegx+9'-wvcK,=_w̜-b~2N2{Wۥg.xlndm˳nk\1 b~m2%S^.st=iҶRv+Te98-Շx+Oq*SZFq^6Râ/ g":vw *ki"xZz#8ָ ˛R9dzBRH5k0s}נV8RÎ"Y%Is^Ƭ]T譍t):qp/ tˑk8gUՊ:ZUF2h== ]aFW zS_?/=>> H ,1 s8ѓk1RCP=c-3rQѝi\]?ix/"兒]95qy!l=zԚ虯ˮU3g:6,w:ܵ>/J_ :Ul|7WKɍONsL=+Y86gsdM3H-sHlr3ؗѡ 6axkx~gq -SX HGfF^ΤݛRVG44Ψdtg<$~c_wN\欨F3?~Oi<ώW.WHf1\}But`xYWYh;ૃ837[L/bVM=q|e)iँ0:c98`hbi:PW.pf˨#PDDFUnD'c>[?ӽüGr>qZ0g^iw!RJm؟6gbQ \5<"kC\sOWVqR{#NN+Zí8#N.1cCXZр}_Qԗ#֧DYɄD@LXckyy͸:;W'۱?BhupMNf45,:^k_u-|=Mu456VqwZgF:+YNI7J$ekW:WgsױutuD4mY.f5A c-y؞: 6k-и1Ůi.֋AO,Ǜ|ٹ׳GvasS_8*WAmەP_%snxRKC+X3wb;uـrSl&ElhkXр9;x8du3KESTՐDE""5VsC)=do-q#z.{%gQlWcHV/Yՙ/m`//Yՙ)T6* ~CGMMX zi֥*jRT2ND_\HLg%eAd1JgI{˽4Vvv n[//A-MKGY\wQxG=G۹{A*z[4S5c׫QfXcdoۓv"fJܐӂ{rSbdjM1ς̜+p$c5aJNL*]qQ"U4lJ̆Ƿ7^1z\KZQk/t5_ *xUb~le449wv|c})GDX" """ """ """ """ """*q_OCklMASѝv<.x=Y 8gJE9g (#1(&#lu*X@{19]S_<_g/U-K2<'?Wqk\x]'~$;ƴrhbu&8U<wd^Uwؿ5Rr[ij.U'Zւ}v |-bD45nfs`ZD(m_ \`-!㌎~gֺ27t4ߚ/=,-3݄D^""" )h* KQM#x i x `-\M]\)Z\.sάU ncڀQi x j<5ϼHi[Es,uZzz%ǸB yv6+?m}49m'Ovhff5'sFVG}sTﶋL Q2Gi$ g8,*"R녯&9\',Φ7b@8*馆_,Hb'ldtNc\C{Fݸ_)(Tڝ gahh4@;+Ucͻk6U[ .z B]4pw#q{T|P6TH4w೴|sbMMU3_OUHDEbp@;":|Σ0H2<lvUm5;9)KRGV@&hkNW:(Ua▾BQI[AT^֘Cyƨy3* z̺3+=!P ߈,o%#:W[缒\tm_Rj6JHدᾗ.RE+8~28Vzi%:ٞICZMvd7ܹВzwqtSA1RVUf9\_' @,-zl:ou y,2Livvrp՞x8{lٮctb"9a3}miMEuy6WΠI͜ %eGYeKy=v-MJmfp4cR]2ZKI`_g5ܽ<=L} k jc@>ϰ/DZ@Y蓇x坜7)1;shBE$:C{0UPSCy5n t/r8+5W+MNK{$o4a">D#XR_촗;{TVA|M;3" """ """ """ """ ""MWpu 7$AVkiYEQM. sFݑڴa+=h\TxPE MESEUbyk\F۝;+Iޯ`EoOu'3U/$Hycl]8vsB&c-%3:syFFgfE ^˥%nK{8# p~bB*w+ :sz?i+KhlU#ś2,.ɔZcZ ܌g|Tj]6~XTeJH+&9@el|5\ՍlS#H;F&h9h2>#::fQ\6)X3w;[]=lBZI?:h)bb "Y |/q͎.;Cl\SYnjk3lq+M눸U[)}En :汻c`6 e.T2>sd9wtYE*->Sm{5Mt. my_/.%{nեkaE+x4u4'Kq$o-LJomE{2.I|w i7p,pRr\\hص?lmeFY"JX9ӿ%N3z8mF;6՚㻝U2|팸;Kn݈ cl(/m ikƖu ݲ+D;w &^յhw J6}\ɿ|B`E$^jYRl!4r٭rT]֚ϯ+>iEwkj4鑙Η} ^/u>| =}|. .ZZ縬}-D쵠wdt}N5ѓ9peCNt-8ے}^ow-k0ihk@*w땮PTQ9z|֋\;]4r!iiw""" tۍU־X\#fqxw"䒒iSzzhTl5qK86nvùJPޮ5S^u 4e#_q=}#li.薪7=-c5G$R87Cq0;Н^G*o? 57+kaceMTR9-Ϛ N˽_j:'味K TU]cCPqw7gcĩ22Nت݀gP:Rb0Ꭺ9qh6;皯7YE|5,6GX[qAU)vj&N02>}gp= -RԈTDKW+uG-cCIs0wԸXxw_A 4BMN8wgn|UfOEN^+_CIZH'#.KV˩qwз;__M]$L9EEDK) iͧ]⶞nVy}};ƇTtŌjp6Jکf2\%|hL{v McΒ7ĬO ɩb&&g12~Sk5sdd=ظ`u²I*k_uH/#}nck{^F7rїԲ:{>coGz=ef\3hyx"]jOUq폮 ,'K$~hqQ#  d7X:LR1؏>EE[ %Yl6PVmh5w}E}^9""" """ """ """ """ =%=A)H;kToХ,IONI"v% Ϲ^D\mYЈ(U=ZZm@:;scBѽCY#iaB>Rū!ѭ!O%wk;k1Y@o0ESᨍD5 -V/54ǫv`|682;\c_ ]n88CG p,m]zԑZcJV?Oj?;8)om[iMHĞҹgCKs265!yW/;!3? 1IG̵k,ul *w+.p~bB| M3\15u 5=LسMpI3`2耂  V,֯'Lt\vKsKCp3_Po+g[G?:δ>eGhXlg=-ۣ?@lGt0׎co jv8`BOLMU 5s\;2p[E:fv\M}R[*|4&jwKO A uK#$H1k[`otkv=+nZ72G^ ?tnk/us6|WTZwGϱ8lQDѤ1.i5 *XF4l7Ϸ<i`8KnM# tm!$cb-'Ti7w  +Ek—1?jޯtϤ.58P,O>K\"܌`4E]atEqkwmNJq&ڭT(`\ؽ's;N<<~GO3*&LcFd{+}SkgrȎㅗl2SV#f/c@k< p4 4ҾL@'jA qzCu;aǙlj].Gba ]{8Gܹ4[qJ}V`rmQ2Ojh3f|1:fpʵlQSK |o*󧉳H+-fw v9IC8Fuhhߵl 4GO#u`}ҟ?;󭓣٣e9x#z`(n4V""" ""F[ւ!|Wvifli5m#ti^J.Y$峴48羹Y>{})b U2\0-w tN6K3Ód{U3[=p:m%๬nĞ]D3Zcl:N|gbQ\#÷)c=pwK\9ˤbuM;$sDh#~+3t.;u-c,H;Vѷ_L^*Ͷ7C dˡ;\  )}cpGֺnk+u5Ms%Ө`KUy8RTfTl/D:3;SW|YAs4.2TpRu:MƠw`ImUqmUu򱠗;p}z*fd$J~F lejTɑ7cOiR#x>Aڠ5;l0B k^|.%oc.H=+ly:sΪiGE[~(kHcɏW~%S\AB"q)v=o#ǩK z.uU.[L_[r/iE֌2\y>ŋc5fsDj6ޫ63U _ɏܱWZ\'V}*1Ah hO+dDv<7JأZs|Εoc#*MһƎR1ZJriZбJvb?r =_hn\"T'qL)J{J{!U&)Z0d8$\xV\"n'h%!Cya#eN$9u\>3G]Ko5:?kP05(ݎM{z{YJn-ca=+%]Um$gEG?֪Jiiw""չ/KwMrm.zYNKwkDxah`57'`ڪ!6GHi$ 1cCX֎@ 5T@^$3A#n-5fMY܈!RIn|pJ (>Hxn6<Ȉ|6[8i:N1v]*:8̻ 8_?V\!AVyB៙w$@sheuLchspq$z=렢 9۫;e8c.Kw7Ìls?g keDr~8Գj<]25pWXD\j 6gO!+~ua[q-uHdc3Ğ˗-" 5k/uskL&+Z~ g̺/Hߵ:ε~ 0.APkoV\4gr1W9qT5Yo3c(nXǴ!VjA -8qO\rCV.9}QzȀ([|J)j1⥢^rk,|K 8dѷ!GJ6@,8u83n@vmƵեj98zpLg[KQ['8I`ڶ.u7+Zc-ES$o!1WŴq 2RGWH}tvO<=f@q#MuK:YZ ^jnݻcoR${]V;SZK3{ksCZ H_#c#`dmk982U['VOPТ Q4cuHX@q͠s'Vm0UJ|Fgw%KZjRTHe7\F|uI#d ׀r5 ?I6&Ҳ&s{sRH6]G\EY%T$4 pχڑD@DDD@QQ uI F=!yvk}Z?Iñݫԫth4hk>S !u1pn*Kͩ xk$}LCiq>!EQy}H8nn|5E] 4\ cMeʮ[3!edm4'2ju8 |%WJ9x:$}ȦѝR1ΛIf_t4U%M)dV\Ivc}z-3 qEQLj.&]lsJ}5k#vF$昶K1V/uXZf3gvv vJYo|ΗR1$,ir&vuop hr,UYW _tevs ,GIWkk1yKjQ2$: 70WۼZHq-7J|q{6CD dL׏ۅUqu=,mM^tw pl^  -NqT|2OA&6GL1N*Ӗ1#z?͖H&@;*)[Yƌ80&%wi~#MK%O,R0:_4Hͻ}gVޟGKPG3L@=rHR1,i%E'qIG -A^ȩ@[oAte{gpݺ/֓{*XGgFGgQ"S᳁]*;WtqlL= ӯtJ#f\ፎo 38 &G#nՄ)+Hc'l4Z ^a$9uc9WX8U8# Ag'4 ubk<;JAc^9t$@T P9iLpMNյǪ'D쨂9ptohsHy:OmR0^/gܪ2:eJCdE|V$1XĥFYe Rᱫ\cU5Q(FeF%[Zq.P­X jW ЮX/B - *G 4HW0GD\X]LдX=XWaTYL~ R0ar";&ܥT ;)4R<DRXS}?ta[|AXU(Wӎa 4஌% a 4U஌e ùfL}?|CG|^ ȪHþG}7>%a+"9%~ ƔY;t [ V}fqٓxJ3a'ڳt]$@'~Cࣾ.Z. QY[X)3\|> 2OJiÑcqQxh8zKfct$ܭFCpGΪS}.-ۼȰ6/ D5I| A tucQUQ$WZ)c]-|9H)r5k;8x~rNںY t0 l9HsKerq<}ӚRvRxE?dOtZAQ;eS{IqHJҩ;lA\n=Xinj؂2n+"R >v;ۅ8KR Gjs6geuV}77,7Z k l,UANQSنVgQUQ╡p9ˏ#fK=Kɖ΋=.bΔřJlGS4SNQcr²JKBqdXU@ULXE bg*WdY UJP+T%E/U(§ S;r**gnRaTc*KUKXW- U$.@Z-WT%ÛeK![sTўp 5bH.j+c#0G%Dt綇CקzuR/ެ[MViL{@ſ:NVb;C)^yjSRQa I}Sq<=ojh M9bF@܉999+X\Z?`06[㋠ƯGu+g~IS>TZc`eTfs {Ak;m*8h.uw8, o:ckatݝ{sbqՕҞݯ>6VOTVˬț#0K$QJruS7>hr [*Wa=SH3ه'q2GJq]};:1RPS"|l?ҟ+{ϣ[2x1OUQ39ъq2{~dUn5׸$FxI%Yweox[>MdM- :5'nO28rӡ|LqZ ҕIRɪIP%<')񲷼|J״{OOEy[)񶷼|Jt{졙y|m?ҟk{wAM<[)񶷼|Jt{졙y|m?ҟk{wAM<[)񶷼|Jt{졙y|m?҇k{wAM<[)񶷼|J״Sp߼~6OVOe{g񶷼|J|m?ҵ%4x{+3>6zN%VUCML,=ƂKN$[muLn#8,v="Z~=pSzչL+$yZqe⣒RJKۆMՖk~Sږ>Wv5= %]lK9F2a;z8 `8ً.t=sVf۩iadpa%^El{.wVŴSN7@ȆdC@J䨶;e,tQ1Ϩsxt g ;DA+?Jָ~yP1 3Qf=o $F0AUnP?Jf@k69K{pvx?2@?ck.3>E4Z^ODo uг5Q ;j٠t[tx.F|tm^7^UxUVɿ3v\XI3*D@DD{HkDu,Èf{J/WH,sPHa-3.^ؘ96Up'RȪ/}OԹa-i畛-ܟ[P!Z-?YwypfW|ѤYnV;-u\UUU[>l}Ntp'p f?T?fdʦ="p $d0yRCa}Ko=\/.<';mo @t7˚;GgX.٦L?}Ÿ~ :W@%qf75ͭ .k[悒=MԬtuCCCFܷ0HedŮ-JW/4ZaWBYS^v*dh1cqvPHsVO-5rS\ Lv[+>=՝y.v[Np@.Y J23w{5(`v`{OxwNZ*j:,43:qr:NSw%|Jfu NRT(nP¯4r³J|*´Үq7Eh0bЉKT+Y UAPVs8ʗ/\I D/TJJWQ_iDM . $.%+*RFy"˂+N ijD#TYHYB,$D(2@H"$GxR 3ֈQޯL/QޯXyVĦE/W=]9\rEleʲEL8+*ӕfA0M9S:8Yd6z_ n'tim OKTrX Nժ8a&d0JKAMŮ-$e#ohARW5=&sFN_8ԯF5q82{Sz@0մXoo{5.0Aj1?$EYa5 KfSќo]/ThO l+/, ӫ*׾y>#{e`WLg3nu8aSm cmmtM ( iiםnۖ[?[[0f% 6O~ ?2 ,v;`d0}`JhEO ص[{8Trg۹_TZe)pyD. 0v;0U\)^-kv <;gsvF8ACJ?oYk)ۛ6VQn{ota_AOJK,+3UU[]5L2K+O b@td;_n ֑%޾X^Yl6?TZPHlj8tF\ְh)yE]r|v_=7j|U~F]8Z;X84lO27p%8^|###7:ibt HSv[~SSM^7tOoZ؟JCtoZFxp>2h5 Q<-TZk,_g|')|Bq Oa|E좥ËZHˁZy{mpRe6*3Z(TRJ 'ݣSyOMN5zƢgHliVœJ{Mzxm#C4Qd3ʏ?Oy;MN5zv7 Xm2g1i8nڝ*cTTՖ#*+COYB^?qC}7jP^6[@paqV2 mG`I5ph7?hy#2vT_:z˾QKyE/ ՓY顆I_v`4.CEAfڨd10`G-h8|QWt_ʞQKyE/ Z%d2Aa 1i r=W"Yek-xzGk.lw5kð=s-Ij[,y:|Ӟ9.%N/O-q~ɉMC~b48Lz34y ^!UqRVGXi%-x~g~UMU5FB_O]O,ZC#:Az+KG=_Z?UM:o]7˗bQS?SS;R8>'aq4%NYe⹧=ӅV׎"h5K2 $ՌXǓ-Ifu 5\&}It쑱N=9 xQ hSVEMѝZkm2i*hVr d,|p>>ZPk^I;K*=#pQ-FMR r1٨Ϣ|uKy~hCuJa裯[g\A?0PL""" -ڊyayI%SIip`I.Cpmtl)*mrLЫ*!uU:@M5dsN6:N #n%z2Mmd/~L7;\>IZA8 h18edXIls>&Ŧ9\֟ 80;˕AdYcXoKu|UwU,27nTާ,p1ҭO 6*XAO5C [ўָ.X+cz^\{ DTLT3'$ 㵹Sv8iUFYmϤhZ̮95%T-Îf#4v\vqM>團2$fk8KeP4)ƑY ~ SnoEuh:Yuwwm;=+>F7VpU|4*9piH;^ŲJž6*iqo)nvASw K7h* %Sռsk[m.rbWHey2D|=N%m;VQtArCJ[TNܴԀ qmh_\oZCjp&n]W+岎VRAYBYli~H't` Jbϊ3+14jIIXtp xR WŎ<1{V}'*`)sMqRmE$m~ǛZI`-{5-gD^ ӱ7dq=4;=~դm' fKCYAKYV &|w6 :̼ȩ^dmk:Kzbn"Ԃ:(׷487}ZO|6,\k%DlwUk ~u ң/ RM$雬㤑;_qOEc DTXk:[+ˤW2O`+hˣٰh CN٦d\UŘvq wV/FxRTV3G*v8Wd%VuhLtKĶ;%tDPohX`UJM)+3)& -يv=-#={35>O6}DؚOF2pJcXޥE"u r(Lr,ҪLc֕WQ=*UI +7Bw+E@_W}_U+2`j2,Wª_W%|*q:R*D"*΅IU/2ۂU+nRFyە+Zr2YU1ȋ'j*VGEH%-<주vG{(mGyW$v;ܯT^Tw[ܬ9򬼯r+Vώ*ӊ9۫NriiV $*b$NQvīU<541%UYcF:pήC#lyD1Q8w^;_e3tҚWMxxd/nKVKM4`S~Z@wrq,6ں;%{{w|)*%'2ۅ+&܅|qKT V5dxvvgmwYY ]+yT`sYST'Ryrs(8Vک1l Iz@|qmlwʥ`,gwiʫMWsKµT6WB O9`627~|R7Bǵ-F݆tMCx8etqyf+Aư[7taY-:Y$xIy9o/JJievBt&Է6~)['Q|Ci.i;yB* uAnH,L:ǘۗ7K;_>W?T;|K:59(/64 =hiim4Mc\oy gwSR5.9pS^bk|.!qv\rM:*˃cTi8~Jձ0CF4hw0rxo=>8$k͜㡊W/H*.N= wƦ<{mR|꩎ؘ0i+uʬwiB*+..7k򺌴<"Wsu,w M:UI-E,8}nsܒHܬ "615hcqKN0Z[MNosLυ&fDdˤ8 2G!{<M_Iղz1Ē˴̞xϚ{w|fGZ-ﭬs6Cfi FرI8夝qwV Yd ,2?<@FZF9ܵvhkVT9 3@א<2O3WS~%sYTl68j=մ 1QzȮmy}jp}n4Եf<&8,{T[5&[56G1厚j3\ד2TkJt!oյf~#psE?G,rȡ9;M)%ͻUSTIcFdl=lҵ'99>b˺d,31cd1Mk$Z܏ܮ|7)+")+g%t@E?T;_2H]O;_>W?LDSW?OS$h7.wкw(?Yw X?>(џs<# Ƹ_эv>EW{%Ef~Eb* D@DDk="[]S$vvlʙ#s2;\j$+nCQ 2+e/;]~eESW9J%asHs-fSN$, nOW~ yE<qA˚ܜN bkC2JfnöEDIiu֞W>6^9y53Sl3eq'q݌H֪N8)]-X^w`rpLTq\ut2tvC0vʇ(x{8R2K]nῄ[·YiwipqbMSJ}J:KKVe Uec-dr{3C<Êج<=u%j LTSM$f-kpISGqWu ԓFG rqd3g@ ;~xffVjb_)GNƀZIk}8arVEu%>[Sn2.RWuD4a~K;Dq5  E,=d5:֟8 IwesZG徱+,;.O(aEQpJ d\5z3̓+/ *["ʙ[qDJVϿ߸sN.̋$4* ҴŐeU`M*U)IK9!<;;Gez_u =\1L= qe?3Ryc'w,Yr<'G4U:r)Qȱ1ʥG*@"&9|TXe9V9]cʐǂ*ֹ^k&=^cy#ѥT *;\*Y3q!ʥV.vЪ t&p U ]`J|\%|_W²>"T3|+TAK+VܤS2Vޫ*QeIIPw5| R#TINPz: p.Y];qO'ieGUS^w5zTjb/psCAir+MD@DDD@DDD@DDsr. tϺ~ȸa5揊)z?Ǽ15OQhzz(_{,X"DDD@U-=t@8i'$Mr\kQEdqUE_ \9-dGٷjSo+%DÀ7\2Prp^VLc/Fq2{w8nу% Z$13SƜgV-g-#8AvRYTsryen{pۅt}%Զr!=dE(KAupK/P^@nڟ7G q.cXg8-  GHTQZdWQL $aD4$ewΤir6Nh~Jn|LUu핤9Įy =IAv#*Zxx~jvK^6u*e.cw5frVi8CiU;N,naP7Uav*Jݕ֕|Y_iWUq\J%{ bAóaUj䢤^5S&<4T\[X{7=^^2קN,tj8>Fr9TX8RY?8RkGQ3`o&9AXEeVp+nrbG wlU=D)w3} C<ի C<"gRH""P""QA-׆w1MO$49-DN&!Y @m̶q1VJ"Da b8>nQqv4ۍ9aJ*[‹aZu5F0 BѷS:=9GϑѤQTg7ݓӏܽjUF2w`}F=QwIMd7COrRw7;kMk#$01:7qMe>5C{JDWұV9- l:3h͵b?oWk7n0a1TUS\-=dF8.mjzHV<ꝍZZavڛGTwCMn-n&/-c{w&oKQEԶF59uxT޺c&_$э84c^vlO` nn 6XUr^ fU*"jgfa,{ݾֆ`eo71q=W>VUjNc\Qw).R)ƽF+FA HGrv#qI?=g t45CqpHn5JQ#afzSc}GZJTUbC摵-8 'sŗ#UHWXk('.tn,M8/r(K$̳bI93ʒ̱UFi Xܪj*9Ou2PS$I$ZeRH=ȣ*f.=;V*&]{՗ȭ>_a-Du+.Zt˥W$WbV]&UHEtQ󞭹ˤV"Dx[sգ&UĆ[AzaD@ys(`eAIsI>9jdemcidcZƫG^2p-km$fdn*  NFV9548-o4[\*`pvtvD,'[hO/ϡG}jBT͏\+|^Ꙩͤ=YU89|[#[i$gk] a^Oc]9WD@DDD@DDD@\܋?]sr. m|q} rv>E8ck*q^}IJ:ŗK/=$tD@DDD@vNqdsW7T[ƍ$k\34󅀪>OWFu66T\Z{"J6ΦV7fnv\z)/UJ\KvߙڼxJyW3[OmEUYk{Ce=GGF*{<'Id4qURa9s?l=֙KW%GqiA'1}gTVs;? -"SUjS;[O9+<9Ek+D32S+gk$Η8` reB|5TQSgNq'fX-VSzs6FG1SV8~p|i.T+{c:nG`#}]AQ]--:,ꏊ/(sa` =vV*4AN `(KK>W `N!F,q|O[iZ >K+)Hmr'y1Nc47_\ ʊwRq5-\o,Z#g外exk,.:zʊI޶'ӵ =u灟Q,|"S*"}A^đ͐{4{nq\xCdcxvd{ݳt]{W<ڬw^5 ^mX'˫ZKpY$}+𭝔P:g<QSA=53;ھ[}uxD̎:lᥚ4='ݕ48̍+c)m^U^5j.'[G vYJ)8bT%[erX YqyiX  T\o6<|xdxyQkO0K gU#cG0]yGG,<¸Ҳ\cF2whi91 ½x2_ji\i+ejWЭDK*U֕lY^iWUUƕrdKY]92 ־ҮJ*iŜT*4TO⴮yMDfHߴ-x 8t$'W:!EFr:*:d*Lu+4| ***EP1fM16#&jbmz%d[]X8.c;z{XyXQa)xPƟ g;Aj.-niH^. D@DDsr. tϺ~ȸa5揊)z?Ǽ15OQhzz(_{,X"DDD@DD6=4Rْ0cTc{v1i簌4ʨ͂R,G&|rr0@촇A)4{CݻoH;vfij(q}=-VRǻk ;AkEg6TSJG%WphSyc` wΜrI*p|T7|3UVZ*"tLFK]oJ2t}t{Cyvv =e uV1{9hv兼qw G[g8*ց$? 0zj !ijJZFή־L !ɺKvF8<\[o6kMOh^nAQ23ȥe_\ /4  p8z˜Wf6>XAõk{WKR_(勫%2cOU`y7Qñg'eÁ;$SUI^jK n_+4nPm>hHg3"8tڰ GgV2{B᮱UU5k8FzL1W#{B]<7G}x.1JLQ  ѱ pz,* =4";.gP =#*w xڻ6;MP:X+!Fs߻g8jyT*<+\ d'>/V]?Nt*P7fZ#[2UA_(ȩ/Qz"J SG֚,TJδԦM@|ԭJ5YJWTlFGA<]t̽jo^,xo.""O6tfuS21M c9P]kk~ӈ, %̞X\#5ty #ifHPݔ9KSFi+X+x"\氀sVkp j:8j榪`ӫT"rWV<]A&Gۦ+-_^yoGb{0/M&>hg@\NTHQL8.A'Gr2Cl\CAru210;ZH^ݠFkr+%US"WFKRΖF{z 9jqelfȼB*z3RZXv:J7M#UoD7[{%+ؠ<*UD!}Tv TunZJ'x3stq:X*`/G==kx}i6HܗB6Fl m|q} rv>E8ck*q^}IJ:ŗK/=$tD@DDD@Z?I$W@u/$~x[‰vbq7|og*:Gְ5Ƣ6h=$jþZzY!nY(FrsNAnX&Rm7PpnXu%)Q[3Y^ѹZO Z๸9jyeGp7 yl1V/WWRpYTI#{NXC3cCC*:9Inëګ^:Z$/<ܐoB8v/YG]keEwJ*$>' 9/$47$;- wQp=6 Mh!,],z{* 0Z=Yq]M.!slNpX^[8Y=Ξ j:]NxěZI9z g Hvd4U4rS:7N;ݠ-Gޮ2kwT,SNS\^Zr0zqx:itClQ Y!!G,niSD~QR=:<<`N)FI;qk7zʧ yF^ ҮYjզ *++>PTM3ƕyGu[qUa\iWE$R2FscZU6gBװ *7Wn _'ݏOq[p9.,]'[畢oW3G2* W 3yt#&7Xj×*K+rfl_F&JqC}Ұʂ/7*lCk[U07)~Kð&5mŇΊTCg::w!hʒޚ,.gI9?Wy'Z;q?4UQqIz|?o՗CbtÓ}9=D󌅷/V=)Z=y$|9pA O_] tEE3AKybGh Sܥ![ݏ&IF2p:mǬpsY3VI*Ӧd#+%W%WžXuONr2VQⱦr{U_l:l̡浦4tMȣkMJJ#MjS*j{ZjV5Y +i*X2} U, 6>[`@*V%nA<֫A/J)^_=޵7xђ =" o j鴸D]nG`m0'M6cQEIt0_bÙaݾz}Ir8l%ctK慞o4-.SgysH!TeO[ ue82N~^hh۹sv⎧sSV+D7(FLٕ̔仇BP~ <$d{OԸJźEW+m:}DlWZwYӨ326^^t{oEN+OCGXc؉eDZ" """ !ۼ5sϟϟx\ⲑΝе- VZ& t;3r 4- 898;m5*=D>cd:7g*Z T_bmW]!DA@OX$&wU='{K[S,/s&{IR!fN3tnm5SF#x 3UоOgTE,qpwa$dL<++g7Xey{kƎ}jT~ W]rdVʘ{ZYOzdo5+Eƺ,*jy96CW,?8qsqDlA1UǑAPOղ \@|AKZabteQY L=hs |n؍K/VqG-s-;4zL4s+Z&{TbۏHgLgG9! z#$ 0t7oZm1TR%0$v8^^PK8"krzxZgpܭ+mSRU7ItxRG/͛Zq2:k]LfVp8q:|0WUcADDK'#˞FNZxOO9z5cQع3缹8V1fmT dPeChsW[.^U AT} h+U4B)pҮ4rҭ8_iWqV%1;-xZ*]獚y0=TKNA]j:izj - q?zDr:74ī'i"IzY.T!zZ.TQ.*KIrP.K  %r\Kʒl|.BJ%[._5!,uʪP'<vwxC4GUv>#rBT}c"Aiwм#áiyg5ʌuX:{}]lw _g46ə* QZ\NSp %Dtr(`KN$a$[eTi`Z%)̤ʶ-؏DWTPWFތ+* 5Z}1V)U UTX5VVpTrX A|2,^/Q8ĵ`kiP=XՉ qϢnX33frVJSZPr"U[/t{{㊍cUU+ .@]-ao\#WYAc kF q9OHaTu8hW{}#Qt˜O6I^\:oG4˽(f@UOAk""jvʪ) 7FH܎~k7jY)[$n-p!zqn-Kx&[2Hc[+L6ڝ'u~o5CFy_![&wsǐp(̘dj3_̳AhF f 9P}Gzۅd*i*c_CDJaMNKҮ/NTѿ屑KQ+Zq+upX&VR[ԝ#vQYfw1֡QӕOScL1䬭ISκ7E;Kάx.8k*ާ#ddd*p?R0X/V.R˃j(p acY/*'4Ys{A<I)I4IvH}!ytOoRZTGoN(-_Ui+\8Yv$Դʚښjsϰ~emhnΟ]flgI!3Տk=)Oksd3ܮ08+ۓ_+bk dm.q>-߅z5n'&mw0FN`ήN8I=3EuH;\ѽ&lHe#imcֺt=ZFwgADDsr> tϺ~a5揊)z?Ǽ15OQhzz(_{,X"DDD@DD$cdc=q1:f9n;Zw K%+j̹knuG&:z({uF;.jfX#w0p#[5}*OGw3Xii$s㕎."FN*ziul:jZ+}nRAi_8am7 kXImO]dEQjoK:&`p}X }m}S&}m3N 'A֞զ,w[mWo5[J]#AxϰJj"㭹pV@Xw؋7Mffֳw)nvhXy}U#;j8V_}^8Z{akfu,sdlkw+QY{pele9jczm'-\T{\ pj' I|[Cuh'ဃu\?Ah|f;Ҥ2ió0@=ī A롬tŵSrm\d4Q6vnㅪ4*^#ecA| gЮ5PXVD* DR/X4SJg mrI/*QÕajG,_}ʴN]h R9w`P)cA`FAȩ D}R'6+le.F#tw`=kRҾ]ԇSmWyJzt/$Y~zV֬];6gU9zV{BvGκPn0v}  L:c\U\LgT1w#:AS象[u\z ]%r>y{3ut1TF;"nŠ@t5TQG,aTUaTp1TU)H叁XRPG,VUa:+$2܅0\G2„gVSQ# *0;TikTOuR3j-h sVqwF44n5V[9U+rѹqfxS8k,_ 83i^v{v%bڻ%%,p6 Hc54`^E~$M0å?c⹾v N]ڻ}%= ;`(!n4`Wy)ɚTv)zz%dP:qz$鿙.-t̏iz^c8̘_J 5D@DDk="MۅXgu$d6HϷ fEƮ^WƗWU63AE:rg*ی9h^{*-33 ӵHqkv g=|M-5ALAݙY m Z-VC|d4{ѷY!dvI RMNKKOG艁>"jx׆zV袴=`]zۓ4 Ӹo6[kķɎ1N1q{$Y*ckO5N(p,J CHA8&C8w$}emY`DEDDϺ~a5W>"?>((c?\s9F K!裬Y~Dc؊GHDDD@DDD@{83Z]X&Ld=9Tk#nC27V]gqWҹTVCgXDA."w޼ֲ3z}KB(R'k!0TmdVtsN%й88wk{{VjKSO,N75 o Qu{$CX_qWо/h+j*BrV ARЬDJ}ʒg |L\X*qb*ALU}J໕Pr %#/*UAJBPrPrU*ԔQ K8 P*8zuMFWT}kK:$*/gt,J>*7XbqbVZx["&kMj[uң&_:3RwZV<⭺E܆Ht+i>*b*i>+g%}QX㢷eyJRk}5EUClPF^uڸ=]EW2 })8'퇇l?ma1z3YUUZGBM-:-^,_kv}`",r&".Y蕐zz%dL?I2?]zW2a}*"/$DDEWGw; splz?T{KF<k6_#,ޑguI %rƷ*.q.sI'$u^*J*'sPG]N6TUFY.#v rh迡} =k* {rcis@nMCc_ۛ% Ov` 6SVb:P;!r^WJ:IH9+r9,rXNL*(a+t?ggW4ZJÐA^G zܵ.E$Tx8; ס)*zvቁ`ց=U wbIl\DEDDD@DDD@>"A\܋?[xo(hGs<{]q|'W{,eDZb* !D@DDD@DD=8;WG #,vH,=c7斝+Mi$Աbf '0Zu8YrN6MIeF =Xz$ޡ$`HMۜ[I82F. rv9۞B׮tqZ״OYQedO#9ֵ݄elj+e6( *YGxi |p@|e,{CpuVɖgf2 ɪlŲ_v[)*+&;h2.}uE6J0G.m l-GMKF 'Ms8O se ]A0y<^ q.-c[IFƨefQeYdR""DDD@K>+ J'MwhdLdU?7D^I""" l4k@4rZƐ~Vjkkel4idw&18 _\Ev. WL_< % O4WQؙV qhB,fd-0pˁ۵shoEqE\%KA&phi{̜s<{Z8IR ٖUT3_*$n߳ Fx)5=ar>T|c(RP=TI+@P Ny椛'SR8+Y<5R%DFqVi%ˣCowڒ)ZӥINr8z76s5F9Xa{CedFG09RؑD]D@DDD@\܋?]sr. m|q} rv>E8ck*q^}IJ:ŗK/=$tD@DDD@DD@OKs |tPotb4'o*C<.c15yo+)#YW,P5,׆#kl˃_Apv5HsZ[j)ek:X,h=R=~{Bq]]-(f0sFD"k3̆4g\,M]QnmHR6Cj/z<9 rE*@v8ϡT EBH+W̯叹_2&.vKU8_p>_ra3 }ʡ0sRZ2Ws u&k%7LKr],^Ԛ՝8{RkVrWŋZwZ+k3biQLYTsWvx%aq31bE2Vi.)hhi:-=MYasMd{U%LѶDtJͅ[n RXs8>S[jISg<2&>GMcrO-^|VXN5 EghM>AlC#wY,c_D>6| u޾: Nch.a<@X9r[$[僽wuI˭?J 2϶W2i?3&ҩH""MaD@Ϳd5z-uaBx6@Z>n} ^(' ڣVޣy#^ 43=W5O7|27{G%-:f.-uru]:ر^-gZj9Z$pU9TxSuUUsSF_ۊV8:w68ǐd*%z#cυ]ŗS[\l-g@}z666555wni-Xo=䜒yIRֈEY D" """ ""?~|A0+tko SS~xck.9]S~%Q,X"Y~EA#""" """ ""qSd pw;t{tY)dT#cV{y;ש]ZxuK͊:p)7ˍILB;=G Q스 |E]8} AM",Ћ8va e-?Ҳ#y(ʤ#5&n]ԳHgGW':zvY* ְuSJ:2sG.w 1esa0cz->uŘ4=g3#3~'0hG:\x+̎IURpR#yCό/E̎1vtbg O%IgXr%ɝZ)T9ދIZ \xoTyDc0pخr2cnosEg*e$;qំG஀GGu/iqx[F|(#5 ?Ӑ?̽l&Cցa=]>p~JF0gC[]pyRZ#s̮h'vN .1+S4 rd#Pjה,RkCTqpնi W&Cdqy._Eo/Y8AآR[`dS Ώx `;S&dVT0D h^}Jڄcd<".,x9n:SN;k. Gwvek3?jՍv=fL-pldt/=Q=XȦu@1^q*2,~fj଍>ϽainG$z _ RTS$wѾ5; *T讥>IGcK ÈՆFMUv|U4TR KCt8푃A4tE5pc-.-'*}E!XY3u6F w~ӆ-+ 49geMǤhEdtfkFø|VrkS&AA$oΦdQ{hdBbwTܰAFހ_xښy$20O944hԪN-z}$wY+XFjb-ՖIVaSCLi#$XI#I8ю@R+41O[̚Frt7<0>bU(U4V|(Ue̫0PTʼ6@s#SHLACR_ڜy?3&ҩH""MaD@G4*I0P0;2~w%{Zg[MLWڰ<љ^?6Q՞,FƬ(ЦmD**y6fc$)"|ko,uS|mM#9s(`vDqE]bI7|bok^kB$K5Ҽvb-EVHX5An3-goQ " """ """ tqk}Eѭ74|QN#O=Qh rv>EN+OCGXc؉eDZ" """ ""?j^+63h%Gܯ|7G.h[P#!=y2q]j5/ҼTPNU$/"RU@&@.p 8}}R̀2IگR_W-nyKRj+yS.tl,i8=Ejga|=Nps[fG#9 gԬDB:y] `MT5h=4;uSl 4 `bL&6^OR<4~ O-$v84 87lS #KIn$7 Əu 8F1KZj.qӶL)bS''7F7ql:Y&A$42I_K$"26SؚX416} QcKKHdokYlP[Kg3*jTM!{dF3>mTƣL;hkrFpXR(拪n|Y·F3{QcGQi3o;{9UJJs_:3gNLQ|~韡:3g@}E?~~韡ήL?~_:3gNL*k?~?RgЀ"/B/=pT5Xi?3&ҩH""MaD@nETT?]iZ?+?ty7S]l.m1gi$+5rSVOQac{;QI%"?>((c?\s9F K!裬Y~Dc؊GHDDD@DDD@^O|bk/|/I!_P8/<^7 pX@"|vi_E]t9ҿh&/E"/ 8}}}T#X_@_y)XJF+cq|9$5$te,WY;k6+(<-[ꖃpT'Vؖj{(b=ԭ*s+ԖƮ65 vƮ65!4rvƮ65|F L2ab13Y UiWtS0YҾiWW)dCB3XpV+/KQ,G()$$ w~@U=w ;y4v1hÅW*H ?9x^h h cxvMbSq7wcq[aBy)gV.?JV4]'G~gKT$HƵp$1H2sysWzڞT Q -$PSDȡkƆPK-ƕ9ƦH` c\ky>3hLQ\bs1OX]!yӘ;ugD MRy*G$9drZt6gTyl|{\ֆ $ ˳rHN-B.-T"-T"-T"-T"-T"-T"Pj95ܔ*El$JI$CeETy?3&ҩH""MaD@XxPY3@88z0B" """ """ ""?~\A0}+tqko SS~xck.=kp*q^}IJ:_lX"DDD@DDEbW  u/ _T6Q;AQ^Zl"q-GqRDґsc4bBC,lQ'v/}¸H hUDX$ mxK)NǏ;q^4 "PfKsjeİ03H}5,'?)mNBH`U+V"Yjv5*q*F,Ta*W|G QhC<ag5F,Y UUAŠ ż/W_]-* -9[rij'lYzy 4±@ (5`vy9U\SjdxkrI8wQ_D©QG2G2y̯H\dr2qRbsA2is]9zp/;lWlLqS/eۛ?uh~bOe}?sM,_IS_`">)ELD'C>5päEYrPGPqn~ƞ/&'aYd d?][L%nW.aݻש.!N`RqI^Z.ʃn2jJO(#$I5 #V)G젥g%\FMD-) p`xoKKrJ??ה!.Sb.ӯtU=_CGE[hO-?26]SӯtU=_C?GE[hO-?3|;䧕ῧ_/{oܾƎEE[h^f1%> wO+N_QT߹}Lom?ƎcJ|Wtr:/Comy|1%< :}GES~=34t_ſ*/C7)cJy^uzgh迋i4t_ſoS|7Omh迋k;)xoK:*/-?י wOSӯtU=_C?GE[hO-?3|;䧕ῧ_/{oܾƎGE[h^f1%> wO+N_QT߹}Lom?ƎcJ|Wtr:/Comy|1%< :}GES~=34t_ſ*/C7)cJy^uzgh迋i4t_ſoS|7Omhk;䯢Wtr3?,OqcVWi}s\)gY*+s5N>)NYnR29'7gؾW!{<(As n˪p1nR}$7sBVV:5}C؊Q*wb+#di3-YjKWkC^#u2K`8Hwn<[o?wj^a^).ղʌTiv ?TvTh.T5z5kk|BZm0v7Qv; g Vإ:񍺆v1Ia.Ƒ[u |nn69]3 WW4LUK tPX\1!3n?i 2HLvGӀp~Bz ]>E_rVڛu gY$O'[$\ik{9&[p%[,uy,Zw"d7M_)>BY [/wb|!Iv $ӂAO/+Of 08KvZfmFp?Vba1q@qAs7-?N1+FҹJԿO*ۨuePًUקe`@i1X 8xuk-b8xc@qp8`֌U} }icU>&TZt5Oq&C9w,qL5۳:!ތOJF>.ϊ5_wнwoS\"slwq`x.ݬRMQaf^FG W瘾(~EB|Q_{TTsuTvQ95iRxQ]LRa挰.i+.}W]/QmʹEtXmfBt05ź{G>c`nc 8ۻ ŏ,Q_U} 6.4VȢ}+]ܷv"-7b^$ yf#{H\XȻO5_wнj)a}Dl@ɮ*pj?].,y}W]'Ȼ^he4b <:G8q%">qbķ)*}S7.,y}W]'Ȼ^Z) 4cتke.'suv7 W<T>IuICW,y q5p.TS;޽-üCa 3-u=eTT~ZyJX$XFѾ!t^]Rǜŏ?j~[Q[aUx>/.8xc_~/2O 8>d~G̻'7x'<?!MG  4MVbiM歊oэi,4[D@DDD@DDw\ɡ/^GM3qq>%z `qOi ~UPw0֞b BѿYLޠ -v++/ ȣ8P〻\3W]'\Occ鿵v.=3USGjk$x3HѳbUGcmшiO8/$:( Ũ}CI%SW.@ts_,\WPAXsR&bsRZLRV2S)utY"B`9Vp^ZԚn Jr\ ʂArfnr*iKY{/zG}Ei9.Q<&|e]>~(7,zGf{d[7Eh <M>^2꘣d128ցoTU$HD@ܑ+IId=dPr__ uT44S?E< t;dV~/R>YFΣT/zG̍MqPM$ΊI۪6Os ׀sK+i׹P9%"@CH!j4Ime]dRe1hK0縱kZPʞKk(nUdnlk#x9qח+==69_,|ΗK^82Z6ܸUo{yĺLև4;%n C ͗GTOױ2^C3p;*j.tPR1#b{9rVtەek*bC7Q1`1 #\#呕xIj))aA,do6MO8j[ѼWN#:(:xWaÌG ip{\jљ/QA<(pβVL @f;Ө ;ݶqRM k 0Amh68#a8ʡxEcn$vyD5;+ac"y@>8Ѳ1Rǀր@$n"n&n"n&pUn#*$$AT 5Hëۻ:lc+SGKs.V7@rN,vrfKlᘁc;sKFw謕 #HO0da&+W]zm_h*YNӳa˲N},v-3 EI6^3م\醴ڨa:I'OGXwJhgE488c'f̭yR;4 =QƓPXƅ^X}hLe\kU>+R8|*ԎWp >^ iUV%렮o rǦ_y YWQk[a*6Gzs3;MT3x3d=nwYc"\mB\.$Ҩ-LwAr4#)e&}U(ʰ#RjV5!zX\\+%˥<+e۞r=Y{z.THI"$J'.&cf*Xuu1SS1+ƌ8̇Y8yy7KeSP#v|I`z76pE(c4v4ն-DEa " H䀃Y蕌$ҲuXOO( ~/1C便'HPC)(h$dm#S4a1 vcy}!p%:7SL3ܗ+$F `HƗ K]y.$TyCII_uZ( eGYᶾgP0G5 -Þfr@2Yg^n6uSeqeC<;~3ٜMA-H2bg?7wM ylꑱ9ݮ'ld|C;^j-c⍛:7 kfUy4X dÆ{ !!eYS`Z9gK3HS[m3)cZƴ hdEj5;$xZ3 `JE#Ʒ0mڰn}i4Qs>l-B\}Cqks47S" """ """ "Kq=<Kp]<ҫ9b0<.#ػ @o# yaPh};@`(ʺ~9&&V~vz95[mI-=F=U9=` =n3c}+źL91͏WsC?:qMp! :VY]Ora-MLlsM`4c޷[Y/ITk#oYTYx&eD!`-k<¿^NAw(|ٟȍukch dMu\$~)PQ2O%0jv:hi|wGY#-]WStAlJ:xᒪC,hֹc$kTEuVmVܢnz^IN\$IdXՊ$[>Uπw]{};~1Wuni2=DK w\36W'޽ƵkXִ`0ZiڒDWD@DDܐk=dVN+II]%n2]c)%853$nkKNO2^c9YzK`8"-t5VAU-FoA#\Aixps pe2Yu-C[A'|Gj-5ҲJ3~Lvr )Y eH{̽9(tlehU]-`{wTbH`vt=Ox)r|&9(W(Z[M$=:84 7#gS8?R0wt֌F<+H;J颈iϙ?nH/`N@2\ઊZ^_(pu=G[O-<Ʀ9bF;;\欖zO"&9%.#9 !BwHjj޸Npx괜Գw9@e~Jda5 Ú3 2gf;18`a)#472W߫TInHce>lmOEUMLPDȘ5F2]I=aѶY){kdik.fw >jml,,k.ss!1o9pSJJ jj茱?7F6꺞z$R2չΌ3 $8s}%e=K])F׃t0gpv<{Ti{꿭K垖jP2&\8;3|t^R|KUj_RWS|^R|KUj_RWS|^R|KUj_RWS|^R|KUj_RWS|^R|KUj_RWS|^R|KUj_RWS|^R|KUj_RWS|^RSjmS(4>S 0_d? RQ}tsL+?K;=KΗ'٭²?*q2hǃIO"^[ZX٪Jj+2䑾猌.2SI##FZ}u" Xhc=Echh9'Md. ֽ!su 2h殻ceR*q-4=,6c՗I#N?9=h*v~hn9b]r{WYĈ/zj7+WԊHLUk486UQ[8tmd2<46j|'qxf .7ښ9B Mi hDl|llVp}DRGY_LUK:d4`d*q8]fILhqiT~# $`uv..JV;C ?Ei-{22rHQY52 *Xѫ[gCM[Wpcwx[Ըt4`KF>(!}*Q#H ~80 8<l M <=XN/嚴bTxI^dϐ;j ':  /0,>Q .-h\Lk@ثVn1VZj]Aa$h  ox#bAX˿[+ wD7>i]s;d<CUudh9Ă󌆟 ԣG~qΧK- :Կj|'AX_a>'YyeKft,Չeu@eKft,Չeu@eKft,Չeu@eKft,Չeu@eKfй.9Z[Q] s(9+.(`նF Kq#ڹ`*1]NpAI^1GSӘE=L:^%$`NI&YaN5ǽҴ1΢sC2zְqa󯮬.3_W\ ä.qnRKgoZuFikBV5ksNby}.$Gr '6+*op9~MP\/ܷz}/X{4~1GcG GOuZ""" """ """  _rϓQ&N4? yoNc6\=[W=ƾ52i\~%>USXfnђ4*Xօ ¥VU -9?V= NN[m- l]xʖFm٭Yw _8e~=SŎUl2-eʰ)DfVe;١9 OqV$ݑ軃[VR*4Sɽ")IYDCD@DDܐk=dVN+II]%(t?\"">m7-+hhj+ad̆!Hk[3 [9epAǒH;v-k).UԾHjX%yd 8Yn[jU$Z-K6hm,}A.x..l0FCG*5]%Ɗ@isHNs f '3+whj6~ o.tou;m[}AvLa9gsXkmۛ> }h"2B*|흦Vtk9ijs9T.ΧKM$=U;439I֦-hY "v>j Kg 8iI]8\/ܷz 7Qy/c~Ə?&?X^hAHөTWPD@DDD@DDD@DDs!v.sd10 p]9ya4[8~eJU#Dd/*2Wƅv0ȣ9[0*%zVD n8V 6=I_C&Œln>Kcqh';CJ'4)ѽgb[ě!E־:EhV՗ȩTI]bjS$z+$K_uڗJ9N$k_ rIze;OZtĒ%P*'j|k9.1pkp%+twp)"쉝 tyok$ꥁav hk@k@ d D@DDD@ܑ+IId=dPr__ W "S8)uC_' -yn_便WUnZն# !lec_Mx%ౘp-Ɯ⫧S3$x$48='[bOo6MW926ӇrFǼ,gpt6[MV<-G66@մ0a`趕J!gW:>75G ݱ žA=}e#g`u,GF 8#lM}ʞ:gI+ahiw-^qж==`{!Lڈ$K$__ EE{%tCp];gvNN|xx %ֆZ%xglAWm1~S ~.gA|V*"[:)]@~t9mx'GPK[C4:a4- f0O`9ӓ$!nT/dmm3uD7} DrLok:Nytj )[,ΕGSdak mqȎk7(`<( cs N#-5PUIR;Lm-CKy2{ xrU!W=4r!i sA!qW>J8,6ꊊ6>(ukk\Ρیj=Z%Dšn~z%n* "nn "nn "Kq]<ҫKq]<ҫ9b/1q _bAAA&V~vz웧aY_\t" """ ""N_o~*/-o^#_Gd =/'tL~cçS]V""" """ ""첹y_HMp-fqă >^oCG\t6 jׂ_WjѺƯjP e[yJNȍ2fe7)ԭ,2M 7I J6YZsR{Egve v[9Hk)H|/V5Ki#D z(rDI;ucZҠbdVʰZXԾ.e/d[t˯EPYp_M GڏksvDz?+YsYv@;;Hأlq44EKO8chkр;J5MX""`DDD@DDw$Gr@AJR}Y:De''~t?\r__ f(&)\PsJ2њZX% <@S ,t 2ApsS´U2:Yz];dŌlu89d`p}5EQZbxLuv$.+S܌M+g,9#HrfN i6)tvp.c1 kZcZφ51II|qRu푁bOQLT1ypݾw]#'Yd1N*gfTv{9+-d]cMD[=o#wjwB-*㞘 b SNZq,tyP~Y1[zGI#=[9rʙd۬v-q)#.sYs$$hGRޑ{HW_S+H[zEO-")]|}1N>"o>ܥu:bE^"/r/ޑ{HW_S([zEO-")]|}1N>"o>ܥu:bE^"/r/ޑ{m lFǵÀn|}1V+$krN/̺yV7]6/̺yV7@s _b͋C9ނC9ނEM0/,7O?³cԹqND@DDD@DDrr&U._ӗ[ߊFGQ侏ɏz=?P^Nazǣ߱#NS]@DDD@DDD@DDo[%¡EO#=᤯ιuEL?ґy~_IOG۲3F-)*KFM^^v3PfB=Xz"|d+y(0NrTC#N1Xy)&yՓrܕDI֩/VK%DT_CM"rң4JPr rn^ԾEʇ=,v=G\e[\k~諣W\D|Ǹxx(E42Ϫ ?gV2?J!gwv0Xn*C%TNgJ=9$~C'h/.EHfR'~Xey1HvMm:%,ٖJN1wnF 呑O=9i5F  tnyv3KsٌjKe%$"9%%'$z I8$QpŢPxxs%{^Z0u4[r@b,_$S6o,#S7SvN\#n ԋSpJ8wVcNHN^IRH+e3jkܼ?'9;#+|7g9Jo.@p{8G ]Y KY 8CZ^9Zw/6QxXFc^KI x<-: vCMӜdyyi[߽cxZKd4I w3ִzNc-tM,{ asY+\[ $GvnqY.׫tc'#[\3XN.55RL4F`fB@έMA~{-,TY瓎[.n1ԛ="h4}q+pG$~7m]=9-^[mT[dзKѰ 5<4M"6 qg*p%D@DDD@DDܐk=dVN+II]%or__ f Sa7Xj&] :ѹn$7VXI:p~b0@7MS0OO Zzx RKpq;, sXYa:\[aQ27Gi~X=j>>(WC S# F!c#*qW#柫iwZCK=Rmu$sb\3 qHlWOQ  3CcFqO0Fܩ\b/|27MʚU L؜d-C/zS2=nvݷvZ}- mTNʪE!7<u;<EWFClq ^f--ih9-!ެGsQ: 1FS!2C}\PǬ;IV5d9vyCf(:ǚ]slgw{{s]K$ݬN;W ,F2WiDinyg8'+ȡSȡU޺?"O;O"O;WzPN??(<?(]A:P@Z({({wAkȡSȡU޺?"O;Q慰5ෑy=%MADs_0s&/̺yV7]6/̺yV7@s _b͋C9ނC9ނEM0/,7O?³cԹqND@DDD@DDrr&U._ӗ[ߊFGQ侏ɏz=?P^Nazǣ߱#NS]@DDD@DDD@DDI˭>X#X_U~u8Fz.W;/BLrn9RѺ!rJdJ|Ԩʊfj}QXU34HU(S:RUr5@y]iVAUcVcR.X۞KZ N$)*I&pdqܹ< tMKOw]o3ÿ;䠵/FU][LՑ]80_r lllk#hk0/NN)R%hDQ,""" """ #"; gV2?Jz%c)>?+便@S7@7MP5wTDG-\(iՐ^u`<+t{k顯lSWhqg\Gôc[)v5.k,:顫6<iIO/vmS:*˔5G\%ifn{9o}n9XMuΦK?WEK(Ec޶bK칠3N5[ YŒ_&D\я8|;oå7ɪI\CXrwy=\Nf?3';gb-\9<Φɯ-==W6+"sc3yӱipjөmӾyc~JG$L1ݖNòAS\X\!cXX$y8n܌r FCY8Z$z>ɂ[eF+,.pF3`x/IqVæ9:b2|gsZSTAFGVK~Vm/\vYkWTڈ.=$ r2 nah$ Y*Ӫ e0utBb8|eVTV馆05ᅻddmuby*QIKDw#V '#EԾt-a9vG7ݙ35]UZ]5;~}&s}ٟ?G7ݙ3j !yvg'f~L^G7ݙ3s}ٟ?f~Lyvg)s}ٟ?G7ݙ3j !yvg+OIkٸ%d:ϯJKp]<ҫKq]<ҫ9b/1q _bAAA&V~vz웧aY_\t" """ ""N_o~*/-o^#_Gd =/'tL~cçS]V""" """ "" Vc}ktCX+ /Ћ\w4ӏxкhd|o,pz=ѓV6VܯJӗ3%. 7W_1#¬1]jL r4\eV/*VAƫMUcUYVB.XtOip0dz2_1\!wкgp )vW>AՊvP(ՔA t(#lq0ikZ0U," """ """ ""H5XOO+'Y蕉FGPKOҀfE$I~Xm\*z &yd?,{!cޡT=N| Y~X}Ux2gCǽ<=UC>d{̀O,zP: &yd?,{!cޡT=N| Y~X}Ux2gCǽ<=UC>d{̀O,zP: &yd?,{!cޡT=N| Y~X}Ux2gCǽ<=UC>d{̀O,zP: &yd?,{!cޡT=N| Y~X}Ux2gCǽ<=UC>d{̀Q:Bӑ/Q\ϧUop~!4Tn.]ӹA9b/1q _b}POT[rp7'6 )T5Ca46ӽ<6ڀT(;8~c;Ǯ3ZO\7f]gҼ{U/>a+r~=puJ|z5>_ܟ,.{+ _YǮ3Z^5}] 精=puJ|z5>_ܟ,.{+ _YǮ3Z^5}] 精=puJ|z5>_ܟ,.{+ _YǮ3Z^5}] 精=puJ|z5>_ܟ,.{+ _YqӷX> \-²Cc-9qvן>+r ?Pv\NEL{vꮏE4~>aڽ (1غpޠW0*D@DDD@DDD@DDFP45 ijct2y9#T@xn'9sdDNU^q6it |/>uZn0uO8LO']}^,<]7uUNt*UPhg`#ZI[ tWŜC[IJߩ4}`cBME]2U -8JU]MIH1ǟs] ns LY߻=u:j XhiᦦaB4s S6SC7AVWkѝ0ݮ< 씴S Xc 28֎]Esӥ jA " """ """ """ y"*敊7Ŏek=5ŔZBi|ԭ=</OOɏRsݥE غeļcl >y<)?&?Y>atStXJ>i?[U> &cd یe+me (x-i-n^ Z\vn~OO\XZˌ􍙵~P֊ fDغBTeY *K!dv<ō~ #'ff)uOP'Z}V?P{%wTrwQ4Ძnu4TFH"-7lJOI?YjhٟQW=S:#"k'c- np{el]=Mʒ,PI#3j4혽ҺdvMT;6==u uE\2 5ʦ7gWƒs {3bh݋v.b~`Y<ՏH mʟaZqкd9iZ\~+?uog s>J|Pod߹˯ϒ39O ̺)s>J|Pod߹˯ϒ39O ̺)s>J|Pod߹˯ϒ39O ̺)s>J|Pod߹˯ϒ39U7Ze~gOBZxmG̷EaJ<أ   "" """ """ ""*_ Dlk5ø*θ=2T,KR4t "b*GiN[R~{sO](ϕttx5^[Zy.h "?% o:S6_NUOOO@z3p l7 7GI{ ^^oi_?o)^_?o)OGc0n鋣 ~2 3UG3+Ӻ4T[j\gڼ)))(L2[ϡM26Glҫ>oOOO@zg7} 2pmc W&@vs)-;s ǫ _L=Mվ>"|SDrǶsF x!Y=7p!Ė>W̼)))(L1?ϡ? ]gм)))(L1quBOSWĆYh*4u~<?%>?%t%Xΰts[0v| 5duB7=3gm>nV'. 5󿬐Y;Hn}h/5| ~J,=6ޚxbo2N&cN*SHop suUId]O+ %%N}Zm%oxF 6]1s,,u; :ZݐmB7H0ЦH`" >`&搚BBi >i /&搚BBi >i /&" """ """ """ """ """ """ """ """ 0XKvY9,mg"v[-6c殟qZ_2rOϙoO歠4y'߼ #|w>eH2|]ϙn>.̟~[ 4y'߼ #|w>eH2|]ϙn>.̟~[ 4y'߼ #|w>eH2|]ϙn>.̟~[ 4y'߼ #|{>eM}ipVjm/0fhOe-H \D@DDD@DDD@DDD@DDD@mayavi-4.1.0/mayavi/0000755000175100001440000000000011674464502015262 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/tools/0000755000175100001440000000000011674464502016422 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/tools/preferences_mirror.py0000644000175100001440000000407111674464502022671 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. # Enthought library imports. from traits.api import HasTraits, List, Str, Instance from apptools.preferences.api import PreferencesHelper ################################################################################ # `PreferencesMirror` class. ################################################################################ class PreferencesMirror(HasTraits): """ This class mirrors preferences from a PreferencesHelper such that users can use them and change them but nothing is saved to disk till the user asks for an explicit save. """ # The preferences we mirror. preferences = Instance(PreferencesHelper) # Private trait to store names of traits. _trait_names = List(Str) ###################################################################### # Public interface. ###################################################################### def save(self): """Updates the actual preferences and thereby persists them to disk. """ for name in self._trait_names: setattr(self.preferences, name, getattr(self, name)) ###################################################################### # Private interface. ###################################################################### def _preferences_changed(self): """Setup traits of our own based on those of the mayavi preferences. """ trait_names = [] opts = self.preferences for key, value in opts.traits().iteritems(): if key not in ['trait_added', 'trait_modified', 'preferences', 'preferences_path']: self.add_trait(key, value) setattr(self, key, getattr(opts, key)) trait_names.append(key) opts.on_trait_change(self._update, key) self._trait_names = trait_names def _update(self, obj, name, old, new): setattr(self, name, new) mayavi-4.1.0/mayavi/tools/pipeline.py0000644000175100001440000000066311674464502020606 0ustar ischnellusers00000000000000""" API module grouping all the mlab functions to manipulate directly the pipeline. """ # Author: Gael Varoquaux # Copyright (c) 2007-2009, Enthought, Inc. # License: BSD Style. from modules import * from sources import * from filters import * from tools import add_dataset, set_extent, add_module_manager, \ get_vtk_src from probe_data import probe_data from tools import _traverse as traverse mayavi-4.1.0/mayavi/tools/data_wizards/0000755000175100001440000000000011674464502021076 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/tools/data_wizards/mydata.csv0000644000175100001440000000014611674464502023073 0ustar ischnellusers00000000000000% This is my sample data: % "Sample Name":"Radius":"Speed" Foo:12.5:85.4 Bar:14.1:87.6 Baz:14.3:89.0 mayavi-4.1.0/mayavi/tools/data_wizards/csv_source_factory.py0000644000175100001440000000330511674464502025353 0ustar ischnellusers00000000000000""" Factory used by mayavi to import csv-like files into datasets. """ # Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. from traits.api import HasTraits, Callable from traitsui.api import Handler from mayavi.tools.data_wizards.data_source_wizard import \ DataSourceWizardView from mayavi.tools.data_wizards.csv_loader import \ CSVLoader, CSVLoaderController class CallbackCSVLoader(CSVLoaderController): """ Simple handler for a TraitsUI view to call a given callback on exit. """ # A callable called when the TraitsUI view is closed. The instance # that the view was editing is passed to this callable. callback = Callable() def closed(self, info, is_ok): print "CallbackHander" if is_ok: self.callback(self.model) class CSVSourceFactory(HasTraits): """ Functor to load a CSV-like data from a file. """ def csv_loaded_callback(self, object): """ """ self.data_source_wizard = DataSourceWizardView( data_sources=self.csv_loader.data_dict) self.data_source_wizard.edit_traits() def __call__(self, fname): """ Pops up the dialogs required for the import of the CSV to happen. """ self.csv_loader = CSVLoader(filename=fname) self.csv_loader.guess_defaults() controller = CallbackCSVLoader(model=self.csv_loader, callback=self.csv_loaded_callback) controller.edit_traits() if __name__ == '__main__': from pyface.api import GUI source_factory = CSVSourceFactory() source_factory('mydata.csv') GUI().start_event_loop() mayavi-4.1.0/mayavi/tools/data_wizards/csv_sniff.py0000644000175100001440000001402211674464502023427 0ustar ischnellusers00000000000000# Author: Ilan Schnell # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # TODO: should derive from HasTraits import csv # FIXME: see loadtxt.py (should really be the loadtxt from numpy) from mayavi.tools.data_wizards.loadtxt import loadtxt class Sniff(object): """ Sniff a CSV file and determine some of it's properties. The properties determined here allow an CSV of unknown format to be read by numpy.loadtxt, i.e. the methods and attributes are suitable to determine required keyword arguments for numpy.loadtxt Example:: s = Sniff('mydata.csv') print repr(s.delimiter()) # ',' print s.skiprows() # 2 a = s.loadtxt() # a is now the array from numpy import loadtxt # make sure it's numpy 1.1.0 or higher b = loadtxt('mydata.csv', **s.kwds()) """ def __init__(self, filename): self._filename = filename self._lines = self._read_few_lines() self._reallines = [line for line in self._lines if line.strip()] self._dialect = csv.Sniffer().sniff(self._reallines[-1]) self._get_comment() if self._dialect.delimiter.isalnum(): self._usePySplit = True self._numcols = 1 else: self._usePySplit = not self._dialect.delimiter.strip() self._numcols = len(self._split(self._reallines[-1])) self._datatypes = self._datatypes_of_line(self._reallines[-1]) def _get_comment(self): self._comment = '#' line0 = self._reallines[0] if line0.startswith('#') or line0.startswith('%'): self._comment = line0[0] self._reallines[0] = self._dialect.delimiter.join(line0.split()[1:]) for i in xrange(1, len(self._reallines)): self._reallines[i] = \ self._reallines[i].split(self._comment)[0] def _read_few_lines(self): res = [] f = open(self._filename, 'rb') for line in f: line = line.strip() res.append(line) if len(res) > 20: break f.close() return res def _split(self, line): if self._usePySplit: return line.split() else: return csv.reader([line], self._dialect).next() def _names(self): if self._datatypes != self._numcols * (str,): for line in self._reallines: if len(self._split(line)) != self._numcols: continue if self._datatypes_of_line(line) != self._numcols * (str,): continue return tuple(t.strip('"\' \t') for t in self._split(line)) return tuple('Column %i' % (i+1) for i in xrange(self._numcols)) def _formats(self): res = [] for c, t in enumerate(self._datatypes): if t == str: items = [len(self._split(l)[c]) for l in self._reallines[1:] if self._datatypes_of_line(l) == self._datatypes] items.append(1) res.append('S%i' % max(items)) elif t == float: res.append(t) else: raise TypeError("Hmm, did not expect: %r" % t) return tuple(res) def _datatypes_of_line(self, line): def isFloat(s): try: float(s) return True except ValueError: return False res = [] for s in self._split(line): if isFloat(s): res.append(float) else: res.append(str) return tuple(res) def _debug(self): print '===== Sniffed information for file %r:' % self._filename print 'delimiter = %r' % self.delimiter() print 'comments = %r' % self.comments() print 'dtype = %r' % self.dtype() print 'skiprows = %r' % self.skiprows() #----------------------------------------------------------------------- # Public API: #----------------------------------------------------------------------- def comments(self): """ Return the character used for comments (usually '#'). """ return self._comment def delimiter(self): """ Return the delimiter string. When whitespace is used as the delimiter, None is returned. """ if self._usePySplit: return None else: return self._dialect.delimiter def skiprows(self): """ The number (int) of rows from the top to skip. """ for n, line in enumerate(self._lines): if self._datatypes == self._datatypes_of_line(line): return n return 0 def dtype(self): """ Return a dict suitable to be used as the dtype keyword argument of loadtxt. """ return {'names': self._names(), 'formats': self._formats()} def kwds(self): """ Return a dict of the keyword argument needed by numpy.loadtxt """ return {'comments' : self.comments(), 'delimiter': self.delimiter(), 'skiprows' : self.skiprows(), 'dtype' : self.dtype()} def loadtxt(self): """ Return the array (by using numpy.loadtxt), using the sniffed information in the keyword arguments. """ return loadtxt(self._filename, **self.kwds()) def loadtxt_unknown(filename, verbose=0): """ Like numpy.loadtxt but more general, in the sense that it uses Sniff first to determine the necessary keyword arguments for loadtxt. """ s = Sniff(filename) if verbose: s._debug() return s.loadtxt() def array2dict(arr): """ Takes an array with special names dtypes and returns a dict where each name is a key and the corresponding data (as a 1d array) is the value. """ res = {} for k in arr.dtype.names: res[k] = arr[k] return res mayavi-4.1.0/mayavi/tools/data_wizards/loadtxt.py0000644000175100001440000001001111674464502023120 0ustar ischnellusers00000000000000# FIXME: # This file contains the loadtxt function from numpy 1.1.0 because there # is a bug the loadtxt function of numpy 1.0.4. # In the future this file can be removed, once Mayavi depends on # numpy 1.1.0 (or higher). import numpy as np def _string_like(obj): try: obj + '' except (TypeError, ValueError): return 0 return 1 def _getconv(dtype): typ = dtype.type if issubclass(typ, np.bool_): return lambda x: bool(int(x)) if issubclass(typ, np.integer): return lambda x: int(float(x)) elif issubclass(typ, np.floating): return float elif issubclass(typ, np.complex): return complex else: return str def loadtxt(fname, dtype=float, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False): """ Load ASCII data from fname into an array and return the array. The data must be regular, same number of values in every row Parameters ---------- fname : filename or a file handle. Support for gzipped files is automatic, if the filename ends in .gz dtype : data-type Data type of the resulting array. If this is a record data-type, the resulting array will be 1-d and each row will be interpreted as an element of the array. The number of columns used must match the number of fields in the data-type in this case. comments : str The character used to indicate the start of a comment in the file. delimiter : str A string-like character used to separate values in the file. If delimiter is unspecified or none, any whitespace string is a separator. converters : {} A dictionary mapping column number to a function that will convert that column to a float. Eg, if column 0 is a date string: converters={0:datestr2num}. Converters can also be used to provide a default value for missing data: converters={3:lambda s: float(s or 0)}. skiprows : int The number of rows from the top to skip. usecols : sequence A sequence of integer column indexes to extract where 0 is the first column, eg. usecols=(1,4,5) will extract the 2nd, 5th and 6th columns. unpack : bool If True, will transpose the matrix allowing you to unpack into named arguments on the left hand side. Examples -------- >>> X = loadtxt('test.dat') # data in two columns >>> x,y,z = load('somefile.dat', usecols=(3,5,7), unpack=True) >>> r = np.loadtxt('record.dat', dtype={'names':('gender','age','weight'), 'formats': ('S1','i4', 'f4')}) SeeAlso: scipy.io.loadmat to read and write matfiles. """ if _string_like(fname): if fname.endswith('.gz'): import gzip fh = gzip.open(fname) else: fh = file(fname) elif hasattr(fname, 'seek'): fh = fname else: raise ValueError('fname must be a string or file handle') X = [] dtype = np.dtype(dtype) defconv = _getconv(dtype) converterseq = None if converters is None: converters = {} if dtype.names is not None: converterseq = [_getconv(dtype.fields[name][0]) \ for name in dtype.names] for i,line in enumerate(fh): if i # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. from traits.api import HasTraits, Str, Int, Array, List, \ Instance, on_trait_change, Property, Button from pyface.api import GUI from traitsui.api import View, Item, HGroup, Group, \ ListEditor, TabularEditor, spring, TextEditor, Controller, VSplit from traitsui.tabular_adapter import TabularAdapter from mayavi.tools.data_wizards.csv_sniff import Sniff, loadtxt, \ array2dict ############################################################################## # ListItem class ############################################################################## class ListItem(HasTraits): """ Class used to represent an item in a list with traits UI. """ column_number = Int name = Str my_name = Str parent=Instance(HasTraits) view = View( HGroup( Item('name', style='readonly', show_label=False, resizable=False), Item('my_name', style='simple', show_label=False, editor=TextEditor(auto_set=False, enter_set=True), springy=True), ) ) ############################################################################## # CSVLoader class ############################################################################## class CSVLoader(HasTraits): """ User interface to load CSV files. """ # The name of the file being loaded. filename = Str # The comment characters comments = Str(desc="The comment characters") # The character giving the delimiter between the columns. delimiter = Str( desc="The character giving the delimiter between the columns") # The number of rows to skip at the beginning of the file skiprows = Int( desc="The number of rows to skip at the beginning of the file") columns = List(ListItem) data = Array data_dict = Property(depends_on='data') def _get_data_dict(self): return array2dict(self.data) def guess_defaults(self): try: kwds = Sniff(self.filename).kwds() except: kwds = { 'comments': '#', 'delimiter': ',', 'dtype': float, 'skiprows': 0 } if kwds['delimiter']: self.delimiter = kwds['delimiter'] else: self.delimiter = ' ' self.comments = kwds['comments'] self.skiprows = kwds['skiprows'] self.names = list(kwds['dtype']['names']) self.formats = list(kwds['dtype']['formats']) self.columns = [ListItem(name = 'Column %i:' % (i+1), parent = self, column_number = i, my_name = val) for i, val in enumerate(self.names)] self.load_data() def load_data(self): kwds = {} kwds['delimiter'] = self.delimiter kwds['comments'] = self.comments kwds['skiprows'] = self.skiprows kwds['dtype'] = dict(names=self.names, formats=self.formats) try: self.data = loadtxt(self.filename, **kwds) except: pass ############################################################################## # CSVLoaderController class ############################################################################## class CSVLoaderController(Controller): """ A controller for the CSVLoader. """ tabular_editor = Instance(HasTraits) def _tabular_editor_default(self): class ArrayAdapter(TabularAdapter): columns = [(n, i) for i, n in enumerate(self.model.names)] font = 'Courier 10' alignment = 'right' format = '%s' return TabularEditor(adapter = ArrayAdapter()) update_preview = Button('Update preview') @on_trait_change('update_preview') def load_data(self): self.model.load_data() @on_trait_change('model.columns.my_name,model.data') def update_table_editor(self, object, name, old, new): if isinstance(object, ListItem): self.tabular_editor.adapter.columns[object.column_number] = \ (new, object.column_number) GUI.set_trait_later(self.info.ui, 'updated', True) file_content = Str @on_trait_change('model.filename') def update_file(self): f = open(self.model.filename) self.file_content = f.read(300) def default_traits_view(self): view = View( VSplit( HGroup( Group( spring, Item('delimiter', label='Column delimiter character'), Item('comments', label='Comment character'), Item('skiprows', label='Number of lines to skip at the ' 'beginning of the file'), spring, Item('handler.update_preview', show_label=False), ), Group( Item('columns', show_label=False, style='readonly', editor=ListEditor(style='custom'), springy=True, ), label="Column names", show_border=True, ), ), Group( Group( Item('data', show_label=False, editor=self.tabular_editor, ), label="Preview table", ), Group( Item('handler.file_content', style='readonly', show_label=False, springy=True), label="%s" % self.model.filename, ), layout='tab'), ), buttons = ['OK', 'Cancel', 'Help'], id = 'csv_load_editor', resizable=True, width=640, height=580, title='CSV import - [%s]' % self.model.filename ) return view if __name__ == '__main__': from pyface.api import GUI csv_loader = CSVLoader(filename='mydata.csv') csv_loader.guess_defaults() controller = CSVLoaderController(model=csv_loader) controller.edit_traits() GUI().start_event_loop() mayavi-4.1.0/mayavi/tools/data_wizards/data_source_wizard.py0000644000175100001440000006676011674464502025340 0ustar ischnellusers00000000000000 from numpy import ones, resize, linspace, atleast_3d from traits.api import Property, Str, Button, Trait, \ Any, Instance, HasStrictTraits, false, Dict, HasTraits, \ CArray, Bool from traitsui.api import EnumEditor, View, Item, HGroup, \ VGroup, spring, Group, TextEditor, HTMLEditor, InstanceEditor, \ TabularEditor, TitleEditor, Label, ArrayEditor, ImageEditor from traitsui.tabular_adapter import TabularAdapter from traitsui.image.image import ImageLibrary from pyface.api import ImageResource from data_source_factory import DataSourceFactory from preview_window import PreviewWindow from mayavi.modules.api import Surface, Glyph from mayavi.filters.api import ExtractEdges ############################################################################ # The DataSourceWizard class ############################################################################ class DataSourceWizard(HasTraits): data_sources = Dict _data_sources_names = Property(depends_on='data_sources') def _get__data_sources_names(self): names = [] for name in self.data_sources: try: self.data_sources[name] + 1 names.append(name) except TypeError: pass names.sort() return names # Dictionnary mapping the views data_type = Trait('point', {'A surface': 'surface', 'A set of points, that can be connected by lines': 'point', 'A set of vectors': 'vector', 'Volumetric data': 'volumetric', }) position_type = Trait('image data', {'Specified explicitly': 'explicit', 'Implicitely positioned on a regular grid': 'image data', 'On an orthogonal grid with varying spacing': 'orthogonal grid', }) # The array that is used for finding out the shape of the grid, # when creating an ImageData grid_shape_source = Property(depends_on='grid_shape_source_') def _get_grid_shape_source(self): if self.grid_shape_source_ == '': # catter for improperly initialized view keys = self._data_sources_names if not self.grid_shape.any(): self.grid_shape = \ self.data_sources[keys[0]].shape return keys[0] elif self.grid_shape_source_[:16]=='Shape of array: ': return self.grid_shape_source_[17:-1] else: return "" # Shadow traits for grid_shape_source grid_shape_source_ = Str def _grid_shape_source_changed(self): if not self.grid_shape_source == '': array_shape = \ atleast_3d(self.data_sources[self.grid_shape_source]).shape grid_shape = ones((3, )) grid_shape[:len(array_shape)] = array_shape self.grid_shape = grid_shape _grid_shape_source_labels = Property(depends_on='_data_sources_names') def _get__grid_shape_source_labels(self): values = ['Shape of array: "%s"' % name for name in self._data_sources_names] values.sort values.append('Specified explicitly') return values # The shape of the grid array. Used when position is implicit grid_shape = CArray(shape=(3,), dtype='i') # Whether or not the data points should be connected. lines = false # The scalar data selection scalar_data = Str('', help="Select the array that gives the value of the " "scalars plotted.") position_x = Str(help="Select the array that gives the x " "position of the data points") position_y = Str(help="Select the array that gives the y " "position of the data points") position_z = Str(help="Select the array that gives the z " "position of the data points") connectivity_triangles = Str has_vector_data = false(help="""Do you want to plot vector components?""") # A boolean to ask the user if he wants to load scalar data has_scalar_data = false vector_u = Str vector_v = Str vector_w = Str #---------------------------------------------------------------------- # Public interface #---------------------------------------------------------------------- def init_arrays(self): # Force all the array names to be properly initialized array_names = set(self.data_sources.keys()) if len(array_names) == 0: # We should probably bail out here. return False for attr in ('position_x', 'position_y', 'position_z', 'scalar_data', 'vector_u', 'vector_v', 'vector_w', 'connectivity_triangles', ): if len(array_names) > 0: array_name = array_names.pop() setattr(self, attr, array_name) def guess_arrays(self): """ Do some guess work on the arrays to find sensible default. """ array_names = set(self._data_sources_names) found_some = False if set(('x', 'y', 'z')).issubset(array_names): self.position_x = 'x' self.position_y = 'y' self.position_z = 'z' array_names = array_names.difference(('x', 'y', 'z')) found_some = True elif set(('X', 'Y', 'Z')).issubset(array_names): self.position_x = 'X' self.position_y = 'Y' self.position_z = 'Z' array_names = array_names.difference(('X', 'Y', 'Z')) found_some = True if set(('u', 'v', 'w')).issubset(array_names): self.vector_u = 'u' self.vector_v = 'v' self.vector_w = 'w' array_names = array_names.difference(('u', 'v', 'w')) found_some = True elif set(('U', 'V', 'W')).issubset(array_names): self.vector_u = 'U' self.vector_v = 'V' self.vector_w = 'W' array_names = array_names.difference(('U', 'V', 'W')) found_some = True if found_some: # Need to re-attribute the guessed names. for attr in ('scalar_data', 'vector_u', 'vector_v', 'vector_w', 'connectivity_triangles'): if len(array_names) > 0: setattr(self, attr, array_names.pop()) else: break def build_data_source(self): """ This is where we apply the selections made by the user in in the wizard to build the data source. """ factory = DataSourceFactory() # Keep a reference to the factory to be able to replay it, say # on other data. self._factory = factory if self.data_type_ == 'point': # The user wants to explicitly position vector, # thus only sensible data structures for points is with # explicit positioning. self.position_type_ == 'explicit' # In addition, this view does not allow for # connectivity. factory.unstructured = True factory.connected = False else: factory.connected = True if (self.position_type_ == "image data" and not self.data_type_=="point"): if not self.has_scalar_data and not self.vector_u=='': # With image data we need a scalar array always: factory.scalar_data = ones(self.grid_shape) factory.position_implicit = True else: factory.position_x = self.get_sdata(self.position_x) factory.position_y = self.get_sdata(self.position_y) factory.position_z = self.get_sdata(self.position_z) if self.position_type_ == "orthogonal grid": factory.orthogonal_grid = True if self.position_type_ == "explicit" and self.data_type_ == "surface": factory.connectivity_triangles = self.get_data( self.connectivity_triangles) if self.lines and self.data_type_=="point": factory.lines = True if self.has_vector_data or self.data_type_ == 'vector': # In the vector view, the user is not explicitly asked to # Enable vectors. factory.has_vector_data = True factory.vector_u = self.get_sdata(self.vector_u) factory.vector_v = self.get_sdata(self.vector_v) factory.vector_w = self.get_sdata(self.vector_w) if self.has_scalar_data or self.data_type_ == 'volumetric': # In the volumetric view, the user is not explicitly asked to # Enable scalars. factory.scalar_data = self.get_sdata(self.scalar_data) if self.connectivity_triangles == '': factory.connectivity_triangles = None self.data_source = factory.build_data_source() if self.has_scalar_data: if hasattr(self.data_source, 'scalar_name'): self.data_source.scalar_name = self.scalar_data elif hasattr(self.data_source, 'point_scalar_name'): self.data_source.point_scalar_name = self.scalars #---------------------------------------------------------------------- # Private interface #---------------------------------------------------------------------- def get_data(self, name): return self.data_sources[name] def get_sdata(self, name): ary = self.data_sources[name] if not self.data_type_ == 'point': ary = resize(ary, self.grid_shape) return ary def active_arrays(self): """ Return the list of the active array-selection drop-downs. """ arrays = [] if self.data_type_ == 'point' or self.position_type_ == 'explicit': arrays.extend( ['position_x' ,'position_y', 'position_z',]) if self.data_type_ == 'vector' or self.has_vector_data: arrays.extend(['vector_u', 'vector_v', 'vector_w']) if self.has_scalar_data or self.data_type_ == 'volumetric': arrays.extend(['scalar_data']) return arrays def check_arrays(self): """ Checks that all the array have the right size. """ arrays_to_check = self.active_arrays() if len(arrays_to_check)==0: return True size = self.get_data(getattr(self, arrays_to_check.pop())).size for attr in arrays_to_check: if not self.get_data(getattr(self, attr)).size == size: return False if ( self.data_type_ == 'surface' and self.position_type_ == "explicit"): if not self.connectivity_triangles.size/3 == size: return False return True ############################################################################ # class ArrayColumnWrapper ############################################################################ class ArrayColumnWrapper(HasStrictTraits): name = Str shape = Str ############################################################################ # class ArrayColumnAdapter ############################################################################ class ArrayColumnAdapter(TabularAdapter): columns = [ ('name', 'name'), ('shape', 'shape'), ] width = 100 ############################################################################ # The DataSourceWizardView class ############################################################################ class DataSourceWizardView(DataSourceWizard): #---------------------------------------------------------------------- # Private traits #---------------------------------------------------------------------- _top_label = Str('Describe your data') _info_text = Str('Array size do not match') _array_label = Str('Available arrays') _data_type_text = Str("What does your data represents?" ) _lines_text = Str("Connect the points with lines" ) _scalar_data_text = Str("Array giving the value of the scalars") _optional_scalar_data_text = Str("Associate scalars with the data points") _connectivity_text = Str("Array giving the triangles") _vector_data_text = Str("Associate vector components") _position_text = Property(depends_on="position_type_") _position_text_dict = {'explicit': 'Coordinnates of the data points:', 'orthogonal grid': 'Position of the layers along each axis:', } def _get__position_text(self): return self._position_text_dict.get(self.position_type_, "") _shown_help_text = Str _data_sources_wrappers = Property(depends_on='data_sources') def _get__data_sources_wrappers(self): return [ ArrayColumnWrapper(name=name, shape=repr(self.data_sources[name].shape)) for name in self._data_sources_names ] # A traits pointing to the object, to play well with traitsUI _self = Instance(DataSourceWizard) _suitable_traits_view = Property(depends_on="data_type_") def _get__suitable_traits_view(self): return "_%s_data_view" % self.data_type_ ui = Any(False) _preview_button = Button(label='Preview structure') def __preview_button_fired(self): if self.ui: self.build_data_source() self.preview() _ok_button = Button(label='OK') def __ok_button_fired(self): if self.ui: self.ui.dispose() self.build_data_source() _cancel_button = Button(label='Cancel') def __cancel_button_fired(self): if self.ui: self.ui.dispose() _is_ok = Bool _is_not_ok = Bool def _anytrait_changed(self): """ Validates if the OK button is enabled. """ if self.ui: self._is_ok = self.check_arrays() self._is_not_ok = not self._is_ok _preview_window = Instance(PreviewWindow, ()) _info_image = Instance(ImageResource, ImageLibrary.image_resource('@std:alert16',)) #---------------------------------------------------------------------- # TraitsUI views #---------------------------------------------------------------------- _coordinates_group = \ HGroup( Item('position_x', label='x', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok')), Item('position_y', label='y', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok')), Item('position_z', label='z', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok')), ) _position_group = \ Group( Item('position_type'), Group( Item('_position_text', style='readonly', resizable=False, show_label=False), _coordinates_group, visible_when='not position_type_=="image data"', ), Group( Item('grid_shape_source_', label='Grid shape', editor=EnumEditor( name='_grid_shape_source_labels', invalid='_is_not_ok')), HGroup( spring, Item('grid_shape', style='custom', editor=ArrayEditor(width=-60), show_label=False), enabled_when='grid_shape_source==""', ), visible_when='position_type_=="image data"', ), label='Position of the data points', show_border=True, show_labels=False, ), _connectivity_group = \ Group( HGroup( Item('_connectivity_text', style='readonly', resizable=False), spring, Item('connectivity_triangles', editor=EnumEditor(name='_data_sources_names'), show_label=False, ), show_labels=False, ), label='Connectivity information', show_border=True, show_labels=False, enabled_when='position_type_=="explicit"', ), _scalar_data_group = \ Group( Item('_scalar_data_text', style='readonly', resizable=False, show_label=False), HGroup( spring, Item('scalar_data', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok')), show_labels=False, ), label='Scalar value', show_border=True, show_labels=False, ) _optional_scalar_data_group = \ Group( HGroup( 'has_scalar_data', Item('_optional_scalar_data_text', resizable=False, style='readonly'), show_labels=False, ), Item('_scalar_data_text', style='readonly', resizable=False, enabled_when='has_scalar_data', show_label=False), HGroup( spring, Item('scalar_data', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok'), enabled_when='has_scalar_data'), show_labels=False, ), label='Scalar data', show_border=True, show_labels=False, ), _vector_data_group = \ VGroup( HGroup( Item('vector_u', label='u', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok')), Item('vector_v', label='v', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok')), Item('vector_w', label='w', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok')), ), label='Vector data', show_border=True, ), _optional_vector_data_group = \ VGroup( HGroup( Item('has_vector_data', show_label=False), Item('_vector_data_text', style='readonly', resizable=False, show_label=False), ), HGroup( Item('vector_u', label='u', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok')), Item('vector_v', label='v', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok')), Item('vector_w', label='w', editor=EnumEditor(name='_data_sources_names', invalid='_is_not_ok')), enabled_when='has_vector_data', ), label='Vector data', show_border=True, ), _array_view = \ View( Item('_array_label', editor=TitleEditor(), show_label=False), Group( Item('_data_sources_wrappers', editor=TabularEditor( adapter = ArrayColumnAdapter(), ), ), show_border=True, show_labels=False )) _questions_view = View( Item('_top_label', editor=TitleEditor(), show_label=False), HGroup( Item('_data_type_text', style='readonly', resizable=False), spring, 'data_type', spring, show_border=True, show_labels=False, ), HGroup( Item('_self', style='custom', editor=InstanceEditor( view_name='_suitable_traits_view'), ), Group( # FIXME: Giving up on context sensitive help # because of lack of time. #Group( # Item('_shown_help_text', editor=HTMLEditor(), # width=300, # label='Help', # ), # show_labels=False, # label='Help', #), #Group( Item('_preview_button', enabled_when='_is_ok'), Item('_preview_window', style='custom', label='Preview structure'), show_labels=False, #label='Preview structure', #), #layout='tabbed', #dock='tab', ), show_labels=False, show_border=True, ), ) _point_data_view = \ View(Group( Group(_coordinates_group, label='Position of the data points', show_border=True, ), HGroup( 'lines', Item('_lines_text', style='readonly', resizable=False), label='Lines', show_labels=False, show_border=True, ), _optional_scalar_data_group, _optional_vector_data_group, # XXX: hack to have more vertical space Label('\n'), Label('\n'), Label('\n'), )) _surface_data_view = \ View(Group( _position_group, _connectivity_group, _optional_scalar_data_group, _optional_vector_data_group, )) _vector_data_view = \ View(Group( _vector_data_group, _position_group, _optional_scalar_data_group, )) _volumetric_data_view = \ View(Group( _scalar_data_group, _position_group, _optional_vector_data_group, )) _wizard_view = View( Group( HGroup( Item('_self', style='custom', show_label=False, editor=InstanceEditor(view='_array_view'), width=0.17, ), '_', Item('_self', style='custom', show_label=False, editor=InstanceEditor(view='_questions_view'), ), ), HGroup( Item('_info_image', editor=ImageEditor(), visible_when="_is_not_ok"), Item('_info_text', style='readonly', resizable=False, visible_when="_is_not_ok"), spring, '_cancel_button', Item('_ok_button', enabled_when='_is_ok'), show_labels=False, ), ), title='Import arrays', resizable=True, ) #---------------------------------------------------------------------- # Public interface #---------------------------------------------------------------------- def __init__(self, **traits): DataSourceFactory.__init__(self, **traits) self._self = self def view_wizard(self): """ Pops up the view of the wizard, and keeps the reference it to be able to close it. """ # FIXME: Workaround for traits bug in enabled_when self.position_type_ self.data_type_ self._suitable_traits_view self.grid_shape_source self._is_ok self.ui = self.edit_traits(view='_wizard_view') def preview(self): """ Display a preview of the data structure in the preview window. """ self._preview_window.clear() self._preview_window.add_source(self.data_source) data = lambda name: self.data_sources[name] g = Glyph() g.glyph.glyph_source.glyph_source = \ g.glyph.glyph_source.glyph_list[0] g.glyph.scale_mode = 'data_scaling_off' if not (self.has_vector_data or self.data_type_ == 'vector'): g.glyph.glyph_source.glyph_source.glyph_type = 'cross' g.actor.property.representation = 'points' g.actor.property.point_size = 3. self._preview_window.add_module(g) if not self.data_type_ in ('point', 'vector') or self.lines: s = Surface() s.actor.property.opacity = 0.3 self._preview_window.add_module(s) if not self.data_type_ == 'point': self._preview_window.add_filter(ExtractEdges()) s = Surface() s.actor.property.opacity = 0.2 self._preview_window.add_module(s) if __name__ == '__main__': from numpy import mgrid x, y, z = mgrid[-5:5, -5:5, -5:5] r = x**2 + y**2 + z**2 X = linspace(0, 8) data_sources = { 'x':X, 'y':y, 'z':z, 'r':r } wizard = DataSourceWizardView(data_sources=data_sources) wizard.init_arrays() wizard.guess_arrays() wizard.view_wizard() mayavi-4.1.0/mayavi/tools/tools.py0000644000175100001440000003006111674464502020134 0ustar ischnellusers00000000000000""" The general purpose tools to manipulate the pipeline with the mlab interface. """ # Author: Prabhu Ramachandran , # Gael Varoquaux # Copyright (c) 2007, 2010 Enthought, Inc. # License: BSD Style. # Standard library imports. import numpy import types # Enthought library imports. from tvtk.api import tvtk # MayaVi related imports. from mayavi.sources.vtk_data_source import VTKDataSource from mayavi.core.module_manager import ModuleManager from mayavi.core.source import Source from mayavi.core.filter import Filter from engine_manager import get_engine, engine_manager, get_null_engine from figure import gcf ###################################################################### # Utility functions. def add_dataset(dataset, name='', **kwargs): """Add a dataset object to the Mayavi pipeline. **Parameters** :dataset: a tvtk dataset, or a Mayavi source. The dataset added to the Mayavi pipeline :figure: a figure identifier number or string, None or False, optionnal. If no `figure` keyword argument is given, the data is added to the current figure (a new figure if created if necessary). If a `figure` keyword argument is given, it should either the name the number of the figure the dataset should be added to, or None, in which case the data is not added to the pipeline. If figure is False, a null engine is created. This null engine does not create figures, and is mainly usefull for tensting, or using the VTK algorithms without visualization. **Returns** The corresponding Mayavi source is returned. """ if isinstance(dataset, tvtk.Object): d = VTKDataSource() d.data = dataset elif isinstance(dataset, Source): d = dataset else: raise TypeError, \ "first argument should be either a TVTK object"\ " or a mayavi source" if len(name) > 0: d.name = name if not 'figure' in kwargs: # No figure has been specified, retrieve the default one. gcf() engine = get_engine() elif kwargs['figure'] is False: # Get a null engine that we can use. engine = get_null_engine() elif kwargs['figure'] is not None: figure = kwargs['figure'] engine = engine_manager.find_figure_engine(figure) engine.current_scene = figure else: # Return early, as we don't want to add the source to an engine. return d engine.add_source(d) return d def add_module_manager(object): """ Add a module-manager, to control colors and legend bars to the given object. """ return get_engine().add_module(ModuleManager(), object) def _traverse(node): """ Generator to traverse a tree accessing the nodes' children attribute. **Example** Here is a simple example printing the names of all the objects in the pipeline:: for obj in mlab.pipeline.traverse(mlab.gcf()): print obj.name """ try: for leaf in node.children: for leaflet in _traverse(leaf): yield leaflet except AttributeError: pass yield node def get_vtk_src(mayavi_object, stop_at_filter=True): """ Goes up the Mayavi pipeline to find the data sources of a given object. **Parameters** :object: any Mayavi visualization object :stop_at_filter: optional boolean flag: if True, the first object exposing data found going up the pipeline is returned. If False, only the source itself is returned. **Returns** :sources: List of vtk data sources (vtk data sources, and not Mayavi source objects). **Notes** This function traverses the Mayavi pipeline. Thus the input object 'mayavi_object' should already be added to the pipeline. """ if isinstance(mayavi_object, tvtk.Object) \ and hasattr(mayavi_object, 'point_data'): # We have been passed a tvtk source return [mayavi_object] if not ( hasattr(mayavi_object, 'parent') or isinstance(mayavi_object, Source)): raise TypeError, 'Cannot find data source for given object %s' % ( mayavi_object) while True: # XXX: If the pipeline is not a DAG, this is an infinite loop if isinstance(mayavi_object, Source): if stop_at_filter or not isinstance(mayavi_object, Filter): return mayavi_object.outputs mayavi_object = mayavi_object.parent def _has_scalar_data(object): """Tests if an object has scalar data. """ data_sources = get_vtk_src(object) for source in data_sources: if source.point_data.scalars is not None: return True elif source.cell_data.scalars is not None: return True return False def _has_vector_data(object): """Tests if an object has vector data. """ data_sources = get_vtk_src(object) for source in data_sources: if source.point_data.vectors is not None: return True elif source.cell_data.vectors is not None: return True return False def _has_tensor_data(object): """Tests if an object has tensor data. """ data_sources = get_vtk_src(object) for source in data_sources: if source.point_data.tensors is not None: return True elif source.cell_data.tensors is not None: return True return False def _find_module_manager(object=None, data_type=None): """If an object is specified, returns its module_manager, elsewhere finds the first module_manager in the scene. """ if object is None: for object in _traverse(gcf()): if isinstance(object, ModuleManager): if data_type == 'scalar': if not _has_scalar_data(object): continue try: if not object.actor.mapper.scalar_visibility: continue except AttributeError: pass if data_type == 'vector' and not _has_vector_data(object): continue if data_type == 'tensor' and not _has_tensor_data(object): continue return object else: if hasattr(object, 'module_manager'): if ((data_type == 'scalar' and _has_scalar_data(object)) or (data_type == 'vector' and _has_vector_data(object)) or (data_type == 'tensor' and _has_tensor_data(object)) or data_type is None): return object.module_manager else: print("This object has no %s data" % data_type) else: print("This object has no color map") return None def _typical_distance(data_obj): """ Returns a typical distance in a cloud of points. This is done by taking the size of the bounding box, and dividing it by the cubic root of the number of points. """ x_min, x_max, y_min, y_max, z_min, z_max = data_obj.bounds distance = numpy.sqrt(((x_max-x_min)**2 + (y_max-y_min)**2 + (z_max-z_min)**2)/(4* data_obj.number_of_points**(0.33))) if distance == 0: return 1 else: return 0.4*distance def _min_distance(x, y, z): """ Return the minimum interparticle distance in a cloud of points. This is done by brute force calculation of all the distances between particle couples. """ distances = numpy.sqrt( (x.reshape((-1,)) - x.reshape((1, -1)))**2 + (y.reshape((-1,)) - y.reshape((1, -1)))**2 + (z.reshape((-1,)) - z.reshape((1, -1)))**2 ) return distances[distances!=0].min() def _min_axis_distance(x, y, z): """ Return the minimum interparticle distance in a cloud of points along one of the axis. This is done by brute force calculation of all the distances with norm infinity between particle couples. """ def axis_min(a): a = numpy.abs(a.reshape((-1,)) - a.reshape((-1, 1))) a = a[a>0] if a.size == 0: return numpy.inf return a.min() distances = min(axis_min(x), axis_min(y), axis_min(z)) if distances == numpy.inf: return 1 else: return distances def set_extent(module, extents): """ Attempts to set the physical extents of the given module. The extents are given as (xmin, xmax, ymin, ymax, zmin, zmax). This does not work on an image plane widget, as this module does not have an actor. Once you use this function on a module, be aware that other modules applied on the same data source will not share the same scale. Thus for instance an outline module will not respect the outline of the actors whose extent you modified. You should pass in the same "extents" parameter for this to work.You can have a look at the wigner.py example for a heavy use of this functionnality. **Note** This function does not work on some specific modules, such as Outline, Axes, or ImagePlaneWidget. For Outline and Axes, use the extent keyword argument of mlab.pipeline.outline and mlab.pipeline.axes. """ if numpy.all(extents == 0.): # That the default setting. return if not hasattr(module, 'actor'): print 'Cannot set extents for %s' % module return xmin, xmax, ymin, ymax, zmin, zmax = extents xo = 0.5*(xmax + xmin) yo = 0.5*(ymax + ymin) zo = 0.5*(zmax + zmin) extentx = 0.5*(xmax - xmin) extenty = 0.5*(ymax - ymin) extentz = 0.5*(zmax - zmin) # Now the actual bounds. xmin, xmax, ymin, ymax, zmin, zmax = module.actor.actor.bounds # Scale the object boundsx = 0.5*(xmax - xmin) boundsy = 0.5*(ymax - ymin) boundsz = 0.5*(zmax - zmin) xs, ys, zs = module.actor.actor.scale if not numpy.allclose(xmin, xmax): scalex = xs*extentx/boundsx else: scalex = 1 if not numpy.allclose(ymin, ymax): scaley = ys*extenty/boundsy else: scaley = 1 if not numpy.allclose(zmin, zmax): scalez = zs*extentz/boundsz else: scalez = 1 module.actor.actor.scale = (scalex, scaley, scalez) ## Remeasure the bounds xmin, xmax, ymin, ymax, zmin, zmax = module.actor.actor.bounds xcenter = 0.5*(xmax + xmin) ycenter = 0.5*(ymax + ymin) zcenter = 0.5*(zmax + zmin) # Center the object module.actor.actor.origin = (0., 0., 0.) xpos, ypos, zpos = module.actor.actor.position module.actor.actor.position = (xpos + xo -xcenter, ypos + yo - ycenter, zpos + zo -zcenter) def start_recording(ui=True): """Start automatic script recording. If the `ui` parameter is `True`, it creates a recorder with a user interface, if not it creates a vanilla recorder without a UI. **Returns** The `Recorder` instance created. """ from apptools.scripting.api import start_recording as start e = get_engine() msg = "Current engine, %s, is already being recorded."%(e) assert e.recorder is None, msg r = start(e, ui=ui) return r def stop_recording(file=None): """Stop the automatic script recording. **Parameters** :file: An open file or a filename or `None`. If this is `None`, nothing is saved. """ from apptools.scripting.api import stop_recording as stop e = get_engine() r = e.recorder if r is not None: stop(e, save=False) if type(file) in types.StringTypes: f = open(file, 'w') r.save(f) f.close() elif hasattr(file, 'write') and hasattr(file, 'flush'): r.save(file) mayavi-4.1.0/mayavi/tools/camera.py0000644000175100001440000003455011674464502020233 0ustar ischnellusers00000000000000""" Controlling the camera. """ # Author: Gael Varoquaux and Prabhu Ramachandran # Copyright (c) 2007-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. import warnings try: import numpy as np except ImportError, m: msg = '''%s\n%s\nPlease check your numpy installation. If you need numpy, 'easy_install numpy' will install it. http://numpy.scipy.org ''' % (m, '_'*80) raise ImportError(msg) from numpy import pi # We can't use gcf, as it creates a circular import in camera management # routines. from engine_manager import get_engine def world_to_display(x, y, z, figure=None): """ Converts 3D world coordinates to screenshot pixel coordinates. **Parameters** :x: float World x coordinate :y: float World y coordinate :z: float World z coordinate :figure: Mayavi figure or None The figure to use for the conversion. If None, the current one is used. **Output** :x: float Screenshot x coordinate :y: float Screenshot y coordinate """ if figure is None: f = get_engine().current_scene else: f = figure if f is None or f.scene is None: return 0, 0 f.scene._renderer.world_point = [x, y, z, 1] f.scene._renderer.world_to_display() x, y, _ = f.scene._renderer.display_point return x, y def roll(roll=None, figure=None): """ Sets or returns the absolute roll angle of the camera. **See also** :mlab.view: control the position and direction of the camera """ if figure is None: f = get_engine().current_scene else: f = figure if f is None: return scene = f.scene if scene is None: return cam = scene.camera if roll is not None: cam.set_roll(roll) if not scene.disable_render: scene.render() return cam.get_roll() # This is needed for usage inside the view function, where roll is a # local variable _roll = roll def rad2deg(rad): """Converts radians to degrees.""" return rad*180./pi def deg2rad(deg): """Converts degrees to radians.""" return deg*pi/180. def get_camera_direction(cam): """ Return the polar coordinates for the camera position: r, theta, phi, as well as the focal point. """ fp = cam.focal_point pos = cam.position x, y, z = pos - fp r = np.sqrt(x*x + y*y + z*z) theta = np.arccos(z/r) phi = np.arctan2(y, x) return r, theta, phi, fp def get_outline_bounds(figure=None): """ Return the pixel bounds of the objects visible on the figure. """ if figure is None: f = get_engine().current_scene else: f = figure if f is None: return scene = f.scene if scene is None: return 1, 1, 1, 1 # Lazy import, to avoid circular imports from figure import screenshot red, green, blue = scene.background # Use mode='rgba' to have float values, as with fig.scene.background outline = screenshot(mode='rgba') outline = ( (outline[..., 0] != red) +(outline[..., 1] != green) +(outline[..., 2] != blue) ) outline_x = outline.sum(axis=0) outline_y = outline.sum(axis=1) height, width = outline.shape width = float(width) height = float(height) outline_x = np.where(outline_x)[0] outline_y = np.where(outline_y)[0] if len(outline_x) == 0: x_min = x_max = .5*width else: x_min = outline_x.min() x_max = outline_x.max() if len(outline_y) == 0: y_min = y_max = .5*height else: y_min = outline_y.min() y_max = outline_y.max() return x_min, x_max, y_min, y_max, width, height def view(azimuth=None, elevation=None, distance=None, focalpoint=None, roll=None, reset_roll=True, figure=None): """ Sets/Gets the view point for the camera:: view(azimuth=None, elevation=None, distance=None, focalpoint=None, roll=None, reset_roll=True, figure=None) If called with no arguments this returns the current view of the camera. To understand how this function works imagine the surface of a sphere centered around the visualization. The `azimuth` argument specifies the angle "phi" on the x-y plane which varies from 0-360 degrees. The `elevation` argument specifies the angle "theta" from the z axis and varies from 0-180 degrees. The `distance` argument is the radius of the sphere and the `focalpoint`, the center of the sphere. Note that if the `elevation` is close to zero or 180, then the `azimuth` angle refers to the amount of rotation of a standard x-y plot with respect to the x-axis. Thus, specifying ``view(0,0)`` will give you a typical x-y plot with x varying from left to right and y from bottom to top. **Keyword arguments**: :azimuth: float, optional. The azimuthal angle (in degrees, 0-360), i.e. the angle subtended by the position vector on a sphere projected on to the x-y plane with the x-axis. :elevation: float, optional. The zenith angle (in degrees, 0-180), i.e. the angle subtended by the position vector and the z-axis. :distance: float or 'auto', optional. A positive floating point number representing the distance from the focal point to place the camera. New in Mayavi 3.4.0: if 'auto' is passed, the distance is computed to have a best fit of objects in the frame. :focalpoint: array_like or 'auto', optional. An array of 3 floating point numbers representing the focal point of the camera. New in Mayavi 3.4.0: if 'auto' is passed, the focal point is positioned at the center of all objects in the scene. :roll: float, optional Controls the roll, ie the rotation of the camera around its axis. :reset_roll: boolean, optional. If True, and 'roll' is not specified, the roll orientation of the camera is reset. :figure: The Mayavi figure to operate on. If None is passed, the current one is used. **Returns**: If no arguments are supplied it returns a tuple of 4 values ``(azimuth, elevation, distance, focalpoint)``, representing the current view. Note that these can be used later on to set the view. If arguments are supplied it returns `None`. **Examples**: Get the current view:: >>> v = view() >>> v (45.0, 45.0, 25.02794981, array([ 0.01118028, 0. , 4.00558996])) Set the view in different ways:: >>> view(45, 45) >>> view(240, 120) >>> view(distance=20) >>> view(focalpoint=(0,0,9)) Set the view to that saved in `v` above:: >>> view(*v) **See also** :mlab.roll: control the roll angle of the camera, ie the direction pointing up """ if figure is None: f = get_engine().current_scene else: f = figure if f is None: return scene = f.scene if scene is None: return ren = scene.renderer cam = scene.camera cos = np.cos sin = np.sin # First compute the current state of the camera. r, theta, phi, fp = get_camera_direction(cam) # If no arguments were specified, just return the current view. if azimuth is None and elevation is None and distance is None \ and focalpoint is None and roll is None: return rad2deg(phi), rad2deg(theta), r, fp # Convert radians to if azimuth is None: azimuth = rad2deg(phi) else: phi = deg2rad(azimuth) if elevation is None: elevation = rad2deg(theta) else: theta = deg2rad(elevation) # We compute the position of the camera on the surface of a sphere # centered at the center of the bounds, with radius chosen from the # bounds. bounds = np.array(ren.compute_visible_prop_bounds()) if distance is not None and not distance == 'auto': r = distance else: r = max(bounds[1::2] - bounds[::2])*2.0 cen = (bounds[1::2] + bounds[::2])*0.5 if focalpoint is not None and not focalpoint == 'auto': cen = np.asarray(focalpoint) # Find camera position. x = r*cos(phi)*sin(theta) y = r*sin(phi)*sin(theta) z = r*cos(theta) # Now setup the view. cam.focal_point = cen cam.position = cen + [x,y,z] cam.compute_view_plane_normal() ren.reset_camera_clipping_range() if roll is not None: print "setting roll" _roll(roll) elif reset_roll: # Now calculate the view_up vector of the camera. If the view up is # close to the 'z' axis, the view plane normal is parallel to the # camera which is unacceptable, so we use a different view up. view_up = [0, 0, 1] if abs(elevation) < 5. or abs(elevation) > 175.: view_up = [sin(phi), cos(phi), 0] cam.view_up = view_up if distance == 'auto': # Reset the zoom, to have the full extents: scene.reset_zoom() x_min, x_max, y_min, y_max, w, h = get_outline_bounds(figure=figure) x_focus, y_focus = world_to_display(cen[0], cen[1], cen[2], figure=figure) ratio = 1.1*max((x_focus - x_min)/x_focus, (x_max - x_focus)/(w - x_focus), (y_focus - y_min)/y_focus, (y_max - y_focus)/(h - y_focus), ) distance = get_camera_direction(cam)[0] r = distance*ratio # Reset the camera position. x = r*cos(phi)*sin(theta) y = r*sin(phi)*sin(theta) z = r*cos(theta) # Now setup the view. cam.position = cen + [x,y,z] cam.compute_view_plane_normal() ren.reset_camera_clipping_range() if not scene.disable_render: scene.render() return rad2deg(phi), rad2deg(theta), r, fp def move(forward=None, right=None, up=None): """ Translates the camera and focal point together. The arguments specify the relative distance to translate the camera and focal point, so as to produce the appearence of moving the camera without changing the effective field of view. If called with no arguments, the function returns the absolute position of the camera and focal pointon a cartesian coordinate system. Note that the arguments specify relative motion, although the return value with no arguments is in an absolute coordinate system. **Keyword arguments**: :forward: float, optional. The distance in space to translate the camera forward (if positive) or backward (if negative) :right: float, optional. The distance in space to translate the camera to the right (if positive) or left (if negative) :up: float, optional. The distance in space to translate the camera up (if positive) or down (if negative) **Returns**: If no arguments are supplied (or all are None), returns a tuple (camera_position, focal_point_position) otherwise, returns None **Examples**: Get the current camera position:: >>> cam,foc = move() >>> cam array([-0.06317079, -0.52849738, -1.68316389]) >>> foc array([ 1.25909623, 0.15692708, -0.37576693]) Translate the camera:: >>> move(3,-1,-1.2) >>> move() (array([ 2.93682921, -1.52849738, -2.88316389]), array([ 4.25909623, -0.84307292, -1.57576693])) Return to the starting position:: >>> move(-3,1,1.2) >>> move() (array([-0.06317079, -0.52849738, -1.68316389]), array([ 1.25909623, 0.15692708, -0.37576693])) **See also** :mlab.yaw: yaw the camera (tilt left-right) :mlab.pitch: pitch the camera (tilt up-down) :mlab.roll: control the absolute roll angle of the camera :mlab.view: set the camera position relative to the focal point instead of in absolute space """ f = get_engine().current_scene if f is None: return scene = f.scene if scene is None: return ren = scene.renderer cam = scene.camera if forward is None and right is None and up is None: return cam.position,cam.focal_point # vector to offset the camera loc and focal point v = np.zeros(3) # view plane vetor points behind viewing direction, so we invert it yhat = -1*cam.view_plane_normal zhat = cam.view_up if forward is not None: xhat = np.cross(yhat,zhat) v += forward*yhat if right is not None: v += right*xhat if up is not None: v += up*zhat # Apply the offset and setup the view. cam.position = cam.position + v cam.focal_point = cam.focal_point + v ren.reset_camera_clipping_range() scene.render() def yaw(degrees): """ Rotates the camera about the axis corresponding to the "up" direction of the current view. Note that this will change the location of the focal point (although not the camera location). This angle is relative to the current direction - the angle is NOT an absolute angle in a fixed coordinate system. **See also** :mlab.pitch: relative rotation about the "right" direction :mlab.roll: absolute roll angle (i.e. "up" direction) :mlab.move: relative translation of the camera and focal point """ f = get_engine().current_scene if f is None: return scene = f.scene if scene is None: return ren = scene.renderer cam = scene.camera cam.yaw(degrees) ren.reset_camera_clipping_range() scene.render() def pitch(degrees): """ Rotates the camera about the axis corresponding to the "right" direction of the current view. Note that this will change the location of the focal point (although not the camera location). This angle is relative to the current direction - the angle is NOT an absolute angle in a fixed coordinate system. **See also** :mlab.yaw: relative rotation about the "up" direction :mlab.roll: absolute roll angle (i.e. "up" direction) :mlab.move: relative translation of the camera and focal point """ f = get_engine().current_scene if f is None: return scene = f.scene if scene is None: return ren = scene.renderer cam = scene.camera cam.pitch(degrees) ren.reset_camera_clipping_range() scene.render() mayavi-4.1.0/mayavi/tools/decorations.py0000644000175100001440000004613111674464502021313 0ustar ischnellusers00000000000000""" Functions for adding decorations (axes, colorbar, outlines..) to the pipeline in a procedural way. """ # Author: Gael Varoquaux # Copyright (c) 2007, 2008, 2009, Enthought, Inc. # License: BSD Style. import operator import numpy as np # Enthought library imports. from traits.api import String, CFloat, Instance, HasTraits, \ Trait, CArray, true, Any, Range, Either import tools from figure import draw, gcf # Mayavi imports import mayavi.modules.api as modules from .pipe_base import make_function from .modules import ModuleFactory from .engine_manager import get_engine, engine_manager ############################################################################# # Colorbar related functions def _orient_colorbar(lut_mgr, orientation): """Orients the given LUTManager (make it horizontal or vertical). """ rep = lut_mgr.scalar_bar_representation colorbar = lut_mgr.scalar_bar if orientation == "vertical": if rep is None: # VTK < 5.2 colorbar.orientation = "vertical" else: rep.orientation = 1 rep.position = (0.01, 0.15) rep.position2 = (0.1, 0.8) colorbar.width = 0.1 colorbar.height = 0.8 colorbar.position = (0.01, 0.15) elif orientation == "horizontal": if rep is None: colorbar.orientation = "horizontal" else: rep.orientation = 0 rep.position = (0.1, 0.01) rep.position2 = (0.8, 0.17) colorbar.width = 0.8 colorbar.height = 0.17 colorbar.position = (0.1, 0.01) else: raise ValueError("Unknown orientation: %s" % orientation) draw() def _lut_manager_properties(lut_manager, **props): """ Internal function used to apply properties to a colorbar. """ need_redraw = False orientation = props.get('orientation', None) if orientation is not None: _orient_colorbar(lut_manager, orientation) colorbar = lut_manager.scalar_bar title = props.get('title', None) if title is not None: colorbar.title = title need_redraw = True label_fmt = props.get('label_fmt', None) if label_fmt is not None: colorbar.label_format = label_fmt need_redraw = True nb_labels = props.get('nb_labels', None) if nb_labels is not None: colorbar.number_of_labels = nb_labels need_redraw = True nb_colors = props.get('nb_colors', None) if nb_colors is not None: colorbar.maximum_number_of_colors = nb_colors need_redraw = True if need_redraw: draw() def scalarbar(object=None, title=None, orientation=None, nb_labels=None, nb_colors=None, label_fmt=None): """Adds a colorbar for the scalar color mapping of the given object. If no object is specified, the first object with scalar data in the scene is used. **Keyword arguments**: :object: Optional object to get the scalar color map from :title: The title string :orientation: Can be 'horizontal' or 'vertical' :nb_labels: The number of labels to display on the colorbar. :label_fmt: The string formater for the labels. This needs to be a formater for float number, eg '%.1f'. :nb_colors: The maximum number of colors displayed on the colorbar. """ module_manager = tools._find_module_manager(object=object, data_type="scalar") if module_manager is None: return if not module_manager.scalar_lut_manager.show_scalar_bar: if title is None: title = '' if orientation is None: orientation = 'horizontal' lut_mgr = module_manager.scalar_lut_manager module_manager.scalar_lut_manager.show_scalar_bar = True _lut_manager_properties(lut_mgr, title=title, orientation=orientation, nb_labels=nb_labels, nb_colors=nb_colors, label_fmt=label_fmt) return lut_mgr def vectorbar(object=None, title=None, orientation=None, nb_labels=None, nb_colors=None, label_fmt=None): """Adds a colorbar for the vector color mapping of the given object. If no object is specified, the first object with vector data in the scene is used. **Keyword arguments** :object: Optional object to get the vector color map from :title: The title string :orientation: Can be 'horizontal' or 'vertical' :nb_labels: The number of labels to display on the colorbar. :label_fmt: The string formater for the labels. This needs to be a formater for float number, eg '%.1f'. :nb_colors: The maximum number of colors displayed on the colorbar. """ module_manager = tools._find_module_manager(object=object, data_type="vector") if module_manager is None: return if not module_manager.vector_lut_manager.show_scalar_bar: if title is None: title = '' orientation = 'horizontal' lut_mgr = module_manager.vector_lut_manager lut_mgr.show_scalar_bar = True _lut_manager_properties(lut_mgr, title=title, orientation=orientation, nb_labels=nb_labels, nb_colors=nb_colors, label_fmt=label_fmt) return lut_mgr def colorbar(object=None, title=None, orientation=None, nb_labels=None, nb_colors=None, label_fmt=None): """Adds a colorbar for the color mapping of the given object. If the object has scalar data, the scalar color mapping is represented. Elsewhere the vector color mapping is represented, if available. If no object is specified, the first object with a color map in the scene is used. **Keyword arguments**: :object: Optional object to get the color map from :title: The title string :orientation: Can be 'horizontal' or 'vertical' :nb_labels: The number of labels to display on the colorbar. :label_fmt: The string formater for the labels. This needs to be a formater for float number, eg '%.1f'. :nb_colors: The maximum number of colors displayed on the colorbar. """ colorbar = scalarbar(object=object, title=title, orientation=orientation, nb_labels=nb_labels, nb_colors=nb_colors, label_fmt=label_fmt) if colorbar is None: colorbar = vectorbar(object=object, title=title, orientation=orientation, nb_labels=nb_labels, nb_colors=nb_colors, label_fmt=label_fmt) return colorbar ############################################################################# class SingletonModuleFactory(ModuleFactory): """ Base classe for factories that can find an existing object matching certain criteria instead of building a new one""" # The parent object on which this module is going to be added. _parent = Any def __init__(self, *args, **kwargs): """ Try to find an module actor with the same name, on the given parent (if any) and use it rather than building a new module.""" # Call the HasTraits constructor, but not the PipeBase one. HasTraits.__init__(self) self._scene = gcf() if not 'figure' in kwargs: self._engine = get_engine() else: figure = kwargs['figure'] self._engine = engine_manager.find_figure_engine(figure) self._engine.current_scene = figure kwargs.pop('figure') if self._scene.scene is not None: self._scene.scene.disable_render = True # Process the arguments if len(args)==1: (parent, ) = args elif len(args)==0: parent = self._engine.current_object else: raise ValueError, "Wrong number of arguments" # Try to find an existing module, if not add one to the pipeline if parent == None: target = self._scene else: target = parent klass = self._target.__class__ for obj in tools._traverse(target): if ( isinstance(obj, klass) and obj.name == self.name ): self._target = obj break else: # Keep a reference to the parent self._parent = parent self._engine.add_module(self._target, obj=parent) # Now calling the traits setter, so that traits handlers are # called self.set(**kwargs) if self._scene.scene is not None: self._scene.scene.disable_render = False ############################################################################# class AxesLikeModuleFactory(SingletonModuleFactory): """ Base class for axes and outline""" extent = CArray(shape=(6,), help="""[xmin, xmax, ymin, ymax, zmin, zmax] Default is the object's extents.""", ) def _extent_changed(self): """ There is no universal way of setting extents for decoration objects. This should be implemented in subclasses """ pass # Override the color and opacity handlers: axes and outlines do not # behave like other modules def _color_changed(self): if self.color: try: self._target.property.color = self.color except AttributeError: try: self._target.actor.property.color = self.color except AttributeError: pass def _opacity_changed(self): try: self._target.property.opacity = self.opacity except AttributeError: try: self._target.actor.property.opacity = self.opacity except AttributeError: pass def __init__(self, *args, **kwargs): """ Overide the call method to be able to catch the extents of the object, if any. """ SingletonModuleFactory.__init__(self, *args, **kwargs) if not 'extent' in kwargs: try: # XXX: Do not use tools.set_extent, as it does not work # on axes. self.extent = self._parent.actor.actor.bounds except AttributeError: """ Either this is not a module, or it has no actors""" ############################################################################# class Outline(AxesLikeModuleFactory): """ Creates an outline for the current (or given) object.""" _target = Instance(modules.Outline, ()) def _extent_changed(self): self._target.manual_bounds = True self._target.bounds = self.extent outline = make_function(Outline) ############################################################################# class Axes(AxesLikeModuleFactory): """ Creates axes for the current (or given) object.""" xlabel = String(None, adapts='axes.x_label', help='the label of the x axis') ylabel = String(None, adapts='axes.y_label', help='the label of the y axis') zlabel = String(None, adapts='axes.z_label', help='the label of the z axis') nb_labels = Range(0, 50, 2, adapts='axes.number_of_labels', desc='The number of labels along each direction') ranges = Trait(None, None, CArray(shape=(6,)), help="""[xmin, xmax, ymin, ymax, zmin, zmax] Ranges of the labels displayed on the axes. Default is the object's extents.""", ) x_axis_visibility = true(adapts='axes.x_axis_visibility', help="Whether or not the x axis is visible (boolean)") y_axis_visibility = true(adapts='axes.y_axis_visibility', help="Whether or not the y axis is visible (boolean)") z_axis_visibility = true(adapts='axes.z_axis_visibility', help="Whether or not the z axis is visible (boolean)") _target = Instance(modules.Axes, ()) def _extent_changed(self): """ Code to modify the extents for """ axes = self._target axes.axes.use_data_bounds = False axes.axes.bounds = self.extent if self.ranges is None: axes.axes.ranges = \ axes.module_manager.source.outputs[0].bounds def _ranges_changed(self): if self.ranges is not None: self._target.axes.ranges = self.ranges self._target.axes.use_ranges = True axes = make_function(Axes) def xlabel(text, object=None): """ Creates a set of axes if there isn't already one, and sets the x label **Keyword arguments**: :object: The object to apply the module to, if not the whole scene is searched for a suitable object. """ return axes(object, xlabel=text) def ylabel(text, object=None): """ Creates a set of axes if there isn't already one, and sets the y label **Keyword arguments**: :object: The object to apply the module to, if not the whole scene is searched for a suitable object. """ return axes(object, ylabel=text) def zlabel(text, object=None): """ Creates a set of axes if there isn't already one, and sets the z label **Keyword arguments** :object: The object to apply the module to, if not the whole scene is searched for a suitable object. """ return axes(object, zlabel=text) ############################################################################## class OrientationAxesFactory(SingletonModuleFactory): """Applies the OrientationAxes mayavi module to the given VTK data object. """ xlabel = String(None, adapts='axes.x_axis_label_text', help='the label of the x axis') ylabel = String(None, adapts='axes.y_axis_label_text', help='the label of the y axis') zlabel = String(None, adapts='axes.z_axis_label_text', help='the label of the z axis') _target = Instance(modules.OrientationAxes, ()) orientation_axes = make_function(OrientationAxesFactory) ############################################################################### class Text(ModuleFactory): """ Adds a text on the figure. **Function signature**:: text(x, y, text, ...) x, and y are the position of the origin of the text. If no z keyword argument is given, x and y are the 2D projection of the figure, they belong to [0, 1]. If a z keyword argument is given, the text is positionned in 3D, in figure coordinnates. """ width = Trait(None, None, CFloat, adapts='width', help="""width of the text.""") z = Trait(None, None, CFloat, help="""Optional z position. When specified, the text is positioned in 3D""") _target = Instance(modules.Text, ()) opacity = CFloat(1, adapts="property.opacity", help="""The opacity of the text.""") def __init__(self, x, y, text, **kwargs): """ Override init as for different positional arguments.""" if 'z' in kwargs and kwargs['z'] is not None: self._target.z_position = kwargs['z'] self._target.position_in_3d = True elif not (x<1. and x>0. and y>0. and y<1.): raise ValueError('Text positions should be in [0, 1] if no z' 'position is given') super(Text, self).__init__(None, **kwargs) self._target.text = text self._target.x_position = x self._target.y_position = y text = make_function(Text) ############################################################################### class Text3D(ModuleFactory): """ Positions text at a 3D location in the scene. **Function signature**:: text3d(x, y, z, text, ...) x, y, and z are the position of the origin of the text. The text is positionned in 3D, in figure coordinnates. """ _target = Instance(modules.Text3D, ()) scale = Either(CFloat(1), CArray(shape=(3,)), help="""The scale of the text, in figure units. Either a float, or 3-tuple of floats.""") orientation = CArray(shape=(3,), adapts='orientation', desc="""the angles giving the orientation of the text. If the text is oriented to the camera, these angles are referenced to the axis of the camera. If not, these angles are referenced to the z axis.""") orient_to_camera = true(adapts='orient_to_camera', desc="""if the text is kept oriented to the camera, or is pointing in a specific direction, regardless of the camera position.""") def __init__(self, x, y, z, text, **kwargs): """ Override init as for different positional arguments.""" if not 'scale' in kwargs: kwargs['scale'] = 1 super(Text3D, self).__init__(None, **kwargs) self._target.text = text self._target.position = (x, y, z) def _scale_changed(self): scale = self.scale if operator.isNumberType(scale): scale = scale*np.ones((3,)) self._target.scale = scale text3d = make_function(Text3D) ############################################################################# class Title(SingletonModuleFactory): """Creates a title for the figure. **Function signature**:: title(text, ...) """ size = CFloat(1, help="the size of the title") height = CFloat(0.8, adapts='y_position', help="""height of the title, in portion of the figure height""") def _size_changed(self): self._target.width = min(0.05*self.size*len(self._text), 1) self._target.x_position = 0.5*(1 - self._target.width) _target = Instance(modules.Text) def __target_default(self): """ This is called only if no existing title is found.""" width = min(0.05*self.size*len(self._text), 1) text= modules.Text(text=self._text, y_position=self.height, x_position=0.5*(1 - width),) text.width =width return text def __init__(self, text, **kwargs): self._text = text # This will be used by _size_changed if not 'name' in kwargs: # The name is used as au unique marker to identify the # title. We need to set it ASAP. self.name = kwargs['name'] = 'Title' super(Title, self).__init__(**kwargs) self._target.text = self._text # We need to set position after Text is initiated, as text will # override these positions self._target.y_position = self.height self._size_changed() title = make_function(Title) mayavi-4.1.0/mayavi/tools/modules.py0000644000175100001440000006544411674464502020461 0ustar ischnellusers00000000000000""" Modules factories and their associated functions for mlab. Module functions meant to be applied to a data source object or a filter should take only one positional argument, the input, to be easily used in helper functions. """ # Author: Gael Varoquaux # Prabhu Ramachandran # Copyright (c) 2007-2008, Enthought, Inc. # License: BSD Style. import numpy import new from traits.api import Trait, CArray, Instance, CFloat, \ Any, false, true, TraitTuple, Range, Bool, Property, CInt, Enum, Either from tvtk.api import tvtk from tvtk.common import camel2enthought from mayavi.core.lut_manager import lut_mode_list import mayavi.modules.api as modules from mayavi.core.registry import registry import tools from pipe_base import PipeFactory, make_function # This the list is dynamically populated further down below at the end. __all__ = [ 'vectors', 'glyph', 'streamline', 'surface', 'iso_surface', 'image_actor', 'contour_surface', 'contour_grid_plane', 'custom_grid_plane', 'image_plane_widget', 'scalar_cut_plane', 'vector_cut_plane', 'volume', ] ############################################################################## # Abstract module classes ############################################################################## class ModuleFactory(PipeFactory): """ Base class for all the modules factories""" color = Trait(None, None, TraitTuple(Range(0., 1.),Range(0., 1.),Range(0., 1.)), help="""the color of the vtk object. Overides the colormap, if any, when specified. This is specified as a triplet of float ranging from 0 to 1, eg (1, 1, 1) for white.""", ) def _color_changed(self): if self.color: self._target.actor.property.color = self.color if hasattr(self._target.actor.mapper, "scalar_visibility"): self._target.actor.mapper.scalar_visibility = False if hasattr(self._target, "property"): self._target.property.color = self.color opacity = CFloat(1., desc="""The overall opacity of the vtk object.""") def _opacity_changed(self): try: self._target.actor.property.opacity = self.opacity except AttributeError: try: self._target.property.opacity = self.opacity except AttributeError: pass line_width = CFloat(2., desc=""" The width of the lines, if any used.""") def _line_width_changed(self): try: self._target.actor.property.line_width = self.line_width except AttributeError: try: self._target.property.line_width = self.line_width except AttributeError: pass ############################################################################## class DataModuleFactory(ModuleFactory): """ Base class for all the module factories operating on data (ie not text and outline) """ reset_zoom = true(help="""Reset the zoom to accomodate the data newly added to the scene. Defaults to True.""") extent = CArray(shape=(6,), help="""[xmin, xmax, ymin, ymax, zmin, zmax] Default is the x, y, z arrays extent. Use this to change the extent of the object created.""", ) def _extent_changed(self): tools.set_extent(self._target, self.extent) transparent = false(help="""make the opacity of the actor depend on the scalar.""") def _transparent_changed(self): if self.transparent: data_range = \ self._target.module_manager.scalar_lut_manager.data_range self._target.module_manager.scalar_lut_manager.lut.alpha_range = \ (0.2, 0.8) data_range = ( numpy.mean(data_range) + 0.4 * ( data_range.max() - data_range.min()) * numpy.array([-1, 1])) self._target.module_manager.scalar_lut_manager.data_range = \ data_range colormap = Trait('blue-red', lut_mode_list(), help="""type of colormap to use.""") def _colormap_changed(self): colormap = self.colormap if colormap[-2:] == "_r": colormap = colormap[:-2] self._target.module_manager.scalar_lut_manager.reverse_lut = True self._target.module_manager.vector_lut_manager.reverse_lut = True self._target.module_manager.scalar_lut_manager.lut_mode = colormap self._target.module_manager.vector_lut_manager.lut_mode = colormap vmin = Trait(None, None, CFloat, help="""vmin is used to scale the colormap. If None, the min of the data will be used""") vmax = Trait(None, None, CFloat, help="""vmax is used to scale the colormap. If None, the max of the data will be used""") def _vmin_changed(self): if self.vmin == None and self.vmax == None: self._target.module_manager.scalar_lut_manager.use_default_range\ = True return self._target.module_manager.scalar_lut_manager.use_default_range \ = False vmin, vmax = \ self._target.module_manager.scalar_lut_manager.data_range if self.vmin is not None: vmin = self.vmin if self.vmax is not None: vmax = self.vmax self._target.module_manager.scalar_lut_manager.data_range = \ (vmin, vmax) _vmax_changed = _vmin_changed def __init__(self, *args, **kwargs): super(DataModuleFactory, self).__init__(*args, **kwargs) # We are adding data to the scene, reset the zoom: scene = self._scene.scene if scene is not None and self.reset_zoom: scene.reset_zoom() class ContourModuleFactory(DataModuleFactory): """ Base class for all the module factories with contours """ contours = Any(5, help="""Integer/list specifying number/list of contours. Specifying 0 shows no contours. Specifying a list of values will only give the requested contours asked for.""") def _contours_changed(self): contour_list = True try: len(self.contours) except TypeError: contour_list = False if contour_list: self._target.contour.contours = self.contours else: assert type(self.contours) == int, \ "The contours argument must be an integer" assert self.contours > 0, "The contours argument must be positive" self._target.contour.set(auto_contours=True, number_of_contours=self.contours) if hasattr(self._target, 'enable_contours'): self._target.enable_contours = True ############################################################################## class CutPlaneFactory(DataModuleFactory): """ Base class for modules with a cut plane. """ plane_orientation = Enum('x_axes', 'y_axes', 'z_axes', desc="""the orientation of the plane""") view_controls = Bool(True, adapts='implicit_plane.visible', desc=("Whether or not the controls of the " "cut plane are shown.")) def _plane_orientation_changed(self): choices = dict(x_axes=numpy.array([ 1., 0., 0.]), y_axes=numpy.array([ 0., 1., 0.]), z_axes=numpy.array([ 0., 0., 1.])) self._target.implicit_plane.normal = \ choices[self.plane_orientation] ############################################################################## # Concrete module classes ############################################################################## # The list of possible glyph modes glyph_mode_dict = {'2darrow': 0, '2dcircle':0, '2dcross':0, '2ddash': 0, '2ddiamond':0, '2dhooked_arrow':0, '2dsquare':0, '2dthick_arrow':0, '2dthick_cross':0, '2dtriangle':0, '2dvertex':0, 'arrow': 1, 'cone': 2, 'cylinder': 3, 'sphere': 4, 'cube': 5, 'axes': 6, 'point': 7} ############################################################################## class VectorsFactory(DataModuleFactory): """Applies the Vectors mayavi module to the given data object source (Mayavi source, or VTK dataset). """ _target = Instance(modules.Vectors, ()) scale_factor = CFloat(1., adapts='glyph.glyph.scale_factor', desc="""the scaling applied to the glyphs. The size of the glyph is by default in drawing units.""") scale_mode = Trait('vector', {'none':'data_scaling_off', 'scalar':'scale_by_scalar', 'vector':'scale_by_vector'}, help="""the scaling mode for the glyphs ('vector', 'scalar', or 'none').""") resolution = CInt(8, desc="The resolution of the glyph created. For " "spheres, for instance, this is the number of " "divisions along theta and phi.") mask_points = Either(None, CInt, desc="If supplied, only one out of 'mask_points' " "data point is displayed. This option is useful " "to reduce the number of points displayed " "on large datasets") def _resolution_changed(self): glyph = self._target.glyph.glyph_source.glyph_source if hasattr(glyph, 'theta_resolution'): glyph.theta_resolution = self.resolution if hasattr(glyph, 'phi_resolution'): glyph.phi_resolution = self.resolution if hasattr(glyph, 'resolution'): glyph.resolution = self.resolution if hasattr(glyph, 'shaft_resolution'): glyph.shaft_resolution = self.resolution if hasattr(glyph, 'tip_resolution'): glyph.tip_resolution = self.resolution def _mask_points_changed(self): if self.mask_points is not None: self._target.glyph.mask_input_points = True self._target.glyph.mask_points.on_ratio = self.mask_points def _scale_mode_changed(self): self._target.glyph.scale_mode = self.scale_mode_ mode = Trait('2darrow', glyph_mode_dict, desc="""the mode of the glyphs.""") def _mode_changed(self): v = self._target # Workaround for different version of VTK: if hasattr(v.glyph.glyph_source, 'glyph_source'): g = v.glyph.glyph_source else: g = v.glyph if self.mode == 'point': g.glyph_source = tvtk.PointSource(radius=0, number_of_points=1) else: g.glyph_source = g.glyph_list[self.mode_] if self.mode_ == 0: g.glyph_source.glyph_type = self.mode[2:] vectors = make_function(VectorsFactory) ############################################################################## class GlyphFactory(VectorsFactory): """Applies the Glyph mayavi module to the given VTK data source (Mayavi source, or VTK dataset). """ _target = Instance(modules.Glyph, ()) scale_mode = Trait('scalar', {'none':'data_scaling_off', 'scalar':'scale_by_scalar', 'vector':'scale_by_vector'}, help="""the scaling mode for the glyphs ('vector', 'scalar', or 'none').""") mode = Trait('sphere', glyph_mode_dict, desc="""the mode of the glyphs.""") glyph = make_function(GlyphFactory) ############################################################################## class StreamlineFactory(DataModuleFactory): """Applies the Streamline mayavi module to the given VTK data object.""" _target = Instance(modules.Streamline, ()) linetype = Trait('line', 'ribbon', 'tube', adapts='streamline_type', desc="""the type of line-like object used to display the streamline.""") seedtype = Trait('sphere', {'sphere':0, 'line':1, 'plane':2, 'point':3}, desc="""the widget used as a seed for the streamlines.""") seed_visible = Bool(True, adapts='seed.widget.enabled', desc="Control the visibility of the seed.", ) seed_scale = CFloat(1., desc="Scales the seed around its default center", ) seed_resolution = Either(None, CInt, desc='The resolution of the seed. Determines the number of ' 'seed points') integration_direction = Trait('forward', 'backward', 'both', adapts='stream_tracer.integration_direction', desc="The direction of the integration.", ) def _seedtype_changed(self): # XXX: this also acts for seed_scale and seed_resolution, but no # need to define explicit callbacks, as all the callbacks are # being called anyhow. self._target.seed.widget = widget = \ self._target.seed.widget_list[self.seedtype_] if not self.seed_scale==1.: widget.enabled = True if self.seedtype == 'line': p1 = widget.point1 p2 = widget.point2 center = (p1 + p2)/2. widget.point1 = center + self.seed_scale*(p1 - center) widget.point2 = center + self.seed_scale*(p2 - center) elif self.seedtype == 'plane': p1 = widget.point1 p2 = widget.point2 center = (p1 + p2)/2. o = widget.origin widget.point1 = center + self.seed_scale*(p1 - center) widget.point2 = center + self.seed_scale*(p2 - center) widget.origin = center + self.seed_scale*(o - center) elif self.seedtype == 'sphere': widget.radius *= self.seed_scale # XXX: Very ugly, but this is only way I have found to # propagate changes. self._target.seed.stop() self._target.seed.start() widget.enabled = self.seed_visible if self.seed_resolution is not None: widget.enabled = True if self.seedtype in ('plane', 'line'): widget.resolution = self.seed_resolution elif self.seedtype == 'sphere': widget.phi_resolution = widget.theta_resolution = \ self.seed_resolution # XXX: Very ugly, but this is only way I have found to # propagate changes. self._target.seed.stop() self._target.seed.start() widget.enabled = self.seed_visible streamline = make_function(StreamlineFactory) ############################################################################## class SurfaceFactory(DataModuleFactory): """Applies the Surface mayavi module to the given data source (Mayavi source, or VTK dataset). """ _target = Instance(modules.Surface, ()) representation = Trait('surface', 'wireframe', 'points', adapts='actor.property.representation', desc="""the representation type used for the surface.""") surface = make_function(SurfaceFactory) ############################################################################## class IsoSurfaceFactory(ContourModuleFactory): """Applies the IsoSurface mayavi module to the given data source (Mayavi source, or VTK dataset). """ _target = Instance(modules.IsoSurface, ()) iso_surface = make_function(IsoSurfaceFactory) ############################################################################## class ContourSurfaceFactory(ContourModuleFactory): """Applies the Surface mayavi module to the given data source (Mayavi source, or VTK dataset) and turns contours on. """ _target = Instance(modules.Surface, ()) def __init__(self, *args, **kwargs): """ Overriding the __init__ to turn contours on.""" super(ContourSurfaceFactory, self).__init__(*args, **kwargs) self._contours_changed() contour_surface = make_function(ContourSurfaceFactory) ############################################################################## class ImageActorFactory(DataModuleFactory): """Applies the ImageActor mayavi module to the given VTK data object.""" _target = Instance(modules.ImageActor, ()) interpolate = Bool(True, adapts='actor.interpolate', desc="""if the pixels in the image are to be interpolated or not.""") opacity = Range(0.0, 1.0, 1.0, adapts='actor.opacity', desc="""the opacity of the image.""") image_actor = make_function(ImageActorFactory) ############################################################################## class ImagePlaneWidgetFactory(DataModuleFactory): """ Applies the ImagePlaneWidget mayavi module to the given data source (Mayavi source, or VTK dataset). """ _target = Instance(modules.ImagePlaneWidget, ()) slice_index = CInt(0, adapts='ipw.slice_index', help="""The index along wich the image is sliced.""") plane_opacity = Range(0.0, 1.0, 1.0, adapts='ipw.plane_property.opacity', desc="""the opacity of the plane actor.""") plane_orientation = Enum('x_axes', 'y_axes', 'z_axes', adapts='ipw.plane_orientation', desc="""the orientation of the plane""") image_plane_widget = make_function(ImagePlaneWidgetFactory) ############################################################################## class ScalarCutPlaneFactory(CutPlaneFactory): """ Applies the ScalarCutPlane mayavi module to the given data source (Mayavi source, or VTK dataset). """ _target = Instance(modules.ScalarCutPlane, ()) scalar_cut_plane = make_function(ScalarCutPlaneFactory) ############################################################################## class VectorCutPlaneFactory(CutPlaneFactory, VectorsFactory): """ Applies the VectorCutPlane mayavi module to the given data source (Mayavi source, or VTK dataset). """ _target = Instance(modules.VectorCutPlane, ()) vector_cut_plane = make_function(VectorCutPlaneFactory) ############################################################################## class ContourGridPlaneFactory(ContourModuleFactory): """ Applies the ContourGridPlane mayavi module to the given data source (Mayavi source, or VTK dataset). """ _target = Instance(modules.ContourGridPlane, ()) contour_grid_plane = make_function(ContourGridPlaneFactory) ############################################################################## class CustomGridPlaneFactory(ContourModuleFactory): """ Applies the CustomGridPlane mayavi module to the given VTK data source (Mayavi source, or VTK dataset). """ _target = Instance(modules.CustomGridPlane, ()) custom_grid_plane = make_function(CustomGridPlaneFactory) ############################################################################## class VolumeFactory(PipeFactory): """ Applies the Volume mayavi module to the given VTK data source (Mayavi source, or VTK dataset). **Note** The range of the colormap can be changed simply using the vmin/vmax parameters (see below). For more complex modifications of the colormap, here is some pseudo code to change the ctf (color transfer function), or the otf (opacity transfer function):: vol = mlab.pipeline.volume(src) # Changing the ctf: from tvtk.util.ctf import ColorTransferFunction ctf = ColorTransferFunction() ctf.add_rgb_point(value, r, g, b) ctf.add_hsv_point(value, h, s, v) # ... vol._volume_property.set_color(ctf) vol._ctf = ctf vol.update_ctf = True # Changing the otf: from tvtk.util.ctf import PiecewiseFunction otf = PiecewiseFunction() otf.add_point(value, opacity) vol._otf = otf vol._volume_property.set_scalar_opacity(otf) """ color = Trait(None, None, TraitTuple(Range(0., 1.),Range(0., 1.),Range(0., 1.)), help="""the color of the vtk object. Overides the colormap, if any, when specified. This is specified as a triplet of float ranging from 0 to 1, eg (1, 1, 1) for white.""", ) vmin = Trait(None, None, CFloat, help="""vmin is used to scale the transparency gradient. If None, the min of the data will be used""") vmax = Trait(None, None, CFloat, help="""vmax is used to scale the transparency gradient. If None, the max of the data will be used""") _target = Instance(modules.Volume, ()) __ctf_rescaled = Bool(False) ###################################################################### # Non-public interface. ###################################################################### def _color_changed(self): if not self.color: return range_min, range_max = self._target.current_range from tvtk.util.ctf import ColorTransferFunction ctf = ColorTransferFunction() try: ctf.range = (range_min, range_max) except Exception: # VTK versions < 5.2 don't seem to need this. pass r, g, b = self.color ctf.add_rgb_point(range_min, r, g, b) ctf.add_rgb_point(range_max, r, g, b) self._target._ctf = ctf self._target._volume_property.set_color(ctf) self._target.update_ctf = True def _vmin_changed(self): vmin = self.vmin vmax = self.vmax range_min, range_max = self._target.current_range if vmin is None: vmin = range_min if vmax is None: vmax = range_max # Change the opacity function from tvtk.util.ctf import PiecewiseFunction, save_ctfs otf = PiecewiseFunction() if range_min < vmin: otf.add_point(range_min, 0.) if range_max > vmax: otf.add_point(range_max, 0.2) otf.add_point(vmin, 0.) otf.add_point(vmax, 0.2) self._target._otf = otf self._target._volume_property.set_scalar_opacity(otf) if self.color is None and not self.__ctf_rescaled and \ ( (self.vmin is not None) or (self.vmax is not None) ): # FIXME: We don't use 'rescale_ctfs' because it screws up the nodes. def _rescale_value(x): nx = (x - range_min)/(range_max - range_min) return vmin + nx*(vmax - vmin) # The range of the existing ctf can vary. scale_min, scale_max = self._target._ctf.range def _rescale_node(x): nx = (x - scale_min)/(scale_max - scale_min) return range_min + nx*(range_max - range_min) if hasattr(self._target._ctf, 'nodes'): rgb = list() for value in self._target._ctf.nodes: r, g, b = \ self._target._ctf.get_color(value) rgb.append((_rescale_node(value), r, g, b)) else: rgb = save_ctfs(self._target.volume_property)['rgb'] from tvtk.util.ctf import ColorTransferFunction ctf = ColorTransferFunction() try: ctf.range = (range_min, range_max) except Exception: # VTK versions < 5.2 don't seem to need this. pass rgb.sort() v = rgb[0] ctf.add_rgb_point(range_min, v[1], v[2], v[3]) for v in rgb: ctf.add_rgb_point(_rescale_value(v[0]), v[1], v[2], v[3]) ctf.add_rgb_point(range_max, v[1], v[2], v[3]) self._target._ctf = ctf self._target._volume_property.set_color(ctf) self.__ctf_rescaled = True self._target.update_ctf = True # This is not necessary: the job is already done by _vmin_changed #_vmax_changed = _vmin_changed volume = make_function(VolumeFactory) ############################################################################ # Automatically generated modules from registry. ############################################################################ class _AutomaticModuleFactory(DataModuleFactory): """The base class for any auto-generated factory classes. NOTE: This class requires the `_metadata` trait be set to the metadata object for the object for which this is a factory. """ # The target. _target = Property # The saved target that is created once and then always returned. _saved_target = Any(None) def _get__target(self): """Getter for the _target trait.""" if self._saved_target is None: self._saved_target = self._metadata.get_callable()() return self._saved_target def _make_functions(namespace): """Make the functions for adding modules and add them to the namespace automatically. """ # Ignore these since they are already provided. ignore = ['axes', 'text', 'orientation_axes'] for mod in registry.modules: func_name = camel2enthought(mod.id) class_name = mod.id if func_name.endswith('_module'): func_name = func_name[:-7] class_name = class_name[:-6] class_name = class_name + 'Factory' # Don't create any that are already defined or ignored. if class_name in namespace or func_name in ignore: continue # The class to wrap. klass = new.classobj(class_name, (_AutomaticModuleFactory,), {'__doc__': mod.help,} ) klass._metadata = mod # The mlab helper function. func = make_function(klass) # Inject class/function into the namespace and __all__. namespace[class_name] = klass namespace[func_name] = func __all__.append(func_name) # Create the module related functions. _make_functions(locals()) mayavi-4.1.0/mayavi/tools/auto_doc.py0000644000175100001440000000431511674464502020574 0ustar ischnellusers00000000000000""" Automatic documentation from traited objects. """ # Author: Gael Varoquaux # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. from textwrap import wrap, dedent import types old_dedent = dedent def dedent(text): """ Removes as much indentation as possible from some text, but does not modify the first line. """ text_lines = [line.rstrip() for line in text.split("\n") ] if len(text_lines)>0: return text_lines[0] + "\n" + old_dedent( "\n".join(text_lines[1:]) ) else: return text def make_doc(klass): """ Builds a docstring from the object's docstring, and it's traits help. """ if hasattr(klass, '__doc__'): doc = dedent(klass.__doc__) + "\n" else: doc = "" doc += dedent("""**Keyword arguments:**""") traits = klass.class_traits().copy() traits.pop('trait_added') traits.pop('trait_modified') doc += traits_doc(traits) return doc def traits_doc(traits): doc = "" traits_names = traits.keys() traits_names.sort() for trait_name in traits_names: trait_obj = traits[trait_name] if not trait_name[0] == '_': doc += format_argument(trait_name, trait_obj) return doc def format_argument(trait_name, trait_obj): doc = "\n :%s: " % trait_name pad = "\n" + (len(doc)-1) * " " help = trait_obj.help if help is not None: arg_desc = help else: arg_desc = '' desc = trait_obj.desc if desc is not None: arg_desc += desc.rstrip() handler = trait_obj.handler if handler is not None: if ( not hasattr(handler, 'aType') or not handler.aType in (types.IntType, types.FloatType)): # These types are simple enough arg_desc += ' Must be %s.' % handler.info() default = trait_obj.default_value()[1] if not default in ('', None) and not trait_obj.array: arg_desc = arg_desc.rstrip() + " Default: %s" % str(default) desc_width = 75 - len(doc) for line in wrap(arg_desc, width=desc_width): doc += line doc += pad return doc mayavi-4.1.0/mayavi/tools/filters.py0000644000175100001440000001714011674464502020447 0ustar ischnellusers00000000000000""" Filter factories and their associated functions for mlab. Module functions meant to be applied to a data source object should take only one positional argument, the data object, to be easily used in helper functions. """ # Author: Gael Varoquaux # Prabhu Ramachandran # Copyright (c) 2007-2008, Enthought, Inc. # License: BSD Style. import new from traits.api import Instance, CFloat, CInt, CArray, Trait, \ Enum, Property, Any, String from tvtk.common import camel2enthought from tvtk.api import tvtk import mayavi.filters.api as filters from mayavi.core.registry import registry from pipe_base import PipeFactory, make_function # This the list is dynamically populated further down below at the end. __all__ = [ 'tube', 'warp_scalar', 'threshold', 'elevation_filter', 'set_active_attribute', 'user_defined' ] ############################################################################## class TubeFactory(PipeFactory): """Applies the Tube mayavi filter to the given VTK object.""" _target = Instance(filters.Tube, ()) tube_sides = CInt(6, adapts='filter.number_of_sides', desc = """number of sides of the tubes used to represent the lines.""") tube_radius = CFloat(0.05, adapts='filter.radius', desc = """radius of the tubes used to represent the lines.""") tube = make_function(TubeFactory) ############################################################################## class WarpScalarFactory(PipeFactory): """Applies the WarpScalar mayavi filter to the given VTK object.""" _target = Instance(filters.WarpScalar, ()) warp_scale = CFloat(1.0, adapts="filter.scale_factor", help="scale of the warp scalar") warp_scalar = make_function(WarpScalarFactory) ############################################################################## class ThresholdFactory(PipeFactory): """Applies the Threshold mayavi filter to the given VTK object.""" _target = Instance(filters.Threshold, ()) filter_type = Enum('cells', 'points', adapts='filter_type', help="If threshold is put on cells or points") low = Trait(None, None, CFloat, help="The lower threshold") def _low_changed(self): if self.low == None: pass else: self._target.lower_threshold = self.low up = Trait(None, None, CFloat, help="The upper threshold") def _up_changed(self): if self.up == None: pass else: self._target.upper_threshold = self.up threshold = make_function(ThresholdFactory) ############################################################################## class ElevationFilterFactory(PipeFactory): """Applies the Elevation Filter mayavi filter to the given VTK object.""" high_point = CArray(default=[0, 0, 1], shape=(3,), adapts="filter.high_point", help="The end point of the projection line") low_point = CArray(default=[0, 0, 0], shape=(3,), adapts="filter.low_point", help="The start point of the projection line") _target = Instance(filters.ElevationFilter, ()) elevation_filter = make_function(ElevationFilterFactory) ############################################################################## class SetActiveAttributeFactory(PipeFactory): """ Applies the SetActiveAttribute Filter mayavi filter to the given VTK object. """ point_scalars = String( adapts="point_scalars_name", help="The name of the active point scalars") point_vectors = String( adapts="point_vectors_name", help="The name of the active point vectors") point_tensors = String( adapts="point_tensors_name", help="The name of the active point tensors") cell_scalars = String( adapts="cell_scalars_name", help="The name of the active cell scalars") cell_vectors = String( adapts="cell_vectors_name", help="The name of the active cell vectors") cell_tensors = String( adapts="cell_tensors_name", help="The name of the active cell tensors") _target = Instance(filters.SetActiveAttribute, ()) set_active_attribute = make_function(SetActiveAttributeFactory) ############################################################################## class UserDefinedFactory(PipeFactory): """Applies the UserDefined mayavi filter to the given TVTK object.""" _target = Instance(filters.UserDefined, ()) filter = Instance(tvtk.Object, adapts="filter", help="the tvtk filter to adapt. This" "be either an instance of the filter, or the" "name of this filter.") def __init__(self, parent, **kwargs): if 'filter' in kwargs: filter = kwargs['filter'] if not isinstance(filter, tvtk.Object): try: filter = getattr(tvtk, filter) except AttributeError: raise Exception('Filter %s unknown to TVTK' % filter) kwargs['filter'] = filter() self._target.filter = kwargs['filter'] self._target.setup_filter() else: self._target.filter = kwargs['filter'] if not 'name' in kwargs: kwargs['name'] = 'UserDefined(%s)' % \ kwargs['filter'].__class__.__name__ super(UserDefinedFactory, self).__init__(parent, **kwargs) user_defined = make_function(UserDefinedFactory) ############################################################################ # Automatically generated filters from registry. ############################################################################ class _AutomaticFilterFactory(PipeFactory): """The base class for any auto-generated factory classes. NOTE: This class requires that the `_metadata` trait be set to the metadata object for the object for which this is a factory. """ # The target. _target = Property # The saved target that is created once and then always returned. _saved_target = Any(None) def _get__target(self): """Getter for the _target trait.""" if self._saved_target is None: self._saved_target = self._metadata.get_callable()() return self._saved_target def _make_functions(namespace): """Make the functions for adding filters and add them to the namespace automatically. """ for fil in registry.filters: func_name = camel2enthought(fil.id) class_name = fil.id if func_name.endswith('_filter'): func_name = func_name[:-7] class_name = class_name[:-6] class_name = class_name + 'Factory' # Don't create any that are already defined. if class_name in namespace: continue # The class to wrap. klass = new.classobj(class_name, (_AutomaticFilterFactory,), {'__doc__': fil.help,} ) klass._metadata = fil # The mlab helper function. func = make_function(klass) # Inject class/function into the namespace and __all__. namespace[class_name] = klass namespace[func_name] = func __all__.append(func_name) # Create the module related functions. _make_functions(locals()) mayavi-4.1.0/mayavi/tools/engine_manager.py0000644000175100001440000001647511674464502021750 0ustar ischnellusers00000000000000""" Central registry for figures with mlab. """ # Standard library imports import warnings # Enthought librairies imports from traits.api import HasTraits, Instance # Local imports from mayavi.preferences.api import preference_manager from mayavi.core.registry import registry from mayavi.core.engine import Engine from mayavi.core.off_screen_engine import OffScreenEngine from mayavi.core.null_engine import NullEngine from mayavi.core.common import process_ui_events from preferences_mirror import PreferencesMirror # The mlab options. options = PreferencesMirror() options.preferences = preference_manager.mlab ###################################################################### def check_backend(): """ Check if either we are in test mode, or if there is a suitable traits backend installed. """ from traitsui.toolkit import toolkit from traits.etsconfig.api import ETSConfig from mayavi.tools.engine_manager import options toolkit() # This forces the selection of a toolkit. if (options.backend != 'test' and options.offscreen != True) and \ ETSConfig.toolkit in ('null', ''): raise ImportError, '''Could not import backend for traits ________________________________________________________________________________ Make sure that you have either the TraitsBackendWx or the TraitsBackendQt projects installed. If you installed Mayavi with easy_install, try easy_install . easy_install Mayavi[app] will also work. If you performed a source checkout, be sure to run 'python setup.py install' in Traits, TraitsGUI, and the Traits backend of your choice. Also make sure that either wxPython or PyQT is installed. wxPython: http://www.wxpython.org/ PyQT: http://www.riverbankcomputing.co.uk/software/pyqt/intro ''' ################################################################################ # `EngineManager` class. ################################################################################ class EngineManager(HasTraits): """ Central registry for figures with mlab. This is a container for a list of engines having declared themselves as usable by mlab. This object is meant to be a thin wrapper on top of the different Engine classes, making sure that mlab knows how to start an engine and get a figure. """ current_engine = Instance(Engine) def get_engine(self): """ Returns an engine in agreement with the options. """ # First check if the current engine is running and if it is in # the registered engines. ce = self.current_engine if ce is not None: if not ce.running or ce not in registry.engines.values(): self.current_engine = None if self.current_engine is not None: engines = list((self.current_engine,)) else: engines = list() engines.extend(registry.engines.values()) if options.backend == 'auto': suitable = [e for e in engines if e.__class__.__name__ != 'NullEngine'] elif options.backend == 'envisage': suitable = [e for e in engines if e.__class__.__name__ == 'EnvisageEngine'] elif options.backend == 'test': suitable = [e for e in engines if e.__class__.__name__ == 'NullEngine'] else: suitable = [e for e in engines if e.__class__.__name__ == 'Engine'] if len(suitable) == 0: return self.new_engine() else: # Return the most engine add to the list most recently. self.current_engine = suitable[-1] return suitable[-1] def get_null_engine(self): """Return a suitable null engine and make that the current engine. """ # First check if the current engine is running and if it is in # the registered engines. ce = self.current_engine if ce is not None: if not ce.running or ce not in registry.engines.values(): self.current_engine = None if self.current_engine is not None: engines = list((self.current_engine,)) else: engines = list() engines.extend(registry.engines.values()) engine = None for e in engines: if e.__class__.__name__ == 'NullEngine': engine = e break else: engine = NullEngine(name='Null Mlab Engine') engine.start() self.current_engine = engine return engine def set_engine(self, engine): """ Sets the mlab engine. """ if not engine.running: warnings.warn('Engine is not running', stacklevel=2) self.current_engine = engine registry.register_engine(engine) def new_engine(self): """ Creates a new engine, envisage or not depending on the options. """ check_backend() if options.backend == 'envisage': from mayavi.plugins.app import Mayavi m = Mayavi(start_gui_event_loop=False) m.main() process_ui_events() window = m.application.workbench.active_window engine = window.get_service(Engine) elif options.backend == 'test': engine = NullEngine(name='Null Mlab Engine') engine.start() else: if options.offscreen: engine = OffScreenEngine(name='Mlab offscreen Engine') engine.start() else: engine = Engine(name='Mlab Engine') engine.start() self.current_engine = engine return engine def find_figure_engine(self, fig): """ Find the engine corresponding to a given mayavi scene. """ for engine in registry.engines.values(): if fig in engine.scenes: return engine else: raise TypeError, "Figure not attached to a mayavi engine." def show_engine(self, engine=None, rich_view=True): """ Show a dialog with the mayavi pipeline. This dialog allows to edit graphicaly the properties of the different objects on the scenes. """ if engine is None: engine = self.get_engine() if engine.__class__.__name__ == 'EnvisageEngine' or \ options.backend == 'test': # FIXME: This should pop up the relevent envisage view pass elif rich_view: from mayavi.core.ui.engine_rich_view import \ EngineRichView figure = engine.current_scene view = EngineRichView(engine=engine) if figure is None: scene = None else: scene = figure.scene return view.scene_editing_view(scene=scene) else: from mayavi.core.ui.engine_view import \ EngineView scene = engine.current_scene view = EngineView(engine=engine) return view.edit_traits() engine_manager = EngineManager() get_engine = engine_manager.get_engine get_null_engine = engine_manager.get_null_engine set_engine = engine_manager.set_engine show_pipeline = engine_manager.show_engine mayavi-4.1.0/mayavi/tools/server.py0000644000175100001440000002212011674464502020277 0ustar ischnellusers00000000000000""" Powerful utility for running a TCP/UDP server that is used to script Mayavi2 from the network. This uses Twisted. This particular version has been written for the wxPython, adding support for a Qt4 version should be trivial. The key functions exposed are:: serve_tcp(...) serve_udp(...) See the function documentation for more information. Here is sample usage:: from mayavi import mlab from mayavi.tools import server mlab.test_plot3d() server.serve_tcp() The TCP server will listen on port 8007 by default in the above. Any data sent to the server is simply exec'd, meaning you can do pretty much anything you want. The `engine`, `scene`, `camera` and `mlab` are all available and can be used. For example after running the above you can do this:: $ telnet localhost 8007 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. scene.camera.azimuth(45) mlab.clf() mlab.test_contour3d() scene.camera.zoom(1.5) The nice thing about this is that you do not loose any interactivity of your app and can continue to use its UI as before, any network commands will be simply run on top of this. **Warning** while this is very powerful it is also a **huge security hole** since the remote user can do pretty much anything they want. """ # Author: Prabhu Ramachandran # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. import sys import wx # Install wxreactor; must be done before the reactor is imported below. from twisted.internet import wxreactor wxreactor.install() # The usual twisted imports. from twisted.internet.protocol import Protocol, DatagramProtocol, Factory from twisted.internet import reactor from twisted.python import log ################################################################################ # `M2UDP` protocol. ################################################################################ class M2UDP(DatagramProtocol): """Implements a brain dead but supremely powerful UDP API. Any data coming in is simply exec'd. Meaning you can do pretty much anything you want. The `engine`, `scene`, `camera` and `mlab` are all available and can be used. For example you can easily send this on the network:: scene.camera.azimuth(45) mlab.clf() mlab.test_contour3d() scene.camera.zoom(1.5) And these will run just fine retaining the full interactivity of the mayavi app. """ def datagramReceived(self, data, (host, port)): """Given a line of data, simply execs it to do whatever.""" log.msg("Received: %r from %s:%d" % (data, host, port)) c = data.strip() if len(c) > 0: mlab = self.mlab engine = self.engine scene = self.scene camera = scene.camera try: exec c in locals(), globals() except: log.err() scene.render() ################################################################################ # `M2TCP` protocol ################################################################################ class M2TCP(Protocol): """Implements a brain dead but suprememly powerful TCP API. Any data coming in is simply exec'd. Meaning you can do pretty much anything you want. The `engine`, `scene`, `camera` and `mlab` are all available and can be used. For example you can easily send this on the network:: scene.camera.azimuth(45) mlab.clf() mlab.test_contour3d() scene.camera.zoom(1.5) And these will run just fine retaining the full interactivity of the mayavi app. """ # Maximum number of concurrent connections allowed. maxConnect = 1 def connectionMade(self): log.msg('ConnectionMade') self.factory.numConnect += 1 if self.factory.numConnect > self.maxConnect: self.transport.write("Server already in use, try later\n") self.transport.loseConnection() def connectionLost(self, reason): log.msg('ConnectionLost') self.factory.numConnect -= 1 def dataReceived(self, data): """Given a line of data, simply execs it to do whatever.""" c = data.strip() log.msg('Received:', c) if len(c) > 0: mlab = self.factory.mlab engine = self.factory.engine scene = self.factory.scene camera = scene.camera try: exec c in locals(), globals() except: log.err() scene.render() ################################################################################ # Utility functions. ################################################################################ def serve_udp(engine=None, port=9007, logto=sys.stdout): """Serve the `M2UDP` protocol using the given `engine` on the specified `port` logging messages to given `logto` which is a file-like object. This function will block till the service is closed. There is no need to call `mlab.show()` after or before this. The Mayavi UI will be fully responsive. **Parameters** :engine: Mayavi engine to use. If this is `None`, `mlab.get_engine()` is used to find an appropriate engine. :port: int: port to serve on. :logto: file : File like object to log messages to. If this is `None` it disables logging. **Examples** Here is a very simple example:: from mayavi import mlab from mayavi.tools import server mlab.test_plot3d() server.serve_udp() Test it like so:: import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(('', 9008)) s.sendto('camera.azimuth(10)', ('', 9007)) **Warning** Data sent is exec'd so this is a security hole. """ from mayavi import mlab e = engine or mlab.get_engine() # Setup the protocol with the right attributes. proto = M2UDP() proto.engine = e proto.scene = e.current_scene.scene proto.mlab = mlab if logto is not None: log.startLogging(logto) log.msg('Serving Mayavi2 UDP server on port', port) log.msg('Using Engine', e) # Register the running wxApp. reactor.registerWxApp(wx.GetApp()) # Listen on port 9007 using above protocol. reactor.listenUDP(port, proto) # Run the server + app. This will block. reactor.run() def serve_tcp(engine=None, port=8007, logto=sys.stdout, max_connect=1): """Serve the `M2TCP` protocol using the given `engine` on the specified `port` logging messages to given `logto` which is a file-like object. This function will block till the service is closed. There is no need to call `mlab.show()` after or before this. The Mayavi UI will be fully responsive. **Parameters** :engine: Mayavi engine to use. If this is `None`, `mlab.get_engine()` is used to find an appropriate engine. :port: int: port to serve on. :logto: file: File like object to log messages to. If this is `None` it disables logging. :max_connect: int: Maximum number of simultaneous connections to support. **Examples** Here is a very simple example:: from mayavi import mlab from mayavi.tools import server mlab.test_plot3d() server.serve_tcp() The TCP server will listen on port 8007 by default in the above. Any data sent to the server is simply exec'd, meaning you can do pretty much anything you want. The `engine`, `scene`, `camera` and `mlab` are all available and can be used. For example after running the above you can do this:: $ telnet localhost 8007 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. scene.camera.azimuth(45) mlab.clf() mlab.test_contour3d() scene.camera.zoom(1.5) **Warning** Data sent is exec'd so this is a security hole. """ from mayavi import mlab e = engine or mlab.get_engine() # Setup the factory with the right attributes. factory = Factory() factory.protocol = M2TCP factory.maxConnect = max_connect factory.numConnect = 0 factory.engine = e factory.scene = e.current_scene.scene factory.mlab = mlab if logto is not None: log.startLogging(logto) log.msg('Serving Mayavi2 TCP server on port', port) log.msg('Using Engine', e) # Register the running wxApp. reactor.registerWxApp(wx.GetApp()) # Listen on port 9007 using above protocol. reactor.listenTCP(port, factory) # Run the server + app. This will block. reactor.run() ################################################################################ # Examples and tests. ################################################################################ def test_tcp(): """Simple test for the TCP server.""" from mayavi import mlab mlab.test_plot3d() serve_tcp() def test_udp(): """Simple test for the UDP server.""" from mayavi import mlab mlab.test_plot3d() serve_udp() if __name__ == '__main__': test_tcp() mayavi-4.1.0/mayavi/tools/pipe_base.py0000644000175100001440000001667511674464502020742 0ustar ischnellusers00000000000000""" Base class for factories for adding objects to the pipeline. """ # Author: Gael Varoquaux # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. import warnings from auto_doc import make_doc from traits.api import HasPrivateTraits, Str, TraitError,\ Instance, Any, Bool from mayavi.core.filter import Filter from mayavi.core.engine import Engine from mayavi.core.source import Source from mayavi.core.scene import Scene from mayavi.core.module_manager import ModuleManager from tvtk.api import tvtk import tools from engine_manager import get_engine def get_obj(obj, components): """ Get the target object for the specified components. """ for component in components: obj = getattr(obj, component) return obj def make_function(factory_class): def the_function(*args, **kwargs): factory = factory_class(*args, **kwargs) return factory._target the_function.__doc__ = make_doc(factory_class) the_function.func_name = factory_class.__name__.lower() return the_function def get_module_manager(obj): """ Returns the module manager that would be used when a module is added on the given object, if any, and None elsewhere. """ if hasattr(obj, 'module_manager'): return obj.module_manager elif isinstance(obj, ModuleManager): return obj for child in reversed(obj.children): if isinstance(child, ModuleManager): return child else: return None ############################################################################## class PipeFactory(HasPrivateTraits): """ Base class for all factories adding pipes on the pipeline """ name = Str(adapts='name', help='the name of the vtk object created.') figure = Instance(Scene) _engine = Instance(Engine, help=('the figure on which the object ' 'should be added')) _target = Any _do_redraw = Bool def add_module(self, parent, kwargs=dict()): """ Add the target module to the given object. """ # We check to see if the module-manager-related option require to # add a new module manager: if parent is not None: module_manager = get_module_manager(parent) if (module_manager is not None and len(module_manager.children) > 0): scalar_lut = module_manager.scalar_lut_manager vector_lut = module_manager.vector_lut_manager if 'vmin' in kwargs: if not scalar_lut.use_default_range and \ kwargs['vmin'] != scalar_lut.data_range[0]: parent = self._engine.add_module(ModuleManager(), module_manager.parent) elif not scalar_lut.use_default_range and \ kwargs['vmin'] != scalar_lut.data_range[0]: parent = self._engine.add_module(ModuleManager(), module_manager.parent) elif 'vmax' in kwargs: if not scalar_lut.use_default_range and \ kwargs['vmax'] != scalar_lut.data_range[1]: parent = self._engine.add_module(ModuleManager(), module_manager.parent) elif not scalar_lut.use_default_range and \ kwargs['vmax'] != scalar_lut.data_range[1]: parent = self._engine.add_module(ModuleManager(), module_manager.parent) elif 'colormap' in kwargs: cmap = kwargs['colormap'] if ( scalar_lut.lut_mode != cmap or vector_lut.lut_mode != cmap): parent = self._engine.add_module(ModuleManager(), module_manager.parent) self._engine.add_module(self._target, obj=parent) def __init__(self, parent, **kwargs): # We are not passing the traits to the parent class super(PipeFactory, self).__init__() # Try to find the right engine and scene to work with ancester = parent while hasattr(ancester, 'parent'): ancester = getattr(ancester, 'parent') if isinstance(ancester, Scene): self._scene = ancester self._engine = ancester.parent break else: if self.figure is not None: self._scene = self.figure else: self._scene = tools.gcf() self._engine = get_engine() scene = self._scene.scene if self.figure is not None and self.figure is not self._scene: warnings.warn('Trying to add a module on the wrong scene') if isinstance(parent, (Source, tvtk.DataSet)) \ and not isinstance(parent, Filter) and scene is not None: # Search the current scene to see if the source is already # in it, if not add it. if not parent in self._scene.children: parent = tools.add_dataset(parent, figure=self._scene) if scene is not None: self._do_redraw = not scene.disable_render scene.disable_render = True if issubclass(self._target.__class__, Filter): self._engine.add_filter(self._target, obj=parent) else: self.add_module(parent, kwargs) # Inject the magical mlab source trait. if hasattr(parent, 'mlab_source'): ms = parent.mlab_source self._target.add_trait('mlab_source', Instance(ms.__class__)) self._target.mlab_source = ms traits = self.get(self.class_trait_names()) [traits.pop(key) for key in traits.keys() if key[0]=='_' or key is None] traits.update(kwargs) # Now calling the traits setter, so that traits handlers are # called self.set(**traits) if scene is not None: scene.disable_render = not self._do_redraw def set(self, trait_change_notify=True, **traits): """ Same as HasTraits.set except that notification is forced, unless trait_change_notify==False""" HasPrivateTraits.set(self, trait_change_notify=trait_change_notify, **traits) if trait_change_notify==False: return for trait in traits.iterkeys(): callback = getattr(self, '_%s_changed' % trait) value = getattr(self, trait) try: if callback is not None: callback() self._anytrait_changed(trait, value) except TraitError: if value==None: # This means "default" pass else: raise def _anytrait_changed(self, name, value): """ This is where we implement the adaptation code. """ trait = self.trait(name) if name[0] == '_': # Private attribute return # hasattr(traits, "adapts") always returns True :-<. if not trait.adapts==None: components = trait.adapts.split('.') obj = get_obj(self._target, components[:-1]) setattr(obj, components[-1], value) mayavi-4.1.0/mayavi/tools/animator.py0000644000175100001440000001402611674464502020611 0ustar ischnellusers00000000000000""" Simple utility code for animations. """ # Author: Prabhu Ramachandran # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. import types from pyface.timer.api import Timer from traits.api import HasTraits, Button, Instance, Range from traitsui.api import View, Group, Item ################################################################################ # `Animator` class. ################################################################################ class Animator(HasTraits): """ Convenience class to manage a timer and present a convenient UI. This is based on the code in `tvtk.tools.visual`. Here is a simple example of using this class:: >>> from mayavi import mlab >>> def anim(): ... f = mlab.gcf() ... while 1: ... f.scene.camera.azimuth(10) ... f.scene.render() ... yield ... >>> anim = anim() >>> t = Animator(500, anim.next) >>> t.edit_traits() This makes it very easy to animate your visualizations and control it from a simple UI. **Notes** If you want to modify the data plotted by an `mlab` function call, please refer to the section on: :ref:`mlab-animating-data` """ ######################################## # Traits. start = Button('Start Animation') stop = Button('Stop Animation') delay = Range(10, 100000, 500, desc='frequency with which timer is called') # The internal timer we manage. timer = Instance(Timer) ###################################################################### # User interface view traits_view = View(Group(Item('start'), Item('stop'), show_labels = False ), Item('_'), Item(name = 'delay'), title = 'Animation Controller', buttons = ['OK']) ###################################################################### # Initialize object def __init__(self, millisec, callable, *args, **kwargs): """Constructor. **Parameters** :millisec: int specifying the delay in milliseconds between calls to the callable. :callable: callable function to call after the specified delay. :\*args: optional arguments to be passed to the callable. :\*\*kwargs: optional keyword arguments to be passed to the callable. """ HasTraits.__init__(self) self.delay = millisec self.timer = Timer(millisec, callable, *args, **kwargs) ###################################################################### # Non-public methods, Event handlers def _start_fired(self): self.timer.Start(self.delay) def _stop_fired(self): self.timer.Stop() def _delay_changed(self, value): t = self.timer if t is None: return if t.IsRunning(): t.Stop() t.Start(value) ################################################################################ # Decorators. def animate(func=None, delay=500, ui=True): """ A convenient decorator to animate a generator that performs an animation. The `delay` parameter specifies the delay (in milliseconds) between calls to the decorated function. If `ui` is True, then a simple UI for the animator is also popped up. The decorated function will return the `Animator` instance used and a user may call its `Stop` method to stop the animation. If an ordinary function is decorated a `TypeError` will be raised. **Parameters** :delay: int specifying the time interval in milliseconds between calls to the function. :ui: bool specifying if a UI controlling the animation is to be provided. **Returns** The decorated function returns an `Animator` instance. **Examples** Here is the example provided in the Animator class documentation:: >>> from mayavi import mlab >>> @mlab.animate ... def anim(): ... f = mlab.gcf() ... while 1: ... f.scene.camera.azimuth(10) ... f.scene.render() ... yield ... >>> a = anim() # Starts the animation. For more specialized use you can pass arguments to the decorator:: >>> from mayavi import mlab >>> @mlab.animate(delay=500, ui=False) ... def anim(): ... f = mlab.gcf() ... while 1: ... f.scene.camera.azimuth(10) ... f.scene.render() ... yield ... >>> a = anim() # Starts the animation without a UI. **Notes** If you want to modify the data plotted by an `mlab` function call, please refer to the section on: :ref:`mlab-animating-data`. """ class Wrapper(object): # The wrapper which calls the decorated function. def __init__(self, function): self.func = function self.ui = ui self.delay = delay def __call__(self, *args, **kw): f = self.func(*args, **kw) if isinstance(f, types.GeneratorType): a = Animator(self.delay, f.next) if self.ui: a.edit_traits() return a else: msg = 'The function "%s" must be a generator '\ '(use yield)!'%(self.func.__name__) raise TypeError(msg) def _wrapper1(function): # Needed to create the Wrapper in the right scope. w = Wrapper(function) return w if func is None: return _wrapper1 else: return _wrapper1(func) mayavi-4.1.0/mayavi/tools/helper_functions.py0000644000175100001440000011315711674464502022353 0ustar ischnellusers00000000000000""" Helper functions for mlab. These combine creation of the data sources, and applying the modules to them to make standard visualization operation. They should always return the module object created, for consistency, and because retrieving the vtk data source from a module object is possible via tools.get_vtk_src Each helper function should have a test function associated with it, both for testing and to ilustrate its use. """ # Author: Gael Varoquaux # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. from modules import VectorsFactory, StreamlineFactory, GlyphFactory, \ IsoSurfaceFactory, SurfaceFactory, ContourSurfaceFactory, \ ImageActorFactory, glyph_mode_dict from sources import vector_scatter, vector_field, scalar_scatter, \ scalar_field, line_source, array2d_source, grid_source, \ triangular_mesh_source, vertical_vectors_source from filters import ExtractVectorNormFactory, WarpScalarFactory, \ TubeFactory, ExtractEdgesFactory, PolyDataNormalsFactory, \ StripperFactory from mayavi.core.scene import Scene from auto_doc import traits_doc, dedent import tools from traits.api import Array, Callable, CFloat, HasTraits, \ List, Trait, Any, Instance, TraitError, true import numpy def document_pipeline(pipeline): def the_function(*args, **kwargs): return pipeline(*args, **kwargs) if hasattr(pipeline, 'doc'): doc = pipeline.doc elif pipeline.__doc__ is not None: doc = pipeline.__doc__ else: doc = '' the_function.__doc__ = dedent("""%s **Keyword arguments:** %s""") % ( dedent(doc), traits_doc(pipeline.get_all_traits()), ) return the_function ############################################################################# class Pipeline(HasTraits): """ Function used to build pipelines for helper functions """ #doc = '' _source_function = Callable() _pipeline = List() # Traits here only for documentation purposes figure = Instance('mayavi.core.scene.Scene', help='Figure to populate.') def __call__(self, *args, **kwargs): """ Calls the logics of the factory, but only after disabling rendering, if needed. """ # First retrieve the scene, if any. if 'figure' in kwargs: figure = kwargs['figure'] assert isinstance(figure, (Scene, None)) scene = figure.scene else: scene = tools.gcf().scene if scene is not None: self._do_redraw = not scene.disable_render scene.disable_render = True # Then call the real logic output = self.__call_internal__(*args, **kwargs) # And re-enable the rendering, if needed. if scene is not None: scene.disable_render = not self._do_redraw return output def __call_internal__(self, *args, **kwargs): """ Builds the source and runs through the pipeline, returning the last object created by the pipeline.""" self.store_kwargs(kwargs) self.source = self._source_function(*args, **kwargs) # Copy the pipeline so as not to modify it for the next call self.pipeline = self._pipeline[:] return self.build_pipeline() def store_kwargs(self, kwargs): """ Merges the given keyword argument, with traits default and store the resulting dictionary in self.kwargs.""" kwargs = kwargs.copy() name = kwargs.pop('name', None) all_traits = self.get_all_traits() if not set(kwargs.keys()).issubset(all_traits.keys()): raise ValueError, "Invalid keyword arguments : %s" % \ ', '.join(str(k) for k in set(kwargs.keys()).difference(all_traits.keys()) ) traits = self.get(self.class_trait_names()) [traits.pop(key) for key in traits.keys() if key[0]=='_' ] traits.update(kwargs) self.kwargs = traits def build_pipeline(self): """ Runs through the pipeline, applying pipe after pipe. """ object = self.source for pipe in self.pipeline: keywords = set(pipe.class_trait_names()) keywords.remove('trait_added') keywords.remove('trait_modified') this_kwargs = {} for key, value in self.kwargs.iteritems(): if key in keywords: this_kwargs[key] = value object = pipe(object, **this_kwargs)._target return object def get_all_traits(self): """ Returns all the traits of class, and the classes in the pipeline. """ traits = {} for pipe in self._pipeline: traits.update(pipe.class_traits()) traits.update(self.class_traits()) traits.pop('trait_added') traits.pop('trait_modified') return traits ############################################################################# class Points3d(Pipeline): """ Plots glyphs (like points) at the position of the supplied data. **Function signatures**:: points3d(x, y, z...) points3d(x, y, z, s, ...) points3d(x, y, z, f, ...) x, y and z are numpy arrays, or lists, all of the same shape, giving the positions of the points. If only 3 arrays x, y, z are given, all the points are drawn with the same size and color. In addition, you can pass a fourth array s of the same shape as x, y, and z giving an associated scalar value for each point, or a function f(x, y, z) returning the scalar value. This scalar value can be used to modulate the color and the size of the points.""" _source_function = Callable(scalar_scatter) _pipeline = [GlyphFactory, ] scale_factor = Any('auto', help='The scaling applied to the glyphs. ' 'the size of the glyph is by default calculated ' 'from the inter-glyph spacing. Specify a float to ' 'give the maximum glyph size in drawing units' ) def __call_internal__(self, *args, **kwargs): """ Override the call to be able to scale automatically the glyphs. """ scale_factor = kwargs.get('scale_factor', 'auto') if scale_factor == 'auto': kwargs['scale_factor'] = 1 g = Pipeline.__call_internal__(self, *args, **kwargs) if scale_factor == 'auto': g.glyph.glyph.scale_factor = \ tools._typical_distance(g.mlab_source.dataset) g.glyph.glyph.clamping = True else: g.glyph.glyph.clamping = False return g points3d = document_pipeline(Points3d()) def test_points3d(): t = numpy.linspace(0, 4*numpy.pi, 20) cos = numpy.cos sin = numpy.sin x = sin(2*t) y = cos(t) z = cos(2*t) s = 2+sin(t) return points3d(x, y, z, s, colormap="copper", scale_factor=.25) def test_points3d_anim(): """Animates the test_points3d example.""" g = test_points3d() t = numpy.linspace(0, 4*numpy.pi, 20) # Animate the points3d. ms = g.mlab_source for i in range(10): ms.z = numpy.cos(2*t*0.1*(i+1)) return g def test_molecule(): """Generates and shows a Caffeine molecule.""" o = [[30, 62, 19],[8, 21, 10]] ox, oy, oz = map(numpy.array, zip(*o)) n = [[31, 21, 11], [18, 42, 14], [55, 46, 17], [56, 25, 13]] nx, ny, nz = map(numpy.array, zip(*n)) c = [[5, 49, 15], [30, 50, 16], [42, 42, 15], [43, 29, 13], [18, 28, 12], [32, 6, 8], [63, 36, 15], [59, 60, 20]] cx, cy, cz = map(numpy.array, zip(*c)) h = [[23, 5, 7], [32, 0, 16], [37, 5, 0], [73, 36, 16], [69, 60, 20], [54, 62, 28], [57, 66, 12], [6, 59, 16], [1, 44, 22], [0, 49, 6]] hx, hy, hz = map(numpy.array, zip(*h)) oxygen = points3d(ox, oy, oz, scale_factor=16, scale_mode='none', resolution=20, color=(1,0,0), name='Oxygen') nitrogen = points3d(nx, ny, nz, scale_factor=20, scale_mode='none', resolution=20, color=(0,0,1), name='Nitrogen') carbon = points3d(cx, cy, cz, scale_factor=20, scale_mode='none', resolution=20, color=(0,1,0), name='Carbon') hydrogen = points3d(hx, hy, hz, scale_factor=10, scale_mode='none', resolution=20, color=(1,1,1), name='Hydrogen') return oxygen, nitrogen, carbon, hydrogen ############################################################################# class Quiver3D(Points3d): """ Plots glyphs (like arrows) indicating the direction of the vectors at the positions supplied. **Function signatures**:: quiver3d(u, v, w, ...) quiver3d(x, y, z, u, v, w, ...) quiver3d(x, y, z, f, ...) u, v, w are numpy arrays giving the components of the vectors. If only 3 arrays, u, v, and w are passed, they must be 3D arrays, and the positions of the arrows are assumed to be the indices of the corresponding points in the (u, v, w) arrays. If 6 arrays, (x, y, z, u, v, w) are passed, the 3 first arrays give the position of the arrows, and the 3 last the components. They can be of any shape. If 4 positional arguments, (x, y, z, f) are passed, the last one must be a callable, f, that returns vectors components (u, v, w) given the positions (x, y, z).""" scalars = Array(help="""optional scalar data.""") _source_function = Callable(vector_scatter) _pipeline = [VectorsFactory, ] quiver3d = document_pipeline(Quiver3D()) def test_quiver3d(): x, y, z = numpy.mgrid[-2:3, -2:3, -2:3] r = numpy.sqrt(x**2 + y**2 + z**4) u = y*numpy.sin(r)/(r+0.001) v = -x*numpy.sin(r)/(r+0.001) w = numpy.zeros_like(z) obj = quiver3d(x, y, z, u, v, w, line_width=3, scale_factor=1) return obj def test_quiver3d_cone(): dims = [8, 8, 8] xmin, xmax, ymin, ymax, zmin, zmax = [-5,5,-5,5,-5,5] x, y, z = numpy.mgrid[-5:5:8j, -5:5:8j, -5:5:8j] x = x.astype('f') y = y.astype('f') z = z.astype('f') sin = numpy.sin cos = numpy.cos u = cos(x) v = sin(y) w = sin(x*z) obj = quiver3d(x, y, z, u, v, w, mode='cone', extent=(0,1, 0,1, 0,1), scale_factor=0.9) return obj def test_quiver3d_2d_data(): dims = [32, 32] xmin, xmax, ymin, ymax = [-5,5,-5,5] x, y = numpy.mgrid[xmin:xmax:dims[0]*1j, ymin:ymax:dims[1]*1j] x = x.astype('f') y = y.astype('f') sin = numpy.sin cos = numpy.cos u = cos(x) v = sin(y) w = numpy.zeros_like(x) return quiver3d(x, y, w, u, v, w, colormap="Purples", scale_factor=0.5, mode="2dthick_arrow") ############################################################################# class Flow(Pipeline): """ Creates a trajectory of particles following the flow of a vector field. **Function signatures**:: flow(u, v, w, ...) flow(x, y, z, u, v, w, ...) flow(x, y, z, f, ...) u, v, w are numpy arrays giving the components of the vectors. If only 3 arrays, u, v, and w are passed, they must be 3D arrays, and the positions of the arrows are assumed to be the indices of the corresponding points in the (u, v, w) arrays. If 6 arrays, (x, y, z, u, v, w) are passed, the 3 first arrays give the position of the arrows, and the 3 last the components. The x, y and z arrays are then supposed to have been generated by `numpy.mgrid`, in other words, they are 3D arrays, with positions lying on a 3D orthogonal and regularly spaced grid with nearest neighbor in space matching nearest neighbor in the array. The function builds a vector field assuming the points are regularly spaced. If 4 positional arguments, (x, y, z, f) are passed, the last one must be a callable, f, that returns vectors components (u, v, w) given the positions (x, y, z).""" scalars = Array(help="""optional scalar data.""") _source_function = Callable(vector_field) _pipeline = [ExtractVectorNormFactory, StreamlineFactory, ] def __call_internal__(self, *args, **kwargs): """ Override the call to be able to choose whether to apply an ExtractVectorNorm filter. """ self.source = self._source_function(*args, **kwargs) kwargs.pop('name', None) self.store_kwargs(kwargs) # Copy the pipeline so as not to modify it for the next call self.pipeline = self._pipeline[:] if tools._has_scalar_data(self.source): self.pipeline.pop(0) return self.build_pipeline() flow = document_pipeline(Flow()) def test_flow(): x, y, z = numpy.mgrid[0:5, 0:5, 0:5] r = numpy.sqrt(x**2 + y**2 + z**4) u = y*numpy.sin(r)/r v = -x*numpy.sin(r)/r w = numpy.zeros_like(z) obj = flow(u, v, w) return obj def test_flow_tubes(): dims = [32, 32, 32] xmin, xmax, ymin, ymax, zmin, zmax = [-5,5,-5,5,-5,5] x, y, z = numpy.mgrid[xmin:xmax:dims[0]*1j, ymin:ymax:dims[1]*1j, zmin:zmax:dims[2]*1j] x = x.astype('f') y = y.astype('f') z = z.astype('f') sin = numpy.sin cos = numpy.cos u = cos(x/2.) v = sin(y/2.) w = sin(x*z/4.) obj = flow(x, y, z, u, v, w, linetype='tube') return obj def test_flow_anim(): dims = [32, 32, 32] xmin, xmax, ymin, ymax, zmin, zmax = [-5,5,-5,5,-5,5] x, y, z = numpy.mgrid[xmin:xmax:dims[0]*1j, ymin:ymax:dims[1]*1j, zmin:zmax:dims[2]*1j] x = x.astype('f') y = y.astype('f') z = z.astype('f') sin = numpy.sin cos = numpy.cos u = cos(x/2.) v = sin(y/2.) w = sin(x*z/4.) obj = flow(x, y, z, u, v, w, linetype='tube') # Now animate the flow. ms = obj.mlab_source for i in range(10): u = cos(x/2. + numpy.pi*(i+1)/10.) w = sin(x*z/4. + numpy.pi*(i+1)/10.) ms.set(u=u, w=w) return obj def test_flow_scalars(): dims = [32, 32, 32] xmin, xmax, ymin, ymax, zmin, zmax = [-5,5,-5,5,-5,5] x, y, z = numpy.mgrid[xmin:xmax:dims[0]*1j, ymin:ymax:dims[1]*1j, zmin:zmax:dims[2]*1j] x = x.astype('f') y = y.astype('f') z = z.astype('f') sin = numpy.sin cos = numpy.cos u = cos(x/2.) v = sin(y/2.) w = sin(x*z/8.) t = x*z obj = flow(u, v, w, scalars=t, seedtype='plane', linetype='tube', colormap='Spectral') return obj ############################################################################# class Contour3d(Pipeline): """ Plots iso-surfaces for a 3D volume of data suplied as arguments. **Function signatures**:: contour3d(scalars, ...) contour3d(x, y, z, scalars, ...) scalars is a 3D numpy arrays giving the data on a grid. If 4 arrays, (x, y, z, scalars) are passed, the 3 first arrays give the position of the arrows, and the last the scalar value. The x, y and z arrays are then supposed to have been generated by `numpy.mgrid`, in other words, they are 3D arrays, with positions lying on a 3D orthogonal and regularly spaced grid with nearest neighbor in space matching nearest neighbor in the array. The function builds a scalar field assuming the points are regularly spaced. If 4 positional arguments, (x, y, z, f) are passed, the last one can also be a callable, f, that returns vectors components (u, v, w) given the positions (x, y, z).""" _source_function = Callable(scalar_field) _pipeline = [IsoSurfaceFactory, ] contour3d = document_pipeline(Contour3d()) def test_contour3d(): x, y, z = numpy.ogrid[-5:5:64j, -5:5:64j, -5:5:64j] scalars = x*x*0.5 + y*y + z*z*2.0 obj = contour3d(scalars, contours=4, transparent=True) return obj def test_contour3d_anim(): dims = [64, 64, 64] xmin, xmax, ymin, ymax, zmin, zmax = [-5,5,-5,5,-5,5] x, y, z = numpy.ogrid[xmin:xmax:dims[0]*1j, ymin:ymax:dims[1]*1j, zmin:zmax:dims[2]*1j] x = x.astype('f') y = y.astype('f') z = z.astype('f') sin = numpy.sin scalars = x*x*0.5 + y*x*0.1 + z*z*0.25 obj = contour3d(scalars, contours=4, transparent=True) # Now animate the contours. ms = obj.mlab_source for i in range(1, 10): ms.scalars = x*x*0.5 + y*x*0.1*(i+1) + z*z*0.25 return obj ############################################################################# class Plot3d(Pipeline): """ Draws lines between points. **Function signatures**:: plot3d(x, y, z, ...) plot3d(x, y, z, s, ...) x, y, z and s are numpy arrays or lists of the same shape. x, y and z give the positions of the successive points of the line. s is an optional scalar value associated with each point.""" tube_radius = Trait(0.025, CFloat, None, adapts='filter.radius', help = """radius of the tubes used to represent the lines, If None, simple lines are used. """) _source_function = Callable(line_source) _pipeline = [StripperFactory, TubeFactory, SurfaceFactory, ] def __call_internal__(self, *args, **kwargs): """ Override the call to be able to choose whether to apply filters. """ self.source = self._source_function(*args, **kwargs) kwargs.pop('name', None) self.store_kwargs(kwargs) # Copy the pipeline so as not to modify it for the next call self.pipeline = self._pipeline[:] if self.kwargs['tube_radius'] == None: self.pipeline.remove(TubeFactory) self.pipeline.remove(StripperFactory) return self.build_pipeline() plot3d = document_pipeline(Plot3d()) def test_plot3d(): """Generates a pretty set of lines.""" n_mer, n_long = 6, 11 pi = numpy.pi dphi = pi/1000.0 phi = numpy.arange(0.0, 2*pi + 0.5*dphi, dphi) mu = phi*n_mer x = numpy.cos(mu)*(1+numpy.cos(n_long*mu/n_mer)*0.5) y = numpy.sin(mu)*(1+numpy.cos(n_long*mu/n_mer)*0.5) z = numpy.sin(n_long*mu/n_mer)*0.5 l = plot3d(x, y, z, numpy.sin(mu), tube_radius=0.025, colormap='Spectral') return l def test_plot3d_anim(): """Generates a pretty set of lines and animates it.""" # Run the standard example and get the module generated. l = test_plot3d() # Some data from the test example for the animation. n_mer, n_long = 6, 11 pi = numpy.pi dphi = pi/1000.0 phi = numpy.arange(0.0, 2*pi + 0.5*dphi, dphi, 'd') mu = phi*n_mer # Now animate the data. ms = l.mlab_source for i in range(10): x = numpy.cos(mu)*(1+numpy.cos(n_long*mu/n_mer + numpy.pi*(i+1)/5.)*0.5) scalars = numpy.sin(mu + numpy.pi*(i+1)/5) ms.set(x=x, scalars=scalars) return l ############################################################################# class ImShow(Pipeline): """ View a 2D array as an image. **Function signatures**:: imshow(s, ...) s is a 2 dimension array. The values of s are mapped to a color using the colormap.""" _source_function = Callable(array2d_source) _pipeline = [ImageActorFactory, ] imshow = document_pipeline(ImShow()) def test_imshow(): """ Use imshow to visualize a 2D 10x10 random array. """ s = numpy.random.random((10,10)) return imshow(s, colormap='gist_earth') ############################################################################# class Surf(Pipeline): """ Plots a surface using regularly-spaced elevation data supplied as a 2D array. **Function signatures**:: surf(s, ...) surf(x, y, s, ...) surf(x, y, f, ...) s is the elevation matrix, a 2D array, where indices along the first array axis represent x locations, and indices along the second array axis represent y locations. x and y can be 1D or 2D arrays such as returned by numpy.ogrid or numpy.mgrid. Arrays returned by numpy.meshgrid require a transpose first to obtain correct indexing order. The points should be located on an orthogonal grid (possibly non-uniform). In other words, all the points sharing a same index in the s array need to have the same x or y value. For arbitrary-shaped position arrays (non-orthogonal grids), see the mesh function. If only 1 array s is passed, the x and y arrays are assumed to be made from the indices of arrays, and an uniformly-spaced data set is created. If 3 positional arguments are passed the last one must be an array s, or a callable, f, that returns an array. x and y give the coordinates of positions corresponding to the s values.""" _source_function = Callable(array2d_source) _pipeline = [WarpScalarFactory, PolyDataNormalsFactory, SurfaceFactory] warp_scale = Any(1, help="""scale of the z axis (warped from the value of the scalar). By default this scale is a float value. If you specify 'auto', the scale is calculated to give a pleasant aspect ratio to the plot, whatever the bounds of the data. If you specify a value for warp_scale in addition to an extent, the warp scale will be determined by the warp_scale, and the plot be positioned along the z axis with the zero of the data centered on the center of the extent. If you are using explicit extents, this is the best way to control the vertical scale of your plots. If you want to control the extent (or range) of the surface object, rather than its scale, see the `extent` keyword argument. """) mask = Array(help="boolean mask array to suppress some data points.") def __call_internal__(self, *args, **kwargs): """ Override the call to be able to scale automatically the axis. """ self.source = self._source_function(*args, **kwargs) kwargs.pop('name', None) # Deal with both explicit warp scale and extent, this is # slightly hairy. The wigner example is a good test case for # this. if not 'warp_scale' in kwargs and not 'extent' in kwargs: try: xi, xf, yi, yf, _, _ = self.source.data.bounds zi, zf = self.source.data.scalar_range except AttributeError: xi, xf, yi, yf, _, _ = self.source.image_data.bounds zi, zf = self.source.image_data.scalar_range aspect_ratios = [(zf - zi)/(xf - xi), (zf - zi)/(yf - yi)] if min(aspect_ratios) < 0.01 or max(aspect_ratios) > 100: print 'Warning: the range of your scalar values differs by ' \ 'more than a factor 100 than the range of the grid values ' \ 'and you did not '\ 'specify a warp_scale. You could try warp_scale="auto".' if 'warp_scale' in kwargs and not kwargs['warp_scale']=='auto' \ and 'extent' in kwargs: # XXX: I should use the logging module. print 'Warning: both warp_scale and extent keyword argument ' \ 'specified, the z bounds of the extents will be overridden' xi, xf, yi, yf, zi, zf = kwargs['extent'] zo = 0.5*(zi + zf) try: si, sf = self.source.data.scalar_range except AttributeError: si, sf = self.source.image_data.scalar_range z_span = kwargs['warp_scale'] * abs(sf - si) zi = zo + si * kwargs['warp_scale'] zf = zi + z_span kwargs['extent'] = (xi, xf, yi, yf, zi, zf) kwargs['warp_scale'] = 1 elif kwargs.get('warp_scale', 1) == 'auto': if 'extent' in kwargs: if 'warp_scale' in kwargs: print "Warning: extent specified, warp_scale='auto' " \ "ignored." else: try: xi, xf, yi, yf, _, _ = self.source.data.bounds zi, zf = self.source.data.scalar_range except AttributeError: xi, xf, yi, yf, _, _ = self.source.image_data.bounds zi, zf = self.source.image_data.scalar_range z0 = zf - zi dz = 0.3*((xf - xi) + (yf - yi)) zi = z0 - 0.5*dz zf = z0 + 0.5*dz kwargs['extent'] = (xi, xf, yi, yf, zi, zf) kwargs['warp_scale'] = 1. self.store_kwargs(kwargs) # Copy the pipeline so as not to modify it for the next call self.pipeline = self._pipeline[:] return self.build_pipeline() surf = document_pipeline(Surf()) def test_simple_surf(): """Test Surf with a simple collection of points.""" x, y = numpy.mgrid[0:3:1,0:3:1] return surf(x, y, numpy.asarray(x, 'd')) def test_simple_surf_anim(): """Test Surf with a simple collection of points and animate it.""" x, y = numpy.mgrid[0:3:1,0:3:1] s = surf(x, y, numpy.asarray(x*0.1, 'd')) ms = s.mlab_source for i in range(10): ms.scalars = numpy.asarray(x*0.1*(i+1), 'd') return s def test_surf(): """Test surf on regularly spaced co-ordinates like MayaVi.""" def f(x, y): sin, cos = numpy.sin, numpy.cos return sin(x+y) + sin(2*x - y) + cos(3*x+4*y) x, y = numpy.mgrid[-7.:7.05:0.1, -5.:5.05:0.05] s = surf(x, y, f) #cs = contour_surf(x, y, f, contour_z=0) return s def test_surf_wigner(): def cat(x, y, alpha=2, eta=1, purity=1): """ Multiphoton shrodinger cat. eta is the fidelity, alpha the number of photons""" cos = numpy.cos exp = numpy.exp return (1 + eta*(exp(-x**2 -(y-alpha)**2) + exp(-x**2 - (y+alpha)**2) + 2 * purity * exp(-x**2 - y**2) * cos(2* alpha * x))/(2 * (1 + exp(- alpha**2))))/2 x, y = numpy.mgrid[-5:5:0.1, -5:5:0.1] return surf(x, y, cat) ############################################################################# class Mesh(Pipeline): """ Plots a surface using grid-spaced data supplied as 2D arrays. **Function signatures**:: mesh(x, y, z, ...) x, y, z are 2D arrays, all of the same shape, giving the positions of the vertices of the surface. The connectivity between these points is implied by the connectivity on the arrays. For simple structures (such as orthogonal grids) prefer the `surf` function, as it will create more efficient data structures. For mesh defined by triangles rather than regular implicit connectivity, see the `triangular_mesh` function. """ scale_mode = Trait('none', {'none':'data_scaling_off', 'scalar':'scale_by_scalar', 'vector':'scale_by_vector'}, help="""the scaling mode for the glyphs ('vector', 'scalar', or 'none').""") scale_factor = CFloat(0.05, desc = """scale factor of the glyphs used to represent the vertices, in fancy_mesh mode. """) tube_radius = Trait(0.025, CFloat, None, help = """radius of the tubes used to represent the lines, in mesh mode. If None, simple lines are used. """) scalars = Array(help="""optional scalar data.""") mask = Array(help="boolean mask array to suppress some data points.") representation = Trait('surface', 'wireframe', 'points', 'mesh', 'fancymesh', desc="""the representation type used for the surface.""") _source_function = Callable(grid_source) _pipeline = [ExtractEdgesFactory, GlyphFactory, TubeFactory, SurfaceFactory] def __call_internal__(self, *args, **kwargs): """ Override the call to be able to choose whether to apply filters. """ self.source = self._source_function(*args, **kwargs) kwargs.pop('name', None) self.store_kwargs(kwargs) # Copy the pipeline so as not to modify it for the next call self.pipeline = self._pipeline[:] if not self.kwargs['representation'] in ('mesh', 'fancymesh'): self.pipeline.remove(ExtractEdgesFactory) self.pipeline.remove(TubeFactory) self.pipeline.remove(GlyphFactory) self.pipeline = [PolyDataNormalsFactory, ] + self.pipeline else: if self.kwargs['tube_radius'] == None: self.pipeline.remove(TubeFactory) if not self.kwargs['representation'] == 'fancymesh': self.pipeline.remove(GlyphFactory) self.kwargs['representation'] = 'surface' return self.build_pipeline() mesh = document_pipeline(Mesh()) def test_mesh(): """A very pretty picture of spherical harmonics translated from the octaviz example.""" pi = numpy.pi cos = numpy.cos sin = numpy.sin dphi, dtheta = pi/250.0, pi/250.0 [phi,theta] = numpy.mgrid[0:pi+dphi*1.5:dphi,0:2*pi+dtheta*1.5:dtheta] m0 = 4; m1 = 3; m2 = 2; m3 = 3; m4 = 6; m5 = 2; m6 = 6; m7 = 4; r = sin(m0*phi)**m1 + cos(m2*phi)**m3 + sin(m4*theta)**m5 + cos(m6*theta)**m7 x = r*sin(phi)*cos(theta) y = r*cos(phi) z = r*sin(phi)*sin(theta); return mesh(x, y, z, colormap="bone") def test_mesh_sphere(r=1.0, npts=(100,100), colormap='jet'): """Create a simple sphere.""" pi = numpy.pi cos = numpy.cos sin = numpy.sin np_phi = npts[0]*1j np_theta = npts[1]*1j phi, theta = numpy.mgrid[0:pi:np_phi, 0:2*pi:np_theta] x = r*sin(phi)*cos(theta) y = r*sin(phi)*sin(theta) z = r*cos(phi) return mesh(x, y, z, colormap=colormap) def test_mesh_sphere_anim(r=1.0, npts=(100,100), colormap='jet'): """Create a simple sphere and animate it.""" pi = numpy.pi cos = numpy.cos sin = numpy.sin np_phi = npts[0]*1j np_theta = npts[1]*1j phi, theta = numpy.mgrid[0:pi:np_phi, 0:2*pi:np_theta] x = r*sin(phi)*cos(theta) y = r*sin(phi)*sin(theta) z = r*cos(phi) s = mesh(x, y, z, colormap=colormap) ms = s.mlab_source for i in range(1, 10): z = (r+i*0.25)*cos(phi) ms.set(z=z, scalars=z) return s def test_fancy_mesh(): """Create a fancy looking mesh using mesh (example taken from octaviz).""" pi = numpy.pi cos = numpy.cos sin = numpy.sin du, dv = pi/20.0, pi/20.0 u, v = numpy.mgrid[0.01:pi+du*1.5:du, 0:2*pi+dv*1.5:dv] x = (1- cos(u))*cos(u+2*pi/3) * cos(v + 2*pi/3.0)*0.5 y = (1- cos(u))*cos(u+2*pi/3) * cos(v - 2*pi/3.0)*0.5 z = -cos(u-2*pi/3.) m = mesh(x, y, z, representation='fancymesh', tube_radius=0.0075, colormap="RdYlGn") return m ############################################################################# class ContourSurf(Pipeline): """ Plots a the contours of a surface using grid-spaced data for elevation supplied as a 2D array. **Function signatures**:: contour_surf(s, ...) contour_surf(x, y, s, ...) contour_surf(x, y, f, ...) s is the elevation matrix, a 2D array. The contour lines plotted are lines of equal s value. x and y can be 1D or 2D arrays (such as returned by numpy.ogrid or numpy.mgrid), but the points should be located on an orthogonal grid (possibly non-uniform). In other words, all the points sharing a same index in the s array need to have the same x or y value. For arbitrary-shaped position arrays (non-orthogonal grids), see the mesh function. If only 1 array s is passed, the x and y arrays are assumed to be made from the indices of arrays, and an uniformly-spaced data set is created. If 3 positional arguments are passed the last one must be an array s, or a callable, f, that returns an array. x and y give the coordinates of positions corresponding to the s values.""" _source_function = Callable(array2d_source) _pipeline = [WarpScalarFactory, ContourSurfaceFactory] contour_surf = document_pipeline(ContourSurf()) def test_contour_surf(): """Test contour_surf on regularly spaced co-ordinates like MayaVi.""" def f(x, y): sin, cos = numpy.sin, numpy.cos return sin(x+y) + sin(2*x - y) + cos(3*x+4*y) x, y = numpy.mgrid[-7.:7.05:0.1, -5.:5.05:0.05] s = contour_surf(x, y, f) return s ############################################################################# # Expose only the glyphs that make (more or less) sense for a barchart. bar_mode_dict = dict() for item in ('cube', '2dtriangle', '2dsquare', '2dvertex', '2dthick_cross', '2ddiamond', '2dcross', '2dcircle'): bar_mode_dict[item] = glyph_mode_dict[item] class BarChart(Pipeline): """ Plots vertical glyphs (like bars) scaled vertical, to do histogram-like plots. This functions accepts a wide variety of inputs, with positions given in 2-D or in 3-D. **Function signatures**:: barchart(s, ...) barchart(x, y, s, ...) barchart(x, y, f, ...) barchart(x, y, z, s, ...) barchart(x, y, z, f, ...) If only one positional argument is passed, it can be a 1-D, 2-D, or 3-D array giving the length of the vectors. The positions of the data points are deducted from the indices of array, and an uniformly-spaced data set is created. If 3 positional arguments (x, y, s) are passed the last one must be an array s, or a callable, f, that returns an array. x and y give the 2D coordinates of positions corresponding to the s values. If 4 positional arguments (x, y, z, s) are passed, the 3 first are arrays giving the 3D coordinates of the data points, and the last one is an array s, or a callable, f, that returns an array giving the data value. """ _source_function = Callable(vertical_vectors_source) _pipeline = [VectorsFactory, ] mode = Trait('cube', bar_mode_dict, desc='The glyph used to represent the bars.') lateral_scale = CFloat(0.9, desc='The lateral scale of the glyph, ' 'in units of the distance between nearest points') auto_scale = true(desc='whether to compute automatically the ' 'lateral scaling of the glyphs. This might be ' 'computationally expensive.') def __call_internal__(self, *args, **kwargs): """ Override the call to be able to scale automatically the axis. """ g = Pipeline.__call_internal__(self, *args, **kwargs) gs = g.glyph.glyph_source # Use a cube source for glyphs. if not 'mode' in kwargs: gs.glyph_source = gs.glyph_dict['cube_source'] # Position the glyph tail on the point. gs.glyph_position = 'tail' gs.glyph_source.center = (0.0, 0.0, 0.5) g.glyph.glyph.orient = False if not 'color' in kwargs: g.glyph.color_mode = 'color_by_scalar' if not 'scale_mode' in kwargs: g.glyph.scale_mode = 'scale_by_vector_components' g.glyph.glyph.clamping = False # The auto-scaling code. It involves finding the minimum # distance between points, which can be very expensive. We # shortcut this calculation for structured data if len(args) == 1 or self.auto_scale: min_axis_distance = 1 else: x, y, z = g.mlab_source.x, g.mlab_source.y, g.mlab_source.z min_axis_distance = \ tools._min_axis_distance(x, y, z) scale_factor = g.glyph.glyph.scale_factor * min_axis_distance lateral_scale = kwargs.pop('lateral_scale', self.lateral_scale) try: g.glyph.glyph_source.glyph_source.y_length = \ lateral_scale/(scale_factor) g.glyph.glyph_source.glyph_source.x_length = \ lateral_scale/(scale_factor) except TraitError: " Not all types of glyphs have controlable y_length and x_length" return g barchart = document_pipeline(BarChart()) def test_barchart(): """ Demo the bar chart plot with a 2D array. """ s = numpy.abs(numpy.random.random((3, 3))) return barchart(s) ############################################################################# class TriangularMesh(Mesh): """ Plots a surface using a mesh defined by the position of its vertices and the triangles connecting them. **Function signatures**:: triangular_mesh(x, y, z, triangles ...) x, y, z are arrays giving the positions of the vertices of the surface. triangles is a list of triplets (or an array) list the vertices in each triangle. Vertices are indexes by their appearance number in the position arrays. For simple structures (such as rectangular grids) prefer the surf or mesh functions, as they will create more efficient data structures. """ _source_function = Callable(triangular_mesh_source) triangular_mesh = document_pipeline(TriangularMesh()) def test_triangular_mesh(): """An example of a cone, ie a non-regular mesh defined by its triangles. """ n = 8 t = numpy.linspace(-numpy.pi, numpy.pi, n) z = numpy.exp(1j*t) x = z.real.copy() y = z.imag.copy() z = numpy.zeros_like(x) triangles = [(0, i, i+1) for i in range(1, n)] x = numpy.r_[0, x] y = numpy.r_[0, y] z = numpy.r_[1, z] t = numpy.r_[0, t] return triangular_mesh(x, y, z, triangles, scalars=t) mayavi-4.1.0/mayavi/tools/sources.py0000644000175100001440000013735511674464502020475 0ustar ischnellusers00000000000000""" Data sources classes and their associated functions for mlab. """ # Author: Gael Varoquaux # Prabhu Ramachandran # Copyright (c) 2007-2010, Enthought, Inc. # License: BSD Style. import operator import numpy as np from traits.api import (HasTraits, Instance, CArray, Either, Bool, on_trait_change, NO_COMPARE) from tvtk.api import tvtk from tvtk.common import camel2enthought from mayavi.sources.array_source import ArraySource from mayavi.core.registry import registry import tools from engine_manager import engine_manager __all__ = [ 'vector_scatter', 'vector_field', 'scalar_scatter', 'scalar_field', 'line_source', 'array2d_source', 'grid_source', 'open', 'triangular_mesh_source', 'vertical_vectors_source', ] ################################################################################ # A subclass of CArray that will accept floats and do a np.atleast_1d ################################################################################ class CArrayOrNumber(CArray): def validate( self, object, name, value): if operator.isNumberType(value): value = np.atleast_1d(value) return CArray.validate(self, object, name, value) ################################################################################ # `MlabSource` class. ################################################################################ class MlabSource(HasTraits): """ This class represents the base class for all mlab sources. These classes allow a user to easily update the data without having to recreate the whole pipeline. """ # The TVTK dataset we manage. dataset = Instance(tvtk.DataSet) # The Mayavi data source we manage. m_data = Instance(HasTraits) ######################################## # Private traits. # Disable the update when data is changed. _disable_update = Bool(False) ###################################################################### # `MlabSource` interface. ###################################################################### def reset(self, **traits): """Function to create the data from input arrays etc. This is to be used when the size of the arrays change or the first time when the data is created. This regenerates the data structures and will be slower in general. """ raise NotImplementedError() def update(self): """Update the visualization. This is to be called after the data of the visualization has changed. """ if not self._disable_update: self.dataset.modified() md = self.m_data if md is not None: if hasattr(md, '_assign_attribute'): md._assign_attribute.update() md.data_changed = True def set(self, trait_change_notify=True, **traits): """Shortcut for setting object trait attributes. This is an overridden method that will make changing multiple traits easier. This method is to be called when the arrays have changed content but not in shape/size. In that case one must call the `reset` method. Parameters ---------- trait_change_notify : Boolean If **True** (the default), then each value assigned may generate a trait change notification. If **False**, then no trait change notifications will be generated. (see also: trait_setq) traits : list of key/value pairs Trait attributes and their values to be set Returns ------- self The method returns this object, after setting attributes. """ try: self._disable_update = True super(MlabSource, self).set(trait_change_notify, **traits) finally: self._disable_update = False if trait_change_notify: self.update() return self ###################################################################### # Non-public interface. ###################################################################### def _m_data_changed(self, ds): if not hasattr(ds, 'mlab_source'): ds.add_trait('mlab_source', Instance(MlabSource)) ds.mlab_source = self ArrayOrNone = Either(None, CArray, comparison_mode=NO_COMPARE) ArrayNumberOrNone = Either(None, CArrayOrNumber, comparison_mode=NO_COMPARE) ################################################################################ # `MGlyphSource` class. ################################################################################ class MGlyphSource(MlabSource): """ This class represents a glyph data source for Mlab objects and allows the user to set the x, y, z, scalar/vector attributes. """ # The x, y, z and points of the glyphs. x = ArrayNumberOrNone y = ArrayNumberOrNone z = ArrayNumberOrNone points = ArrayOrNone # The scalars shown on the glyphs. scalars = ArrayNumberOrNone # The u, v, w components of the vector and the vectors. u = ArrayNumberOrNone v = ArrayNumberOrNone w = ArrayNumberOrNone vectors = ArrayOrNone ###################################################################### # `MlabSource` interface. ###################################################################### def reset(self, **traits): """Creates the dataset afresh or resets existing data source.""" # First convert numbers to arrays. for name in ('x', 'y', 'z', 'u', 'v', 'w', 'scalars'): if name in traits and traits[name] is not None: traits[name] = np.atleast_1d(traits[name]) # First set the attributes without really doing anything since # the notification handlers are not called. self.set(trait_change_notify=False, **traits) vectors = self.vectors scalars = self.scalars points = self.points x, y, z = self.x, self.y, self.z x = np.atleast_1d(x) y = np.atleast_1d(y) z = np.atleast_1d(z) if 'points' in traits: x=points[:,0].ravel() y=points[:,1].ravel() z=points[:,2].ravel() self.set(x=x,y=y,z=z,trait_change_notify=False) else: points = np.c_[x.ravel(), y.ravel(), z.ravel()].ravel() points.shape = (points.size/3, 3) self.set(points=points, trait_change_notify=False) u, v, w = self.u, self.v, self.w if u is not None: u = np.atleast_1d(u) v = np.atleast_1d(v) w = np.atleast_1d(w) if len(u) > 0: vectors = np.c_[u.ravel(), v.ravel(), w.ravel()].ravel() vectors.shape = (vectors.size/3, 3) self.set(vectors=vectors, trait_change_notify=False) if 'vectors' in traits: u=vectors[:,0].ravel() v=vectors[:,1].ravel() w=vectors[:,2].ravel() self.set(u=u,v=v,w=w,trait_change_notify=False) else: if u is not None and len(u) > 0: vectors = np.c_[u.ravel(), v.ravel(), w.ravel()].ravel() vectors.shape = (vectors.size/3, 3) self.set(vectors=vectors, trait_change_notify=False) if vectors is not None and len(vectors) > 0: assert len(points) == len(vectors) if scalars is not None: scalars = np.atleast_1d(scalars) if len(scalars) > 0: assert len(points) == len(scalars) # Create the dataset. polys = np.arange(0, len(points), 1, 'l') polys = np.reshape(polys, (len(points), 1)) if self.dataset is None: # Create new dataset if none exists pd = tvtk.PolyData() else: # Modify existing one. pd = self.dataset pd.set(points=points, polys=polys) if self.vectors is not None: pd.point_data.vectors = self.vectors pd.point_data.vectors.name = 'vectors' if self.scalars is not None: pd.point_data.scalars = self.scalars pd.point_data.scalars.name = 'scalars' self.dataset = pd ###################################################################### # Non-public interface. ###################################################################### def _x_changed(self, x): x = np.atleast_1d(x) self.points[:,0] = x self.update() def _y_changed(self, y): y = np.atleast_1d(y) self.points[:,1] = y self.update() def _z_changed(self, z): z = np.atleast_1d(z) self.points[:,2] = z self.update() def _u_changed(self, u): u = np.atleast_1d(u) self.vectors[:,0] = u self.update() def _v_changed(self, v): v = np.atleast_1d(v) self.vectors[:,1] = v self.update() def _w_changed(self, w): w = np.atleast_1d(w) self.vectors[:,2] = w self.update() def _points_changed(self, p): p = np.atleast_2d(p) self.dataset.points = p self.update() def _scalars_changed(self, s): if s is None: self.dataset.point_data.scalars = None self.dataset.point_data.remove_array('scalars') else: s = np.atleast_1d(s) self.dataset.point_data.scalars = s self.dataset.point_data.scalars.name = 'scalars' self.update() def _vectors_changed(self, v): self.dataset.point_data.vectors = v self.dataset.point_data.vectors.name = 'vectors' self.update() ################################################################################ # `MVerticalGlyphSource` class. ################################################################################ class MVerticalGlyphSource(MGlyphSource): """ This class represents a vertical glyph data source for Mlab objects and allows the user to set the x, y, z, scalar attributes. The vectors are created from the scalars to represent them in the vertical direction. """ def reset(self, **traits): """Creates the dataset afresh or resets existing data source.""" if 'scalars' in traits: s = traits['scalars'] if s is not None: traits['u'] = traits['v'] = np.ones_like(s), traits['w'] = s super(MVerticalGlyphSource, self).reset(**traits) def _scalars_changed(self, s): self.dataset.point_data.scalars = s self.dataset.point_data.scalars.name = 'scalars' self.set(vectors=np.c_[np.ones_like(s), np.ones_like(s), s]) self.update() ################################################################################ # `MArraySource` class. ################################################################################ class MArraySource(MlabSource): """ This class represents an array data source for Mlab objects and allows the user to set the x, y, z, scalar/vector attributes. """ # The x, y, z arrays for the volume. x = ArrayOrNone y = ArrayOrNone z = ArrayOrNone # The scalars shown on the glyphs. scalars = ArrayOrNone # The u, v, w components of the vector and the vectors. u = ArrayOrNone v = ArrayOrNone w = ArrayOrNone vectors = ArrayOrNone ###################################################################### # `MlabSource` interface. ###################################################################### def reset(self, **traits): """Creates the dataset afresh or resets existing data source.""" # First set the attributes without really doing anything since # the notification handlers are not called. self.set(trait_change_notify=False, **traits) vectors = self.vectors scalars = self.scalars x, y, z = [np.atleast_3d(a) for a in self.x, self.y, self.z] u, v, w = self.u, self.v, self.w if 'vectors' in traits: u=vectors[:,0].ravel() v=vectors[:,1].ravel() w=vectors[:,2].ravel() self.set(u=u,v=v,w=w,trait_change_notify=False) else: if u is not None and len(u) > 0: #vectors = np.concatenate([u[..., np.newaxis], # v[..., np.newaxis], # w[..., np.newaxis] ], # axis=3) vectors = np.c_[u.ravel(), v.ravel(), w.ravel()].ravel() vectors.shape = (u.shape[0] , u.shape[1], w.shape[2], 3) self.set(vectors=vectors, trait_change_notify=False) if vectors is not None and len(vectors) > 0 and scalars is not None: assert len(scalars) == len(vectors) if x.shape[0] <= 1: dx = 1 else: dx = x[1, 0, 0] - x[0, 0, 0] if y.shape[1] <= 1: dy = 1 else: dy = y[0, 1, 0] - y[0, 0, 0] if z.shape[2] <= 1: dz = 1 else: dz = z[0, 0, 1] - z[0, 0, 0] if self.m_data is None: ds = ArraySource(transpose_input_array=True) else: ds = self.m_data old_scalar = ds.scalar_data ds.set(vector_data=vectors, origin=[x.min(), y.min(), z.min()], spacing=[dx, dy, dz], scalar_data=scalars) if scalars is old_scalar: ds._scalar_data_changed(scalars) self.dataset = ds.image_data self.m_data = ds ###################################################################### # Non-public interface. ###################################################################### @on_trait_change('[x, y, z]') def _xyz_changed(self): x, y, z = self.x, self.y, self.z dx = x[1, 0, 0] - x[0, 0, 0] dy = y[0, 1, 0] - y[0, 0, 0] dz = z[0, 0, 1] - z[0, 0, 0] ds = self.dataset ds.origin = [x.min(), y.min(), z.min()] ds.spacing = [dx, dy, dz] if self.m_data is not None: self.m_data.set(origin=ds.origin, spacing=ds.spacing) self.update() def _u_changed(self, u): self.vectors[...,0] = u self.m_data._vector_data_changed(self.vectors) def _v_changed(self, v): self.vectors[...,1] = v self.m_data._vector_data_changed(self.vectors) def _w_changed(self, w): self.vectors[...,2] = w self.m_data._vector_data_changed(self.vectors) def _scalars_changed(self, s): old = self.m_data.scalar_data self.m_data.scalar_data = s if old is s: self.m_data._scalar_data_changed(s) def _vectors_changed(self, v): self.m_data.vector_data = v ################################################################################ # `MLineSource` class. ################################################################################ class MLineSource(MlabSource): """ This class represents a line data source for Mlab objects and allows the user to set the x, y, z, scalar attributes. """ # The x, y, z and points of the glyphs. x = ArrayOrNone y = ArrayOrNone z = ArrayOrNone points = ArrayOrNone # The scalars shown on the glyphs. scalars = ArrayOrNone ###################################################################### # `MlabSource` interface. ###################################################################### def reset(self, **traits): """Creates the dataset afresh or resets existing data source.""" # First set the attributes without really doing anything since # the notification handlers are not called. self.set(trait_change_notify=False, **traits) points = self.points scalars = self.scalars x, y, z = self.x, self.y, self.z if 'points' in traits: x=points[:,0].ravel() y=points[:,1].ravel() z=points[:,2].ravel() self.set(x=x,y=y,z=z,trait_change_notify=False) else: points = np.c_[x.ravel(), y.ravel(), z.ravel()].ravel() points.shape = (len(x), 3) self.set(points=points, trait_change_notify=False) # Create the dataset. n_pts = len(points) - 1 lines = np.zeros((n_pts, 2), 'l') lines[:,0] = np.arange(0, n_pts-0.5, 1, 'l') lines[:,1] = np.arange(1, n_pts+0.5, 1, 'l') if self.dataset is None: pd = tvtk.PolyData() else: pd = self.dataset # Avoid lines refering to non existing points: First set the # lines to None, then set the points, then set the lines # refering to the new points. pd.set(lines=None) pd.set(points=points) pd.set(lines=lines) if scalars is not None and len(scalars) > 0: assert len(x) == len(scalars) pd.point_data.scalars = np.ravel(scalars) pd.point_data.scalars.name = 'scalars' self.dataset = pd ###################################################################### # Non-public interface. ###################################################################### def _x_changed(self, x): self.points[:,0] = x self.update() def _y_changed(self, y): self.points[:,1] = y self.update() def _z_changed(self, z): self.points[:,2] = z self.update() def _points_changed(self, p): self.dataset.points = p self.update() def _scalars_changed(self, s): self.dataset.point_data.scalars = s.ravel() self.dataset.point_data.scalars.name = 'scalars' self.update() ################################################################################ # `MArray2DSource` class. ################################################################################ class MArray2DSource(MlabSource): """ This class represents a 2D array data source for Mlab objects and allows the user to set the x, y and scalar attributes. """ # The x, y values. # Values of X and Y as None are accepted, in that case we would build # values of X and Y automatically from the shape of scalars x = ArrayOrNone y = ArrayOrNone # The scalars shown on the glyphs. scalars = ArrayOrNone # The masking array. mask = ArrayOrNone ###################################################################### # `MlabSource` interface. ###################################################################### def reset(self, **traits): """Creates the dataset afresh or resets existing data source.""" # First set the attributes without really doing anything since # the notification handlers are not called. self.set(trait_change_notify=False, **traits) x, y, mask = self.x, self.y, self.mask scalars = self.scalars # We may have used this without specifying x and y at all in # which case we set them from the shape of scalars. nx, ny = scalars.shape #Build X and Y from shape of Scalars if they are none if x is None and y is None: x, y = np.mgrid[-nx/2.:nx/2, -ny/2.:ny/2] if mask is not None and len(mask) > 0: scalars[mask.astype('bool')] = np.nan # The NaN trick only works with floats. scalars = scalars.astype('float') self.set(scalars=scalars, trait_change_notify=False) z = np.array([0]) self.set(x=x, y=y, z=z, trait_change_notify=False) # Do some magic to extract the first row/column, independently of # the shape of x and y x = np.atleast_2d(x.squeeze().T)[0, :].squeeze() y = np.atleast_2d(y.squeeze())[0, :].squeeze() if x.ndim == 0: dx = 1 else: dx = x[1] - x[0] if y.ndim == 0: dy = 1 else: dy = y[1] - y[0] if self.m_data is None: ds = ArraySource(transpose_input_array=True) else: ds = self.m_data old_scalar = ds.scalar_data ds.set(origin=[x.min(), y.min(), 0], spacing=[dx, dy, 1], scalar_data=scalars) if old_scalar is scalars: ds._scalar_data_changed(scalars) self.dataset = ds.image_data self.m_data = ds ###################################################################### # Non-public interface. ###################################################################### @on_trait_change('[x, y]') def _xy_changed(self): x, y,scalars = self.x, self.y, self.scalars nx, ny = scalars.shape if x is None or y is None: x, y = np.mgrid[-nx/2.:nx/2, -ny/2.:ny/2] self.trait_setq(x=x,y=y) x = np.atleast_2d(x.squeeze().T)[0, :].squeeze() y = np.atleast_2d(y.squeeze())[0, :].squeeze() dx = x[1] - x[0] dy = y[1] - y[0] ds = self.dataset ds.origin = [x.min(), y.min(), 0] ds.spacing = [dx, dy, 1] if self.m_data is not None: self.m_data.set(origin=ds.origin, spacing=ds.spacing) self.update() def _scalars_changed(self, s): mask = self.mask if mask is not None and len(mask) > 0: s[mask.astype('bool')] = np.nan # The NaN tric only works with floats. s = s.astype('float') self.set(scalars=s, trait_change_notify=False) old = self.m_data.scalar_data self.m_data.scalar_data = s if s is old: self.m_data._scalar_data_changed(s) ################################################################################ # `MGridSource` class. ################################################################################ class MGridSource(MlabSource): """ This class represents a grid source for Mlab objects and allows the user to set the x, y, scalar attributes. """ # The x, y, z and points of the grid. x = ArrayOrNone y = ArrayOrNone z = ArrayOrNone points = ArrayOrNone # The scalars shown on the glyphs. scalars = ArrayOrNone ###################################################################### # `MlabSource` interface. ###################################################################### def reset(self, **traits): """Creates the dataset afresh or resets existing data source.""" # First set the attributes without really doing anything since # the notification handlers are not called. self.set(trait_change_notify=False, **traits) points = self.points scalars = self.scalars x, y, z = self.x, self.y, self.z assert len(x.shape) == 2, "Array x must be 2 dimensional." assert len(y.shape) == 2, "Array y must be 2 dimensional." assert len(z.shape) == 2, "Array z must be 2 dimensional." assert x.shape == y.shape, "Arrays x and y must have same shape." assert y.shape == z.shape, "Arrays y and z must have same shape." #Points in the grid source will always be created using x,y,z #Changing of points is not allowed because it cannot be used to modify values of x,y,z nx, ny = x.shape points = np.c_[x.ravel(), y.ravel(), z.ravel()].ravel() points.shape = (nx*ny, 3) self.set(points=points, trait_change_notify=False) i, j = np.mgrid[0:nx-1,0:ny-1] i, j = np.ravel(i), np.ravel(j) t1 = i*ny+j, (i+1)*ny+j, (i+1)*ny+(j+1) t2 = (i+1)*ny+(j+1), i*ny+(j+1), i*ny+j nt = len(t1[0]) triangles = np.zeros((nt*2, 3), 'l') triangles[0:nt,0], triangles[0:nt,1], triangles[0:nt,2] = t1 triangles[nt:,0], triangles[nt:,1], triangles[nt:,2] = t2 if self.dataset is None: pd = tvtk.PolyData() else: pd = self.dataset pd.set(points=points, polys=triangles) if scalars is not None and len(scalars) > 0: if not scalars.flags.contiguous: scalars = scalars.copy() self.set(scalars=scalars, trait_change_notify=False) assert x.shape == scalars.shape pd.point_data.scalars = scalars.ravel() pd.point_data.scalars.name = 'scalars' self.dataset = pd ###################################################################### # Non-public interface. ###################################################################### def _x_changed(self, x): self.trait_setq(x=x); self.points[:,0] = x.ravel() self.update() def _y_changed(self, y): self.trait_setq(y=y) self.points[:,1] = y.ravel() self.update() def _z_changed(self, z): self.trait_setq(z=z) self.points[:,2] = z.ravel() self.update() def _points_changed(self, p): self.dataset.points = p self.update() def _scalars_changed(self, s): self.dataset.point_data.scalars = s.ravel() self.dataset.point_data.scalars.name = 'scalars' self.update() ################################################################################ # `MTriangularMeshSource` class. ################################################################################ class MTriangularMeshSource(MlabSource): """ This class represents a triangular mesh source for Mlab objects and allows the user to set the x, y, scalar attributes. """ # The x, y, z and points of the grid. x = ArrayOrNone y = ArrayOrNone z = ArrayOrNone points = ArrayOrNone triangles = ArrayOrNone # The scalars shown on the glyphs. scalars = ArrayOrNone ###################################################################### # `MlabSource` interface. ###################################################################### def reset(self, **traits): """Creates the dataset afresh or resets existing data source.""" # First set the attributes without really doing anything since # the notification handlers are not called. self.set(trait_change_notify=False, **traits) points = self.points scalars = self.scalars x, y, z = self.x, self.y, self.z points = np.c_[x.ravel(), y.ravel(), z.ravel()].ravel() points.shape = (points.size/3, 3) self.set(points=points, trait_change_notify=False) triangles = self.triangles assert triangles.shape[1] == 3, \ "The shape of the triangles array must be (X, 3)" assert triangles.max() < len(points), \ "The triangles indices must be smaller that the number of points" assert triangles.min() >= 0, \ "The triangles indices must be positive or null" if self.dataset is None: pd = tvtk.PolyData() else: pd = self.dataset # Set the points first, and the triangles after: so that the # polygone can refer to the right points, in the polydata. pd.set(points=points) pd.set(polys=triangles) if (not 'scalars' in traits and scalars is not None and scalars.shape != x.shape): # The scalars where set probably automatically to z, by the # factory. We need to reset them, as the size has changed. scalars = z if scalars is not None and len(scalars) > 0: if not scalars.flags.contiguous: scalars = scalars.copy() self.set(scalars=scalars, trait_change_notify=False) assert x.shape == scalars.shape pd.point_data.scalars = scalars.ravel() pd.point_data.scalars.name = 'scalars' self.dataset = pd ###################################################################### # Non-public interface. ###################################################################### def _x_changed(self, x): self.trait_setq(x=x); self.points[:,0] = x.ravel() self.update() def _y_changed(self, y): self.trait_setq(y=y) self.points[:,1] = y.ravel() self.update() def _z_changed(self, z): self.trait_setq(z=z) self.points[:,2] = z.ravel() self.update() def _points_changed(self, p): self.dataset.points = p self.update() def _scalars_changed(self, s): self.dataset.point_data.scalars = s.ravel() self.dataset.point_data.scalars.name = 'scalars' self.update() def _triangles_changed(self, triangles): if triangles.min() < 0: raise ValueError, 'The triangles array has negative values' if triangles.max() > self.x.size: raise ValueError, 'The triangles array has values larger than' \ 'the number of points' self.dataset.polys = triangles self.update() ############################################################################ # Argument processing ############################################################################ def convert_to_arrays(args): """ Converts a list of iterables to a list of arrays or callables, if needed. """ args = list(args) for index, arg in enumerate(args): if not callable(arg): if not hasattr(arg, 'shape'): arg = np.atleast_1d(np.array(arg)) if np.any(np.isinf(arg)): raise ValueError("""Input array contains infinite values You can remove them using: a[np.isinf(a)] = np.nan """) args[index] = arg return args def process_regular_vectors(*args): """ Converts different signatures to (x, y, z, u, v, w). """ args = convert_to_arrays(args) if len(args)==3: u, v, w = [np.atleast_3d(a) for a in args] assert len(u.shape)==3, "3D array required" x, y, z = np.indices(u.shape) elif len(args)==6: x, y, z, u, v, w = args elif len(args)==4: x, y, z, f = args if not callable(f): raise ValueError, "When 4 arguments are provided, the fourth must be a callable" u, v, w = f(x, y, z) else: raise ValueError, "wrong number of arguments" assert ( x.shape == y.shape and y.shape == z.shape and u.shape == z.shape and v.shape == u.shape and w.shape == v.shape ), "argument shape are not equal" return x, y, z, u, v, w def process_regular_scalars(*args): """ Converts different signatures to (x, y, z, s). """ args = convert_to_arrays(args) if len(args)==1: s = np.atleast_3d(args[0]) assert len(s.shape)==3, "3D array required" x, y, z = np.indices(s.shape) elif len(args)==3: x, y, z = args s = None elif len(args)==4: x, y, z, s = args if callable(s): s = s(x, y, z) else: raise ValueError, "wrong number of arguments" assert ( x.shape == y.shape and y.shape == z.shape and ( s is None or s.shape == z.shape ) ), "argument shape are not equal" return x, y, z, s def process_regular_2d_scalars(*args, **kwargs): """ Converts different signatures to (x, y, s). """ args = convert_to_arrays(args) for index, arg in enumerate(args): if not callable(arg): args[index] = np.atleast_2d(arg) if len(args)==1: s = args[0] assert len(s.shape)==2, "2D array required" x, y = np.indices(s.shape) elif len(args)==3: x, y, s = args if callable(s): s = s(x, y) else: raise ValueError, "wrong number of arguments" assert len(s.shape)==2, "2D array required" if 'mask' in kwargs: mask = kwargs['mask'] s[mask.astype('bool')] = np.nan # The NaN tric only works with floats. s = s.astype('float') return x, y, s ############################################################################ # Sources ############################################################################ def vector_scatter(*args, **kwargs): """ Creates scattered vector data. **Function signatures**:: vector_scatter(u, v, w, ...) vector_scatter(x, y, z, u, v, w, ...) vector_scatter(x, y, z, f, ...) If only 3 arrays u, v, w are passed the x, y and z arrays are assumed to be made from the indices of vectors. If 4 positional arguments are passed the last one must be a callable, f, that returns vectors. **Keyword arguments**: :name: the name of the vtk object created. :scalars: optional scalar data. :figure: optionally, the figure on which to add the data source. If None, the source is not added to any figure, and will be added automatically by the modules or filters. If False, no figure will be created by modules or filters applied to the source: the source can only be used for testing, or numerical algorithms, not visualization.""" x, y, z, u, v, w = process_regular_vectors(*args) scalars = kwargs.pop('scalars', None) if scalars is not None: scalars = np.ravel(scalars) name = kwargs.pop('name', 'VectorScatter') data_source = MGlyphSource() data_source.reset(x=x, y=y, z=z, u=u, v=v, w=w, scalars=scalars) ds = tools.add_dataset(data_source.dataset, name, **kwargs) data_source.m_data = ds return ds def vector_field(*args, **kwargs): """ Creates vector field data. **Function signatures**:: vector_field(u, v, w, ...) vector_field(x, y, z, u, v, w, ...) vector_field(x, y, z, f, ...) If only 3 arrays u, v, w are passed the x, y and z arrays are assumed to be made from the indices of vectors. If the x, y and z arrays are passed, they should have been generated by `numpy.mgrid` or `numpy.ogrid`. The function builds a scalar field assuming the points are regularily spaced on an orthogonal grid. If 4 positional arguments are passed the last one must be a callable, f, that returns vectors. **Keyword arguments**: :name: the name of the vtk object created. :scalars: optional scalar data. :figure: optionally, the figure on which to add the data source. If None, the source is not added to any figure, and will be added automatically by the modules or filters. If False, no figure will be created by modules or filters applied to the source: the source can only be used for testing, or numerical algorithms, not visualization.""" if len(args) == 3: x = y = z = np.atleast_3d(1) u, v, w = [np.atleast_3d(a) for a in args] else: x, y, z, u, v, w = [np.atleast_3d(a) for a in process_regular_vectors(*args)] scalars = kwargs.pop('scalars', None) if scalars is not None: scalars = np.atleast_3d(scalars) data_source = MArraySource() data_source.reset(x=x, y=y, z=z, u=u, v=v, w=w, scalars=scalars) name = kwargs.pop('name', 'VectorField') return tools.add_dataset(data_source.m_data, name, **kwargs) def scalar_scatter(*args, **kwargs): """ Creates scattered scalar data. **Function signatures**:: scalar_scatter(s, ...) scalar_scatter(x, y, z, s, ...) scalar_scatter(x, y, z, s, ...) scalar_scatter(x, y, z, f, ...) If only 1 array s is passed the x, y and z arrays are assumed to be made from the indices of vectors. If 4 positional arguments are passed the last one must be an array s, or a callable, f, that returns an array. **Keyword arguments**: :name: the name of the vtk object created. :figure: optionally, the figure on which to add the data source. If None, the source is not added to any figure, and will be added automatically by the modules or filters. If False, no figure will be created by modules or filters applied to the source: the source can only be used for testing, or numerical algorithms, not visualization.""" x, y, z, s = process_regular_scalars(*args) if s is not None: s = np.ravel(s) data_source = MGlyphSource() data_source.reset(x=x, y=y, z=z, scalars=s) name = kwargs.pop('name', 'ScalarScatter') ds = tools.add_dataset(data_source.dataset, name, **kwargs) data_source.m_data = ds return ds def scalar_field(*args, **kwargs): """ Creates a scalar field data. **Function signatures**:: scalar_field(s, ...) scalar_field(x, y, z, s, ...) scalar_field(x, y, z, f, ...) If only 1 array s is passed the x, y and z arrays are assumed to be made from the indices of arrays. If the x, y and z arrays are passed they are supposed to have been generated by `numpy.mgrid`. The function builds a scalar field assuming the points are regularily spaced. If 4 positional arguments are passed the last one must be an array s, or a callable, f, that returns an array. **Keyword arguments**: :name: the name of the vtk object created. :figure: optionally, the figure on which to add the data source. If None, the source is not added to any figure, and will be added automatically by the modules or filters. If False, no figure will be created by modules or filters applied to the source: the source can only be used for testing, or numerical algorithms, not visualization.""" if len(args) == 1: # Be lazy, don't create three big arrays for 1 input array. The # MArraySource is clever-enough to handle flat arrays x = y = z = np.atleast_1d(1) s = args[0] else: x, y, z, s = process_regular_scalars(*args) data_source = MArraySource() data_source.reset(x=x, y=y, z=z, scalars=s) name = kwargs.pop('name', 'ScalarField') return tools.add_dataset(data_source.m_data, name, **kwargs) def line_source(*args, **kwargs): """ Creates line data. **Function signatures**:: line_source(x, y, z, ...) line_source(x, y, z, s, ...) line_source(x, y, z, f, ...) If 4 positional arguments are passed the last one must be an array s, or a callable, f, that returns an array. **Keyword arguments**: :name: the name of the vtk object created. :figure: optionally, the figure on which to add the data source. If None, the source is not added to any figure, and will be added automatically by the modules or filters. If False, no figure will be created by modules or filters applied to the source: the source can only be used for testing, or numerical algorithms, not visualization.""" if len(args)==1: raise ValueError, "wrong number of arguments" x, y, z, s = process_regular_scalars(*args) data_source = MLineSource() data_source.reset(x=x, y=y, z=z, scalars=s) name = kwargs.pop('name', 'LineSource') ds = tools.add_dataset(data_source.dataset, name, **kwargs) data_source.m_data = ds return ds def array2d_source(*args, **kwargs): """ Creates structured 2D data from a 2D array. **Function signatures**:: array2d_source(s, ...) array2d_source(x, y, s, ...) array2d_source(x, y, f, ...) If 3 positional arguments are passed the last one must be an array s, or a callable, f, that returns an array. x and y give the coordinnates of positions corresponding to the s values. x and y can be 1D or 2D arrays (such as returned by numpy.ogrid or numpy.mgrid), but the points should be located on an orthogonal grid (possibly non-uniform). In other words, all the points sharing a same index in the s array need to have the same x or y value. If only 1 array s is passed the x and y arrays are assumed to be made from the indices of arrays, and an uniformly-spaced data set is created. **Keyword arguments**: :name: the name of the vtk object created. :figure: optionally, the figure on which to add the data source. If None, the source is not added to any figure, and will be added automatically by the modules or filters. If False, no figure will be created by modules or filters applied to the source: the source can only be used for testing, or numerical algorithms, not visualization. :mask: Mask points specified in a boolean masking array. """ data_source = MArray2DSource() mask = kwargs.pop('mask', None) if len(args) == 1 : args = convert_to_arrays(args) s = np.atleast_2d(args[0]) data_source.reset(scalars=s, mask=mask) else: x, y, s = process_regular_2d_scalars(*args, **kwargs) data_source.reset(x=x, y=y, scalars=s, mask=mask) name = kwargs.pop('name', 'Array2DSource') return tools.add_dataset(data_source.m_data, name, **kwargs) def grid_source(x, y, z, **kwargs): """ Creates 2D grid data. x, y, z are 2D arrays giving the positions of the vertices of the surface. The connectivity between these points is implied by the connectivity on the arrays. For simple structures (such as orthogonal grids) prefer the array2dsource function, as it will create more efficient data structures. **Keyword arguments**: :name: the name of the vtk object created. :scalars: optional scalar data. :figure: optionally, the figure on which to add the data source. If None, the source is not added to any figure, and will be added automatically by the modules or filters. If False, no figure will be created by modules or filters applied to the source: the source can only be used for testing, or numerical algorithms, not visualization. """ scalars = kwargs.pop('scalars', None) if scalars is None: scalars = z x, y, z, scalars = convert_to_arrays((x, y, z, scalars)) data_source = MGridSource() data_source.reset(x=x, y=y, z=z, scalars=scalars) name = kwargs.pop('name', 'GridSource') ds = tools.add_dataset(data_source.dataset, name, **kwargs) data_source.m_data = ds return ds def vertical_vectors_source(*args, **kwargs): """ Creates a set of vectors pointing upward, useful eg for bar graphs. **Function signatures**:: vertical_vectors_source(s, ...) vertical_vectors_source(x, y, s, ...) vertical_vectors_source(x, y, f, ...) vertical_vectors_source(x, y, z, s, ...) vertical_vectors_source(x, y, z, f, ...) If only one positional argument is passed, it can be a 1D, 2D, or 3D array giving the length of the vectors. The positions of the data points are deducted from the indices of array, and an uniformly-spaced data set is created. If 3 positional arguments (x, y, s) are passed the last one must be an array s, or a callable, f, that returns an array. x and y give the 2D coordinates of positions corresponding to the s values. The vertical position is assumed to be 0. If 4 positional arguments (x, y, z, s) are passed, the 3 first are arrays giving the 3D coordinates of the data points, and the last one is an array s, or a callable, f, that returns an array giving the data value. **Keyword arguments**: :name: the name of the vtk object created. :figure: optionally, the figure on which to add the data source. If None, the source is not added to any figure, and will be added automatically by the modules or filters. If False, no figure will be created by modules or filters applied to the source: the source can only be used for testing, or numerical algorithms, not visualization. """ if len(args) == 3: x, y, data = args if np.isscalar(x): z = 0 else: z = np.zeros_like(x) args = (x, y, z, data) x, y, z, s = process_regular_scalars(*args) if s is not None: s = np.ravel(s) data_source = MVerticalGlyphSource() data_source.reset(x=x, y=y, z=z, scalars=s) name = kwargs.pop('name', 'VerticalVectorsSource') ds = tools.add_dataset(data_source.dataset, name, **kwargs) data_source.m_data = ds return ds def triangular_mesh_source(x, y, z, triangles, **kwargs): """ Creates 2D mesh by specifying points and triangle connectivity. x, y, z are 2D arrays giving the positions of the vertices of the surface. The connectivity between these points is given by listing triplets of vertices inter-connected. These vertices are designed by there position index. **Keyword arguments**: :name: the name of the vtk object created. :scalars: optional scalar data. :figure: optionally, the figure on which to add the data source. If None, the source is not added to any figure, and will be added automatically by the modules or filters. If False, no figure will be created by modules or filters applied to the source: the source can only be used for testing, or numerical algorithms, not visualization. """ x, y, z, triangles = convert_to_arrays((x, y, z, triangles)) if triangles.min() < 0: raise ValueError, 'The triangles array has negative values' if triangles.max() > x.size: raise ValueError, 'The triangles array has values larger than' \ 'the number of points' scalars = kwargs.pop('scalars', None) if scalars is None: scalars = z data_source = MTriangularMeshSource() data_source.reset(x=x, y=y, z=z, triangles=triangles, scalars=scalars) name = kwargs.pop('name', 'TriangularMeshSource') ds = tools.add_dataset(data_source.dataset, name, **kwargs) data_source.m_data = ds return ds def open(filename, figure=None): """Open a supported data file given a filename. Returns the source object if a suitable reader was found for the file. """ if figure is None: engine = tools.get_engine() else: engine = engine_manager.find_figure_engine(figure) engine.current_scene = figure src = engine.open(filename) return src ############################################################################ # Automatically generated sources from registry. ############################################################################ def _create_data_source(metadata): """Creates a data source and adds it to the mayavi engine given metadata of the source. Returns the created source. """ factory = metadata.get_callable() src = factory() engine = tools.get_engine() engine.add_source(src) return src def _make_functions(namespace): """Make the automatic functions and add them to the namespace.""" for src in registry.sources: if len(src.extensions) == 0: func_name = camel2enthought(src.id) if func_name.endswith('_source'): func_name = func_name[:-7] func = lambda metadata=src: _create_data_source(metadata) func.__doc__ = src.help func.__name__ = func_name # Inject function into the namespace and __all__. namespace[func_name] = func __all__.append(func_name) _make_functions(locals()) mayavi-4.1.0/mayavi/tools/probe_data.py0000644000175100001440000000532011674464502021074 0ustar ischnellusers00000000000000""" A helper function to retrieve the data from Mayavi structures on arbitrary points. """ import numpy as np from tvtk.api import tvtk from . import tools def probe_data(mayavi_object, x, y, z, type='scalars', location='points'): """ Retrieve the data from a described by Mayavi visualization object at points x, y, z. **Parameters** :viz_obj: A Mayavi visualization object, or a VTK dataset The object describing the data you are interested in. :x: float or ndarray. The x position where you want to retrieve the data. :y: float or ndarray. The y position where you want to retrieve the data. :z: float or ndarray The z position where you want to retrieve the data. :type: 'scalars', 'vectors' or 'tensors', optional The type of the data to retrieve. :location: 'points' or 'cells', optional The location of the data to retrieve. **Returns** The values of the data at the given point, as an ndarray (or multiple arrays, in the case of vectors or tensors) of the same shape as x, y, and z. """ dataset = tools.get_vtk_src(mayavi_object)[0] assert type in ('scalars', 'vectors', 'cells'), ( "Invalid value for type: must be 'scalars', 'vectors' or " "'cells', but '%s' was given" % type) x = np.atleast_1d(x) y = np.atleast_1d(y) z = np.atleast_1d(z) shape = x.shape assert y.shape == z.shape == shape, \ 'The x, y and z arguments must have the same shape' probe_data = mesh = tvtk.PolyData(points=np.c_[x.ravel(), y.ravel(), z.ravel()]) shape = list(shape) probe = tvtk.ProbeFilter() probe.input = probe_data probe.source = dataset probe.update() if location == 'points': data = probe.output.point_data elif location == 'cells': data = probe.output.cell_data else: raise ValueError("Invalid value for data location, must be " "'points' or 'cells', but '%s' was given." % location) values = getattr(data, type) if values is None: raise ValueError("The object given has no %s data of type %s" % (location, type)) values = values.to_array() if type == 'scalars': values = np.reshape(values, shape) elif type == 'vectors': values = np.reshape(values, shape + [3, ]) values = np.rollaxis(values, -1) else: values = np.reshape(values, shape + [-1, ]) values = np.rollaxis(values, -1) return values mayavi-4.1.0/mayavi/tools/mlab_scene_model.py0000644000175100001440000000342411674464502022247 0ustar ischnellusers00000000000000"""`MlabSceneModel` makes it easy to plug `mayavi.mlab` in traits UI views. """ # Authors: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. from traits.api import Instance, Property from tvtk.pyface.scene_model import SceneModel from mayavi.core.engine import Engine from mayavi.core.scene import Scene from mayavi import mlab as m2_mlab ###################################################################### # `MlabSceneModel` class ###################################################################### class MlabSceneModel(SceneModel): """ An container for an mlab model, that can be exposed using a Mayavi scene in a TraitsUI view. """ # The mayavi engine. engine = Instance(Engine) # The mlab instance. mlab = Property() # A reference to the mayavi scene object mayavi_scene = Instance(Scene) def __init__(self, parent=None, **traits): super(MlabSceneModel, self).__init__(parent, **traits) # Store the current mlab figure: current_figure = self.engine.current_scene # register ourselves with the engine. self.engine.new_scene(self) # XXX: This is not thread-safe self.mayavi_scene = self.engine.current_scene # Restore the current figure. We do this, because MlabSceneModel # can be created lazy by Traits, on access. Having side effects # thus renders the code quite unpredictable self.engine.current_scene = current_figure ################################################################### # Private API. ################################################################### def _engine_default(self): return m2_mlab.get_engine() def _get_mlab(self): return m2_mlab mayavi-4.1.0/mayavi/tools/show.py0000644000175100001440000001064611674464502017763 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. from traits.etsconfig.api import ETSConfig from pyface.api import GUI, ApplicationWindow from traits.api import HasTraits, Button, Any from traitsui.api import View, Group, Item from pyface.util import guisupport # Globals. # The GUI instance. _gui = None # The stop show instance. _stop_show = None def is_ui_running(): """ Returns True if the UI event loop is running. """ from engine_manager import options if options.offscreen: return True elif ETSConfig.toolkit == 'wx': return guisupport.is_event_loop_running_wx() elif ETSConfig.toolkit == 'qt4': return guisupport.is_event_loop_running_qt4() else: return False ################################################################################ # `StopShow` class. ################################################################################ class StopShow(HasTraits): ######################################## # Traits stop = Button('Stop interaction', desc='if the UI interaction is to be stopped') # Private traits. # Stores a reference to the UI object so it can be disposed when the # interaction is stopped. _ui = Any view = View(Group(Item('stop'), show_labels=False), buttons = [], title='Control Show') ###################################################################### # `object` interface. ###################################################################### def __init__(self, **traits): super(StopShow, self).__init__(**traits) self._ui = self.edit_traits() ###################################################################### # Non-public interface. ###################################################################### def _stop_fired(self): _gui.stop_event_loop() self._ui.dispose() def show(func=None, stop=False): """ Start interacting with the figure. By default, this function simply creates a GUI and starts its event loop if needed. If it is used as a decorator, then it may be used to decorate a function which requires a UI. If the GUI event loop is already running it simply runs the function. If not the event loop is started and function is run in the toolkit's event loop. The choice of UI is via `ETSConfig.toolkit`. If the argument stop is set to True then it pops up a UI where the user can stop the event loop. Subsequent calls to `show` will restart the event loop. **Parameters** :stop: A boolean which specifies if a UI dialog is displayed which allows the event loop to be stopped. **Examples** Here is a simple example demonstrating the use of show:: >>> from mayavi import mlab >>> mlab.test_contour3d() >>> mlab.show() You can stop interaction via a simple pop up UI like so:: >>> mlab.test_contour3d() >>> mlab.show(stop=True) The decorator can be used like so:: >>> @mlab.show ... def do(): ... mlab.test_contour3d() ... >>> do() The decorator can also be passed the stop argument:: >>> @mlab.show(stop=True) ... def do(): ... mlab.test_contour3d() ... >>> do() """ global _gui, _stop_show if func is None: if not is_ui_running(): g = GUI() _gui = g if stop: _stop_show = StopShow() g.start_event_loop() return def wrapper(*args, **kw): """Wrapper function to run given function inside the GUI event loop. """ global _gui, _stop_show tk = ETSConfig.toolkit if is_ui_running(): # In this case we should not pop up the UI since we likely # don't want to stop the mainloop. return func(*args, **kw) else: g = GUI() if tk == 'wx': # Create a dummy app so invoke later works on wx. a = ApplicationWindow(size=(1,1)) GUI.invoke_later(lambda: a.close()) a.open() GUI.invoke_later(func, *args, **kw) _gui = g if stop: # Pop up the UI to stop the mainloop. _stop_show = StopShow() g.start_event_loop() return wrapper mayavi-4.1.0/mayavi/tools/__init__.py0000644000175100001440000000013211674464502020527 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/tools/mlab.py0000644000175100001440000000043711674464502017713 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. print "!! mayavi.tools.mlab is obsolete and has been replaced by !!" print "!! mayavi.mlab. Please update your code. !!" from mayavi.mlab import * mayavi-4.1.0/mayavi/tools/figure.py0000644000175100001440000002564311674464502020267 0ustar ischnellusers00000000000000""" Functions related to creating the engine or the figures. """ # Author: Gael Varoquaux # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Standard library imports. from types import IntType import gc import warnings import copy import numpy as np # Enthought imports from pyface.timer.api import do_later # imports from tvtk.api import tvtk from mayavi.core.scene import Scene from mayavi.core.registry import registry from .camera import view from .engine_manager import get_engine, options, set_engine ###################################################################### # A list to store the allocated scene numbers __scene_number_list = set((0,)) def figure(figure=None, bgcolor=None, fgcolor=None, engine=None, size=(400, 350)): """ Creates a new scene or retrieves an existing scene. If the mayavi engine is not running this also starts it. **Keyword arguments** :figure: The name of the figure, or handle to it. :bgcolor: The color of the background (None is default). :fgcolor: The color of the foreground, that is the color of all text annotation labels (axes, orientation axes, scalar bar labels). It should be sufficiently far from `bgcolor` to see the annotation texts. (None is default). :engine: The mayavi engine that controls the figure. :size: The size of the scene created, in pixels. May not apply for certain scene viewer. """ if isinstance(figure, Scene): if figure.scene is None: engine = registry.find_scene_engine(figure) else: engine = registry.find_scene_engine(figure.scene) set_engine(engine) engine.current_scene = figure else: if engine is None: engine = get_engine() if figure is None: name = max(__scene_number_list) + 1 __scene_number_list.update((name,)) name = 'Mayavi Scene %d' % name engine.new_scene(name=name, size=size) engine.current_scene.name = name else: if type(figure) in (IntType, np.int, np.int0, np.int8, np.int16, np.int32, np.int64): name = int(figure) __scene_number_list.update((name,)) name = 'Mayavi Scene %d' % name else: name = str(figure) # Go looking in the engine see if the scene is not already # running for scene in engine.scenes: if scene.name == name: engine.current_scene = scene return scene else: engine.new_scene(name=name, size=size) engine.current_scene.name = name figure = engine.current_scene scene = figure.scene if scene is not None: if hasattr(scene, 'isometric_view'): scene.isometric_view() else: # Not every viewer might implement this method view(40, 50) scene = figure.scene if scene is not None: if bgcolor is None: bgcolor = options.background_color scene.background = bgcolor if fgcolor is None: fgcolor = options.foreground_color scene.foreground = fgcolor return figure def gcf(engine=None): """Return a handle to the current figure. You can supply the engine from which you want to retrieve the current figure, if you have several mayavi engines. """ if engine is None: engine = get_engine() scene = engine.current_scene if scene is None: return figure(engine=engine) return scene def clf(figure=None): """Clear the current figure. You can also supply the figure that you want to clear. """ try: if figure is None: scene = gcf() else: scene = figure disable_render = scene.scene.disable_render scene.scene.disable_render = True scene.children[:] = [] scene._mouse_pick_dispatcher.clear_callbacks() scene.scene.disable_render = disable_render except AttributeError: pass gc.collect() def close(scene=None, all=False): """ Close a figure window close() by itself closes the current figure. close(num) closes figure number num. close(name) closes figure named name. close(figure), where figure is a scene instance, closes that figure. close(all=True) closes all figures controlled by mlab """ if all is True: engine = get_engine() # We need the copy, as the list gets pruned as we close scenes for scene in copy.copy(engine.scenes): engine.close_scene(scene) return if not isinstance(scene, Scene): engine = get_engine() if scene is None: scene = engine.current_scene else: if type(scene) in (IntType, np.int, np.int0, np.int8, np.int16, np.int32, np.int64): scene = int(scene) name = 'Mayavi Scene %d' % scene else: name = str(scene) # Go looking in the engine see if the scene is not already # running for scene in engine.scenes: if scene.name == name: break else: warnings.warn('Scene %s not managed by mlab' % name) return else: if scene.scene is None: engine = registry.find_scene_engine(scene) else: engine = registry.find_scene_engine(scene.scene) engine.close_scene(scene) def draw(figure=None): """ Forces a redraw of the current figure. """ if figure is None: figure = gcf() figure.render() def savefig(filename, size=None, figure=None, magnification='auto', **kwargs): """ Save the current scene. The output format are deduced by the extension to filename. Possibilities are png, jpg, bmp, tiff, ps, eps, pdf, rib (renderman), oogl (geomview), iv (OpenInventor), vrml, obj (wavefront) **Parameters** :size: the size of the image created (unless magnification is set, in which case it is the size of the window used for rendering). :figure: the figure instance to save to a file. :magnification: the magnification is the scaling between the pixels on the screen, and the pixels in the file saved. If you do not specify it, it will be calculated so that the file is saved with the specified size. If you specify a magnification, Mayavi will use the given size as a screen size, and the file size will be 'magnification * size'. **Notes** If the size specified is larger than the window size, and no magnification parameter is passed, the magnification of the scene is changed so that the image created has the requested size. Please note that if you are trying to save images with sizes larger than the window size, there will be additional computation cost. Any extra keyword arguments are passed along to the respective image format's save method. """ if figure is None: figure = gcf() current_mag = figure.scene.magnification try: if size is not None: current_x, current_y = tuple(figure.scene.get_size()) target_x, target_y = size if magnification is 'auto': magnification = max(target_x//current_x, target_y//current_y) + 1 target_x = int(target_x/magnification) target_y = int(target_y/magnification) size = target_x, target_y elif magnification is 'auto': magnification = 1 figure.scene.magnification = int(magnification) figure.scene.save(filename, size=size, **kwargs) finally: figure.scene.magnification = int(current_mag) def sync_camera(reference_figure, target_figure): """ Synchronise the camera of the target_figure on the camera of the reference_figure. """ reference_figure.scene._renderer.sync_trait('active_camera', target_figure.scene._renderer) target_figure.scene._renderer.active_camera.on_trait_change( lambda: do_later(target_figure.scene.render)) def screenshot(figure=None, mode='rgb', antialiased=False): """ Return the current figure pixmap as an array. **Parameters** :figure: a figure instance or None, optional If specified, the figure instance to capture the view of. :mode: {'rgb', 'rgba'} The color mode of the array captured. :antialiased: {True, False} Use anti-aliasing for rendering the screenshot. Uses the number of aa frames set by figure.scene.anti_aliasing_frames **Notes** On most systems, this works similarly to taking a screenshot of the rendering window. Thus if it is hidden by another window, you will capture the other window. This limitation is due to the heavy use of the hardware graphics system. **Examples** This function can be useful for integrating 3D plotting with Mayavi in a 2D plot created by matplotlib. >>> from mayavi import mlab >>> mlab.test_plot3d() >>> arr = mlab.screenshot() >>> import pylab as pl >>> pl.imshow(arr) >>> pl.axis('off') >>> pl.show() """ if figure is None: figure = gcf() x, y = tuple(figure.scene.get_size()) # Try to lift the window figure.scene._lift() if mode == 'rgb': out = tvtk.UnsignedCharArray() shape = (y, x, 3) pixel_getter = figure.scene.render_window.get_pixel_data pg_args = (0, 0, x-1, y-1, 1, out) elif mode == 'rgba': out = tvtk.FloatArray() shape = (y, x, 4) pixel_getter = figure.scene.render_window.get_rgba_pixel_data pg_args = (0, 0, x-1, y-1, 1, out) else: raise ValueError('mode type not understood') if antialiased: # save the current aa value to restore it later old_aa = figure.scene.render_window.aa_frames figure.scene.render_window.aa_frames = figure.scene.anti_aliasing_frames figure.scene.render() pixel_getter(*pg_args) figure.scene.render_window.aa_frames = old_aa figure.scene.render() else: pixel_getter(*pg_args) # Return the array in a way that pylab.imshow plots it right: out = out.to_array() out.shape = shape out = np.flipud(out) return out mayavi-4.1.0/mayavi/sources/0000755000175100001440000000000011674464502016745 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/sources/poly_data_reader.py0000644000175100001440000001301411674464502022614 0ustar ischnellusers00000000000000"""A PolyData file reader object. """ # Author: R.Sreekanth # Suyog Dutt Jain # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import basename # Enthought imports. from traits.api import Instance, Str,Dict from traitsui.api import View, Item, Group, Include from tvtk.api import tvtk # Local imports from mayavi.core.file_data_source import FileDataSource from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.common import error ######################################################################## # `PolyDataReader` class ######################################################################## class PolyDataReader(FileDataSource): """A PolyData file reader. The reader supports all the different types of poly data files. """ # The version of this class. Used for persistence. __version__ = 0 # The PolyData file reader reader = Instance(tvtk.Object, allow_none=False, record=True) ###################################################################### # Private Traits _reader_dict = Dict(Str, Instance(tvtk.Object)) # Our View. view = View(Group(Include('time_step_group'), Item(name='base_file_name'), Item(name='reader', style='custom', resizable=True), show_labels=False), resizable=True) #output_info = PipelineInfo(datasets=['none']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ###################################################################### # `object` interface ###################################################################### def __set_pure_state__(self, state): # The reader has its own file_name which needs to be fixed. state.reader.file_name = state.file_path.abs_pth # Now call the parent class to setup everything. super(PolyDataReader, self).__set_pure_state__(state) ###################################################################### # `FileDataSource` interface ###################################################################### def update(self): self.reader.update() if len(self.file_path.get()) == 0: return self.render() ###################################################################### # Non-public interface ###################################################################### def _file_path_changed(self, fpath): value = fpath.get() if len(value) == 0: return # Extract the file extension splitname = value.strip().split('.') extension = splitname[-1].lower() # Select polydata reader based on file type old_reader = self.reader if self._reader_dict.has_key(extension): self.reader = self._reader_dict[extension] else: error('Invalid extension for file: %s'%value) return self.reader.file_name = value.strip() self.reader.update() self.reader.update_information() if old_reader is not None: old_reader.on_trait_change(self.render, remove=True) self.reader.on_trait_change(self.render) old_outputs = self.outputs self.outputs = [self.reader.output] if self.outputs == old_outputs: self.data_changed = True # Change our name on the tree view self.name = self._get_name() def _get_name(self): """ Returns the name to display on the tree view. Note that this is not a property getter. """ fname = basename(self.file_path.get()) ret = "%s"%fname if len(self.file_list) > 1: ret += " (timeseries)" if '[Hidden]' in self.name: ret += ' [Hidden]' return ret def __reader_dict_default(self): """Default value for reader dict.""" rd = {'stl':tvtk.STLReader(), 'stla':tvtk.STLReader(), 'stlb':tvtk.STLReader(), 'txt':tvtk.SimplePointsReader(), 'raw':tvtk.ParticleReader(), 'ply':tvtk.PLYReader(), 'pdb':tvtk.PDBReader(), 'slc':tvtk.SLCReader(), 'xyz':tvtk.XYZMolReader(), 'obj':tvtk.OBJReader(), 'facet':tvtk.FacetReader(), 'cube':tvtk.GaussianCubeReader(), 'g':tvtk.BYUReader(), } return rd # Callable to check if the reader can actually read the file def can_read(cls,filename): """ Class method to check if the reader can actually read the file. Returns 'True' if it can read it succesfully else 'False' """ # Extract the file extension splitname = filename.strip().split('.') extension = splitname[-1].lower() if extension == 'xyz': from vtk import vtkObject o = vtkObject w = o.GetGlobalWarningDisplay() o.SetGlobalWarningDisplay(0) # Turn it off. r = tvtk.XYZMolReader() r.file_name = filename r.update() o.SetGlobalWarningDisplay(w) if len(r.output.points) != 0: return True return False return None can_read = classmethod(can_read) mayavi-4.1.0/mayavi/sources/parametric_surface.py0000644000175100001440000001061011674464502023154 0ustar ischnellusers00000000000000"""A module that displays Parametric Surfaces, which are generated by sets of equations describing the deformation of the real plane into the surface. """ # Authors: KK Rai (kk.rai [at] iitb.ac.in) # R. Ambareesha (ambareesha [at] iitb.ac.in) # Prabhu Ramachandran (prabhu [at] aero.iitb.ac.in) # Enthought library imports. from traits.api import Instance, Enum, Dict, Str from tvtk.api import tvtk # Local imports from mayavi.core.source import Source from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ParametricSurface` class. ###################################################################### class ParametricSurface(Source): # The version of this class. Used for persistence. __version__ = 0 # Flag to set the parametric function type. function = Enum('boy','conic_spiral','cap','dini', 'ellipsoid','enneper','figure8klein','klein', 'mobius','random_hills','roman','spline', 'super_ellipsoid','super_toroid','torus', desc='which parametric function to be used') # Define the trait 'parametric_function' whose value must be an instance of # type ParametricFunction parametric_function = Instance(tvtk.ParametricFunction, allow_none=False, record=True) # The Parametric function source which generates the data. source = Instance(tvtk.ParametricFunctionSource, args=(), kw={'scalar_mode': 'distance'}, allow_none=False, record=True) # Information about what this object can produce. output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ######################################## # Private traits. # A dictionary that maps the function names to instances of the # parametric surfaces _function_dict = Dict(Str, Instance(tvtk.ParametricFunction, allow_none=False)) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): # Setup the function dict. fd = {'boy':tvtk.ParametricBoy(), 'conic_spiral':tvtk.ParametricConicSpiral(), 'cap':tvtk.ParametricCrossCap(), 'dini':tvtk.ParametricDini(), 'ellipsoid':tvtk.ParametricEllipsoid(), 'enneper':tvtk.ParametricEnneper(), 'figure8klein':tvtk.ParametricFigure8Klein(), 'klein':tvtk.ParametricKlein(), 'mobius':tvtk.ParametricMobius(), 'random_hills':tvtk.ParametricRandomHills(), 'roman':tvtk.ParametricRoman(), 'spline':tvtk.ParametricSpline(), 'super_ellipsoid':tvtk.ParametricSuperEllipsoid(), 'super_toroid':tvtk.ParametricSuperToroid(), 'torus':tvtk.ParametricTorus()} self._function_dict = fd # Call parent class' init. super(ParametricSurface, self).__init__(**traits) # Initialize the function to the default mode's instance from # the dictionary self.parametric_function = self._function_dict[self.function] # Call render everytime source traits change. self.source.on_trait_change(self.render) # Setup the outputs. self.outputs = [self.source.output] ###################################################################### # Non-public methods. ###################################################################### def _function_changed(self, value): """This method is invoked (automatically) when the `function` trait is changed. """ self.parametric_function = self._function_dict[self.function] def _parametric_function_changed(self, old, new): """This method is invoked (automatically) when the `parametric_function` attribute is changed. """ self.source.parametric_function = self.parametric_function # Setup the handlers so that if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) self.data_changed = True mayavi-4.1.0/mayavi/sources/ui/0000755000175100001440000000000011674464502017362 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/sources/ui/parametric_surface.py0000644000175100001440000000207311674464502023575 0ustar ischnellusers00000000000000""" Traits View definition file. The view trait of the parent class is extracted from the model definition file. This file can either be exec()ed or imported. See core/base.py:Base.trait_view() for what is currently used. Using exec() allows view changes without needing to restart Mayavi, but is slower than importing. """ # Authors: Prabhu Ramachandran # Vibha Srinivasan # Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. from traitsui.api import Item, Group, View view = View(Group(Item(name='function'), Item(name='parametric_function', style='custom', resizable=True), label='Function', show_labels=False ), Group(Item(name='source', style='custom', resizable=True), label='Source', show_labels=False), resizable=True) mayavi-4.1.0/mayavi/sources/ui/__init__.py0000644000175100001440000000013211674464502021467 0ustar ischnellusers00000000000000# Author: Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/sources/three_ds_importer.py0000644000175100001440000000302411674464502023034 0ustar ischnellusers00000000000000"""An importer for 3D Studio files. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import basename # Enthought imports. from tvtk.api import tvtk from traits.api import Instance # Local imports from mayavi.sources.vrml_importer import VRMLImporter ###################################################################### # `ThreeDSImporter` class. ###################################################################### class ThreeDSImporter(VRMLImporter): # The 3DS importer. reader = Instance(tvtk.ThreeDSImporter, args=(), kw={'compute_normals':True}, allow_none=False, record=True) ###################################################################### # Non-public interface ###################################################################### def _file_name_changed(self, value): # This hack is necessary since for some reason the importer # does not clear out the earlier actors. self.reader = reader = tvtk.ThreeDSImporter(compute_normals=True) reader.file_name = value if self.scene is not None: self.reader.render_window = self.scene.render_window name = "3DStudio file (%s)"%basename(self.file_name) if '[Hidden]' in self.name: name += ' [Hidden]' self.name = name self._file_path.set(value) self._update_reader() self.render() mayavi-4.1.0/mayavi/sources/builtin_image.py0000644000175100001440000000757111674464502022141 0ustar ischnellusers00000000000000""" A module that offers lots of VTK image data sources """ #Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Enum, Dict, Str from traitsui.api import View, Item, Group from tvtk.api import tvtk # Local imports from mayavi.core.source import Source from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `BuiltinImage` class. ###################################################################### class BuiltinImage(Source): # The version of this class. Used for persistence. __version__ = 0 # Flag to set the image data type. source = Enum('ellipsoid','gaussian','grid','mandelbrot','noise', 'sinusoid','rt_analytic', desc='which image data source to be used') # Define the trait 'data_source' whose value must be an instance of # type ImageAlgorithm data_source = Instance(tvtk.ImageAlgorithm, allow_none=False, record=True) # Information about what this object can produce. output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) # Create the UI for the traits. view = View(Group(Item(name='source'), Item(name='data_source', style='custom', resizable=True), label='Image Source', show_labels=False), resizable=True) ######################################## # Private traits. # A dictionary that maps the source names to instances of the # image data objects. _source_dict = Dict(Str, Instance(tvtk.ImageAlgorithm, allow_none=False)) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): # Call parent class' init. super(BuiltinImage, self).__init__(**traits) # Initialize the source to the default mode's instance from # the dictionary if needed. if 'source' not in traits: self._source_changed(self.source) def __set_pure_state__(self, state): self.source = state.source super(BuiltinImage, self).__set_pure_state__(state) ###################################################################### # Non-public methods. ###################################################################### def _source_changed(self, value): """This method is invoked (automatically) when the `function` trait is changed. """ self.data_source = self._source_dict[self.source] def _data_source_changed(self, old, new): """This method is invoked (automatically) when the image data source is changed .""" self.outputs = [self.data_source.output] if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) def __source_dict_default(self): """The default _source_dict trait.""" sd = { 'ellipsoid':tvtk.ImageEllipsoidSource(), 'gaussian':tvtk.ImageGaussianSource(), 'grid':tvtk.ImageGridSource(), 'mandelbrot':tvtk.ImageMandelbrotSource(), 'noise':tvtk.ImageNoiseSource(), 'sinusoid':tvtk.ImageSinusoidSource(), } if hasattr(tvtk, 'RTAnalyticSource'): sd['rt_analytic'] = tvtk.RTAnalyticSource() else: sd['rt_analytic'] = tvtk.ImageNoiseSource() return sd mayavi-4.1.0/mayavi/sources/api.py0000644000175100001440000000151711674464502020074 0ustar ischnellusers00000000000000""" Defines the publicly accessible MayaVi2 sources. """ # Author: Frederic Petit, Prabhu Ramachandran # Copyright (c) 2007-2008, Enthought, Inc. # License: BSD Style. from array_source import ArraySource from builtin_image import BuiltinImage from builtin_surface import BuiltinSurface from chaco_reader import ChacoReader from image_reader import ImageReader from parametric_surface import ParametricSurface from plot3d_reader import PLOT3DReader from point_load import PointLoad from poly_data_reader import PolyDataReader from three_ds_importer import ThreeDSImporter from vrml_importer import VRMLImporter from volume_reader import VolumeReader from vtk_data_source import VTKDataSource from vtk_file_reader import VTKFileReader from vtk_xml_file_reader import VTKXMLFileReader from unstructured_grid_reader import UnstructuredGridReader mayavi-4.1.0/mayavi/sources/chaco_reader.py0000644000175100001440000000456211674464502021725 0ustar ischnellusers00000000000000"""A Chaco file reader. """ # Author: Suyog Dutt Jain # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Str from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports. from mayavi.core.source import Source from mayavi.core.pipeline_info import PipelineInfo ######################################################################## # `ChacoReader` class ######################################################################## class ChacoReader(Source): """A Chaco reader. """ # The version of this class. Used for persistence. __version__ = 0 base_name = Str('', desc='basename of the Chaco files') # The VTK data file reader. reader = Instance(tvtk.ChacoReader, args=(), allow_none=False, record=True) # Information about what this object can produce. output_info = PipelineInfo(datasets=['unstructured_grid']) ######################################## # View related code. # Our view. view = View(Group(Item(name='reader', style='custom', resizable=True), show_labels=False), resizable=True) ###################################################################### # `FileDataSource` interface ###################################################################### def __init__(self, base_name='', configure=True, **traits): super(ChacoReader, self).__init__(**traits) if configure: self.reader.edit_traits(kind='livemodal') self.base_name = self.reader.base_name def update(self): if len(self.base_name) == 0: return self.reader.update() self.render() ###################################################################### # Non-public interface ###################################################################### def _base_name_changed(self, value): if len(value) == 0: return else: self.reader.base_name = value self._update_reader_output() def _update_reader_output(self): self.reader.update() self.reader.update_information() self.reader.on_trait_change(self.render) self.outputs = [self.reader.output] self.data_changed = True mayavi-4.1.0/mayavi/sources/plot3d_reader.py0000644000175100001440000002366011674464502022055 0ustar ischnellusers00000000000000"""A PLOT3D file reader. This reader does not support a timeseries of files. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import basename, isfile, exists, splitext # Enthought library imports. from traits.api import Trait, Instance, Str, TraitPrefixMap, Button from traitsui.api import View, Group, Item, FileEditor from tvtk.api import tvtk from apptools.persistence.state_pickler import set_state from apptools.persistence.file_path import FilePath # Local imports. from mayavi.core.source import Source from mayavi.core.common import handle_children_state, error from mayavi.core.pipeline_info import PipelineInfo ######################################################################## # `PLOT3DReader` class ######################################################################## class PLOT3DReader(Source): """A PLOT3D file reader. This reader does not support a timeseries of files. """ # The version of this class. Used for persistence. __version__ = 0 # XYZ file name xyz_file_name = Str('', desc='the XYZ file') # The (optional) Q file. q_file_name = Str('', desc='the Q file') # The active scalar name. scalars_name = Trait('density', TraitPrefixMap({'density': 100, 'pressure':110, 'temperature': 120, 'enthalpy': 130, 'internal energy': 140, 'kinetic energy': 144, 'velocity magnitude': 153, 'stagnation energy': 163, 'entropy': 170, 'swirl': 184}), desc='scalar data attribute to show') # The active vector name. vectors_name = Trait('momentum', TraitPrefixMap({'velocity': 200, 'vorticity': 201, 'momentum': 202, 'pressure gradient': 210}), desc='vector data attribute to show') # The VTK data file reader. reader = Instance(tvtk.PLOT3DReader, args=(), allow_none=False, record=True) # Information about what this object can produce. output_info = PipelineInfo(datasets=['structured_grid']) ######################################## # View related code. update_reader = Button('Update Reader') # Our view. view = View(Group(Item('xyz_file_name', editor=FileEditor()), Item('q_file_name', editor=FileEditor()), Item(name='scalars_name', enabled_when='len(object.q_file_name) > 0'), Item(name='vectors_name', enabled_when='len(object.q_file_name)>0'), Item(name='update_reader'), label='Reader', ), Group(Item(name='reader', style='custom', resizable=True), show_labels=False, label='PLOT3DReader' ), resizable=True) ######################################## # Private traits. # The current file paths. This is not meant to be touched by the # user. xyz_file_path = Instance(FilePath, args=(), desc='the current XYZ file path') q_file_path = Instance(FilePath, args=(), desc='the current Q file path') ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(PLOT3DReader, self).__get_pure_state__() # These traits are dynamically created. for name in ('scalars_name', 'vectors_name', 'xyz_file_name', 'q_file_name'): d.pop(name, None) return d def __set_pure_state__(self, state): xyz_fn = state.xyz_file_path.abs_pth q_fn = state.q_file_path.abs_pth if not isfile(xyz_fn): msg = 'Could not find file at %s\n'%xyz_fn msg += 'Please move the file there and try again.' raise IOError, msg # Setup the reader state. set_state(self, state, first=['reader'], ignore=['*']) # Initialize the files. self.initialize(xyz_fn, q_fn, configure=False) # Now set the remaining state without touching the children. set_state(self, state, ignore=['children', 'xyz_file_path', 'q_file_path']) # Setup the children. handle_children_state(self.children, state.children) # Setup the children's state. set_state(self, state, first=['children'], ignore=['*']) ###################################################################### # `FileDataSource` interface ###################################################################### def initialize(self, xyz_file_name, q_file_name='', configure=True): """Given an xyz filename and a Q filename which may or may not be part of a time series, this initializes the list of files. This method need not be called to initialize the data. If configure is True, it pops up a UI to configure the PLOT3DReader. """ if len(q_file_name) == 0: base = splitext(xyz_file_name)[0] qf = base + '.q' if exists(qf): q_file_name = qf if configure: # First set properties of the reader. This is useful when # the data format has atypical defaults. Automatic # detection can be disastrous sometimes due to VTK related # problems. self.reader.edit_traits(kind='livemodal') self.xyz_file_name = xyz_file_name if len(q_file_name) > 0: self.q_file_name = q_file_name def update(self): if len(self.xyz_file_path.get()) == 0: return self.reader.update() self.render() ###################################################################### # Non-public interface ###################################################################### def _xyz_file_name_changed(self, value): if len(value) == 0: return else: self.reader.xyz_file_name = value self.xyz_file_path.set(value) self._update_reader_output() def _q_file_name_changed(self, value): if len(value) == 0: return else: self.reader.q_file_name = value self.q_file_path.set(value) self._update_reader_output() def _update_reader_output(self): r = self.reader r.update() if r.error_code != 0: try: self.reader.i_blanking = True except AttributeError: pass else: r.update() # Try reading file. if r.error_code != 0: # No output so the file might be an ASCII file. try: # Turn off IBlanking. r.set(i_blanking = False, binary_file = False) except AttributeError: pass else: r.update() # Try again this time as ascii and with blanking. if r.error_code != 0: # No output so the file might be an ASCII file. try: # Turn on IBlanking. r.i_blanking = True except AttributeError: pass else: r.update() # If there still is an error, ask the user. if r.error_code != 0: r.edit_traits(kind='livemodal') r.update() # If there still is an error, ask the user to retry. if r.error_code != 0: msg = 'Unable to read file properly. '\ 'Please check the settings of the reader '\ 'on the UI and press the "Update Reader" button '\ 'when done and try again!' error(msg) return # Now setup the outputs by resetting self.outputs. Changing # the outputs automatically fires a pipeline_changed event. try: n = r.number_of_output_ports except AttributeError: # for VTK >= 4.5 n = r.number_of_outputs outputs = [] for i in range(n): outputs.append(r.get_output(i)) self.outputs = outputs # Fire data_changed just in case the outputs are not # really changed. This can happen if the dataset is of # the same type as before. self.data_changed = True # Change our name on the tree view self.name = self._get_name() def _scalars_name_changed(self, value): self.reader.scalar_function_number = self.scalars_name_ self.reader.modified() self.update() self.data_changed = True def _vectors_name_changed(self, value): self.reader.vector_function_number = self.vectors_name_ self.reader.modified() self.update() self.data_changed = True def _update_reader_fired(self): self.reader.modified() self._update_reader_output() self.pipeline_changed = True def _get_name(self): """ Gets the name to display on the tree view. """ xyz_fname = basename(self.xyz_file_path.get()) q_fname = basename(self.q_file_path.get()) if len(self.q_file_name) > 0: ret = "PLOT3D:%s, %s"%(xyz_fname, q_fname) else: ret = "PLOT3D:%s"%(xyz_fname) if '[Hidden]' in self.name: ret += ' [Hidden]' return ret mayavi-4.1.0/mayavi/sources/vrml_importer.py0000644000175100001440000001153611674464502022226 0ustar ischnellusers00000000000000"""An importer for VRML files. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import basename # Enthought imports. from tvtk.api import tvtk from traits.api import Instance, Str from traitsui.api import View, Item, FileEditor from apptools.persistence.file_path import FilePath from apptools.persistence.state_pickler import set_state # Local imports from mayavi.core.source import Source from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `VRMLImporter` class. ###################################################################### class VRMLImporter(Source): __version__ = 0 # The file name. file_name = Str('', enter_set=True, auto_set=False, desc='the VRML file name') # The VRML importer. reader = Instance(tvtk.VRMLImporter, args=(), allow_none=False, record=True) output_info = PipelineInfo(datasets=['none']) ############### # Private traits. # Our file path used for persistence _file_path = Instance(FilePath, args=()) # Our View. view = View(Item(name='file_name', editor=FileEditor())) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(VRMLImporter, self).__get_pure_state__() # These traits are dynamically created. for name in ('reader', 'file_name'): d.pop(name) return d def __set_pure_state__(self, state): # The reader has its own file_name which needs to be fixed. fname = state._file_path.abs_pth # Now call the parent class to setup everything. self.initialize(fname) # Setup the rest of the state. set_state(self, state, ignore=['_file_path']) def initialize(self, file_name): self.file_name = file_name ###################################################################### # `PipelineBase` interface. ###################################################################### def add_actors(self): """Adds `self.actors` to the scene. """ if not self._actors_added: self.reader.render_window = self.scene.render_window self._update_reader() self._actors_added = True if not self.visible: self._visible_changed(self.visible) self.scene.render() def remove_actors(self): """Removes `self.actors` from the scene. """ if self._actors_added: self.scene.remove_actors(self.actors) self._actors_added = False self.scene.render() ###################################################################### # Non-public interface ###################################################################### def _file_name_changed(self, value): reader = self.reader reader.file_name = value self._file_path.set(value) self._update_reader() self.render() name = "VRML file (%s)"%basename(self.file_name) if '[Hidden]' in self.name: name += ' [Hidden]' self.name = name def _update_reader(self): reader = self.reader if self.scene is None or reader.file_name is None \ or len(reader.file_name) == 0: return actors1 = [x for x in self.scene.renderer.actors] reader.read() self.scene.render() actors2 = [x for x in self.scene.renderer.actors] self.actors = [x for x in actors2 if x not in actors1] # If these are the first actors on scene reset the view. if len(actors1) == 0: self.scene.reset_zoom() def _scene_changed(self, old, new): if self._actors_added: old.remove_actors(self.actors) reader = self.reader reader.render_window = new.render_window self._update_reader() def _actors_changed(self, old, new): if self._actors_added: self.scene.remove_actors(old) # The actors are added automatically when the importer # does a read. self.scene.render() def _actors_items_changed(self, list_event): if self._actors_added: self.scene.remove_actors(list_event.removed) # The actors are added automatically when the importer # does a read. self.scene.render() def _visible_changed(self, value): if value: if not self._actors_added: self.scene.add_actors(self.actors) self._actors_added = True super(VRMLImporter, self)._visible_changed(value) mayavi-4.1.0/mayavi/sources/vtk_data_source.py0000644000175100001440000003323311674464502022500 0ustar ischnellusers00000000000000"""This source manages a VTK dataset given to it. When this source is pickled or persisted, it saves the data given to it in the form of a gzipped string. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. import os import tempfile # Enthought library imports. from traits.api import Instance, List, Str, Bool, Int from traitsui.api import View, Group, Item from apptools.persistence.state_pickler \ import gzip_string, gunzip_string, set_state from tvtk.api import tvtk from tvtk import messenger # Local imports. from mayavi.core.source import Source from mayavi.core.common import handle_children_state from mayavi.core.trait_defs import DEnum from mayavi.core.pipeline_info import (PipelineInfo, get_tvtk_dataset_name) from vtk_xml_file_reader import get_all_attributes ###################################################################### # Utility functions. ###################################################################### def write_dataset_to_string(data): """Given a dataset, convert the dataset to an ASCII string that can be stored for persistence. """ w = tvtk.DataSetWriter(write_to_output_string=1) warn = w.global_warning_display w.set_input(data) w.global_warning_display = 0 w.update() if w.output_string_length == 0: # Some VTK versions (5.2) have a bug when writing structured # grid datasets and produce empty output. We work around this # by writing to a file and then reading that output. w.write_to_output_string = 0 fh, fname = tempfile.mkstemp('.vtk') os.close(fh); os.remove(fname) w.file_name = fname w.write() # Read the data and delete the file. sdata = open(fname).read() os.remove(fname) else: sdata = w.output_string w.global_warning_display = warn return sdata def has_attributes(dataset): """Returns `True` when the given TVTK `dataset` has any attribute arrays in point and cell data and `False` otherwise. """ pd = dataset.point_data if pd is not None and pd.number_of_arrays > 0: return True cd = dataset.cell_data if cd is not None and cd.number_of_arrays > 0: return True return False ###################################################################### # `VTKDataSource` class ###################################################################### class VTKDataSource(Source): """This source manages a VTK dataset given to it. When this source is pickled or persisted, it saves the data given to it in the form of a gzipped string. Note that if the VTK dataset has changed internally and you need to notify the mayavi pipeline to flush the data just call the `modified` method of the VTK dataset and the mayavi pipeline will update automatically. """ # The version of this class. Used for persistence. __version__ = 0 # The VTK dataset to manage. data = Instance(tvtk.DataSet, allow_none=False) # Information about what this object can produce. output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ######################################## # Dynamic traits: These traits are dynamic and are updated on the # _update_data method. # The active point scalar name. point_scalars_name = DEnum(values_name='_point_scalars_list', desc='scalar point data attribute to use') # The active point vector name. point_vectors_name = DEnum(values_name='_point_vectors_list', desc='vectors point data attribute to use') # The active point tensor name. point_tensors_name = DEnum(values_name='_point_tensors_list', desc='tensor point data attribute to use') # The active cell scalar name. cell_scalars_name = DEnum(values_name='_cell_scalars_list', desc='scalar cell data attribute to use') # The active cell vector name. cell_vectors_name = DEnum(values_name='_cell_vectors_list', desc='vectors cell data attribute to use') # The active cell tensor name. cell_tensors_name = DEnum(values_name='_cell_tensors_list', desc='tensor cell data attribute to use') ######################################## # Our view. view = View(Group(Item(name='point_scalars_name'), Item(name='point_vectors_name'), Item(name='point_tensors_name'), Item(name='cell_scalars_name'), Item(name='cell_vectors_name'), Item(name='cell_tensors_name'), Item(name='data'), )) ######################################## # Private traits. # These private traits store the list of available data # attributes. The non-private traits use these lists internally. _point_scalars_list = List(Str) _point_vectors_list = List(Str) _point_tensors_list = List(Str) _cell_scalars_list = List(Str) _cell_vectors_list = List(Str) _cell_tensors_list = List(Str) # This filter allows us to change the attributes of the data # object and will ensure that the pipeline is properly taken care # of. Directly setting the array in the VTK object will not do # this. _assign_attribute = Instance(tvtk.AssignAttribute, args=(), allow_none=False) # Toggles if this is the first time this object has been used. _first = Bool(True) # The ID of the observer for the data. _observer_id = Int(-1) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(VTKDataSource, self).__get_pure_state__() for name in ('_assign_attribute', '_first', '_observer'): d.pop(name, None) for name in ('point_scalars', 'point_vectors', 'point_tensors', 'cell_scalars', 'cell_vectors', 'cell_tensors'): d.pop('_' + name + '_list', None) d.pop('_' + name + '_name', None) data = self.data if data is not None: sdata = write_dataset_to_string(data) z = gzip_string(sdata) d['data'] = z return d def __set_pure_state__(self, state): z = state.data if z is not None: d = gunzip_string(z) r = tvtk.DataSetReader(read_from_input_string=1, input_string=d) warn = r.global_warning_display r.global_warning_display = 0 r.update() r.global_warning_display = warn self.data = r.output # Now set the remaining state without touching the children. set_state(self, state, ignore=['children', 'data']) # Setup the children. handle_children_state(self.children, state.children) # Setup the children's state. set_state(self, state, first=['children'], ignore=['*']) ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. """ # Do nothing if we are already running. if self.running: return # Update the data just in case. self._update_data() # Call the parent method to do its thing. This will typically # start all our children. super(VTKDataSource, self).start() def update(self): """Invoke this to flush data changes downstream. This is typically used when you change the data object and want the mayavi pipeline to refresh. """ # This tells the VTK pipeline that the data has changed. This # will fire the data_changed event automatically. self.data.modified() ###################################################################### # Non-public interface ###################################################################### def _data_changed(self, old, new): if has_attributes(self.data): aa = self._assign_attribute aa.input = new self._update_data() self.outputs = [aa.output] else: self.outputs = [self.data] self.data_changed = True self.output_info.datasets = \ [get_tvtk_dataset_name(self.outputs[0])] # Add an observer to the VTK dataset after removing the one # for the old dataset. We use the messenger to avoid an # uncollectable reference cycle. See the # tvtk.messenger module documentation for details. if old is not None: old.remove_observer(self._observer_id) self._observer_id = new.add_observer('ModifiedEvent', messenger.send) new_vtk = tvtk.to_vtk(new) messenger.connect(new_vtk, 'ModifiedEvent', self._fire_data_changed) # Change our name so that our label on the tree is updated. self.name = self._get_name() def _fire_data_changed(self, *args): """Simply fire the `data_changed` event.""" self.data_changed = True def _set_data_name(self, data_type, attr_type, value): if value is None: return dataset = self.data if len(value) == 0: # If the value is empty then we deactivate that attribute. d = getattr(dataset, attr_type + '_data') method = getattr(d, 'set_active_%s'%data_type) method(None) self.data_changed = True return aa = self._assign_attribute data = None if attr_type == 'point': data = dataset.point_data elif attr_type == 'cell': data = dataset.cell_data method = getattr(data, 'set_active_%s'%data_type) method(value) aa.assign(value, data_type.upper(), attr_type.upper() +'_DATA') if data_type == 'scalars' and dataset.is_a('vtkImageData'): # Set the scalar_type for image data, if not you can either # get garbage rendered or worse. s = getattr(dataset, attr_type + '_data').scalars r = s.range dataset.scalar_type = s.data_type aa.output.scalar_type = s.data_type aa.update() # Fire an event, so the changes propagate. self.data_changed = True def _point_scalars_name_changed(self, value): self._set_data_name('scalars', 'point', value) def _point_vectors_name_changed(self, value): self._set_data_name('vectors', 'point', value) def _point_tensors_name_changed(self, value): self._set_data_name('tensors', 'point', value) def _cell_scalars_name_changed(self, value): self._set_data_name('scalars', 'cell', value) def _cell_vectors_name_changed(self, value): self._set_data_name('vectors', 'cell', value) def _cell_tensors_name_changed(self, value): self._set_data_name('tensors', 'cell', value) def _update_data(self): if self.data is None: return pnt_attr, cell_attr = get_all_attributes(self.data) pd = self.data.point_data scalars = pd.scalars if self.data.is_a('vtkImageData') and scalars is not None: # For some reason getting the range of the scalars flushes # the data through to prevent some really strange errors # when using an ImagePlaneWidget. r = scalars.range self._assign_attribute.output.scalar_type = scalars.data_type self.data.scalar_type = scalars.data_type def _setup_data_traits(obj, attributes, d_type): """Given the object, the dict of the attributes from the `get_all_attributes` function and the data type (point/cell) data this will setup the object and the data. """ attrs = ['scalars', 'vectors', 'tensors'] aa = obj._assign_attribute data = getattr(obj.data, '%s_data'%d_type) for attr in attrs: values = attributes[attr] values.append('') setattr(obj, '_%s_%s_list'%(d_type, attr), values) if len(values) > 1: default = getattr(obj, '%s_%s_name'%(d_type, attr)) if obj._first and len(default) == 0: default = values[0] getattr(data, 'set_active_%s'%attr)(default) aa.assign(default, attr.upper(), d_type.upper() +'_DATA') aa.update() kw = {'%s_%s_name'%(d_type, attr): default, 'trait_change_notify': False} obj.set(**kw) _setup_data_traits(self, pnt_attr, 'point') _setup_data_traits(self, cell_attr, 'cell') if self._first: self._first = False # Propagate the data changed event. self.data_changed = True def _get_name(self): """ Gets the name to display on the tree. """ ret = "VTK Data (uninitialized)" if self.data is not None: typ = self.data.__class__.__name__ ret = "VTK Data (%s)"%typ if '[Hidden]' in self.name: ret += ' [Hidden]' return ret mayavi-4.1.0/mayavi/sources/vtk_xml_file_reader.py0000644000175100001440000003273511674464502023336 0ustar ischnellusers00000000000000"""A VTK XML file reader object. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import basename # Enthought library imports. from traits.api import Instance, List, Str, Bool from traitsui.api import View, Group, Item, Include from tvtk.api import tvtk # Local imports. from mayavi.core.file_data_source import FileDataSource from mayavi.core.common import error from mayavi.core.trait_defs import DEnum from mayavi.core.pipeline_info import (PipelineInfo, get_tvtk_dataset_name) ###################################################################### # Utility functions. ###################################################################### def find_file_data_type(file_name): "Parses the named file to see what type of data there is." r = tvtk.XMLFileReadTester(file_name=file_name) if r.test_read_file(): return r.file_data_type else: error("File %s is not a valid VTK XML file!"%(file_name)) def get_array_type(arr): """Returns if the array is a scalar ('scalars'), vector ('vectors') or tensor ('tensors'). It looks at the number of components to decide. If it has a wierd number of components it returns the empty string. """ n = arr.number_of_components ret = {1: 'scalars', 3: 'vectors', 4: 'scalars', 9:'tensors'} return ret.get(n) or '' def get_attribute_list(data): """ Gets scalar, vector and tensor information from the given data (either cell or point data). """ attr = {'scalars':[], 'vectors':[], 'tensors':[]} if data is not None: n = data.number_of_arrays for i in range(n): name = data.get_array_name(i) arr = data.get_array(i) if arr is not None: # Some VTK datasets claim they have n arrays, but # actually some of these are None (eg the output of a # tvtk.GraphToPolyData()) t = get_array_type(arr) if len(t) > 0 and name is not None: attr[t].extend([name]) def _mk_first(lst, value): """Makes the specified `value` the first item in `lst`.""" lst.remove(value) lst.insert(0, value) attr1 = attr.copy() for a in attr: v = getattr(data, a) if v is not None: name = v.name if name is not None: try: _mk_first(attr[a], v.name) except ValueError: # Sometimes we have a multi-component scalar. attr1[a].insert(0, name) return attr1 def get_all_attributes(obj): """Gets the scalar, vector and tensor attributes that are available in the given VTK data object. """ point_attr = get_attribute_list(obj.point_data) cell_attr = get_attribute_list(obj.cell_data) return point_attr, cell_attr ###################################################################### # `VTKXMLFileReader` class ###################################################################### class VTKXMLFileReader(FileDataSource): """A VTK XML file reader. The reader supports all the different types of data sets. This reader also supports a time series. Currently, this reader assumes that there is only one output that has configurable attributes. """ # The version of this class. Used for persistence. __version__ = 0 ######################################## # Dynamic traits: These traits are dynamic and are automatically # updated depending on the contents of the file. # The active point scalar name. An empty string indicates that # the attribute is "deactivated". This is useful when you have # both point and cell attributes and want to use cell data by # default. point_scalars_name = DEnum(values_name='_point_scalars_list', desc='scalar point data attribute to use') # The active point vector name. point_vectors_name = DEnum(values_name='_point_vectors_list', desc='vectors point data attribute to use') # The active point tensor name. point_tensors_name = DEnum(values_name='_point_tensors_list', desc='tensor point data attribute to use') # The active cell scalar name. cell_scalars_name = DEnum(values_name='_cell_scalars_list', desc='scalar cell data attribute to use') # The active cell vector name. cell_vectors_name = DEnum(values_name='_cell_vectors_list', desc='vectors cell data attribute to use') # The active cell tensor name. cell_tensors_name = DEnum(values_name='_cell_tensors_list', desc='tensor cell data attribute to use') ######################################## # The VTK data file reader. reader = Instance(tvtk.XMLReader) # Information about what this object can produce. output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) # Our view. view = View(Group(Include('time_step_group'), Item(name='point_scalars_name'), Item(name='point_vectors_name'), Item(name='point_tensors_name'), Item(name='cell_scalars_name'), Item(name='cell_vectors_name'), Item(name='cell_tensors_name'), Item(name='reader'), )) ######################################## # Private traits. # These private traits store the list of available data # attributes. The non-private traits use these lists internally. _point_scalars_list = List(Str) _point_vectors_list = List(Str) _point_tensors_list = List(Str) _cell_scalars_list = List(Str) _cell_vectors_list = List(Str) _cell_tensors_list = List(Str) # This filter allows us to change the attributes of the data # object and will ensure that the pipeline is properly taken care # of. Directly setting the array in the VTK object will not do # this. _assign_attribute = Instance(tvtk.AssignAttribute, args=(), allow_none=False) # Toggles if this is the first time this object has been used. _first = Bool(True) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(VTKXMLFileReader, self).__get_pure_state__() for name in ('_assign_attribute', '_first'): d.pop(name, None) # Pickle the 'point_scalars_name' etc. since these are # properties and not in __dict__. attr = {} for name in ('point_scalars', 'point_vectors', 'point_tensors', 'cell_scalars', 'cell_vectors', 'cell_tensors'): d.pop('_' + name + '_list', None) d.pop('_' + name + '_name', None) x = name + '_name' attr[x] = getattr(self, x) d.update(attr) return d def __set_pure_state__(self, state): # The reader has its own file_name which needs to be fixed. state.reader.file_name = state.file_path.abs_pth # Now call the parent class to setup everything. super(VTKXMLFileReader, self).__set_pure_state__(state) ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. """ # Do nothing if we are already running. if self.running: return # Call the parent method to do its thing. This will typically # start all our children. super(VTKXMLFileReader, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return # Call the parent method to do its thing. super(VTKXMLFileReader, self).stop() ###################################################################### # `FileDataSource` interface ###################################################################### def update(self): if len(self.file_path.get()) == 0: return reader = self.reader reader.update() self.render() def update_data(self): if len(self.file_path.get()) == 0: return self.reader.update() pnt_attr, cell_attr = get_all_attributes(self.reader.output) def _setup_data_traits(obj, attributes, d_type): """Given the object, the dict of the attributes from the `get_all_attributes` function and the data type (point/cell) data this will setup the object and the data. """ attrs = ['scalars', 'vectors', 'tensors'] aa = obj._assign_attribute data = getattr(obj.reader.output, '%s_data'%d_type) for attr in attrs: values = attributes[attr] values.append('') setattr(obj, '_%s_%s_list'%(d_type, attr), values) if len(values) > 1: default = getattr(obj, '%s_%s_name'%(d_type, attr)) if obj._first and len(default) == 0: default = values[0] getattr(data, 'set_active_%s'%attr)(default) aa.assign(default, attr.upper(), d_type.upper() +'_DATA') aa.update() kw = {'%s_%s_name'%(d_type, attr): default, 'trait_change_notify': False} obj.set(**kw) _setup_data_traits(self, cell_attr, 'cell') _setup_data_traits(self, pnt_attr, 'point') if self._first: self._first = False # Propagate the data changed event. self.data_changed = True ###################################################################### # Non-public interface ###################################################################### def _file_path_changed(self, fpath): value = fpath.get() if len(value) == 0: return else: if self.reader is None: d_type = find_file_data_type(fpath.get()) self.reader = eval('tvtk.XML%sReader()'%d_type) reader = self.reader reader.file_name = value reader.update() # Setup the outputs by resetting self.outputs. Changing # the outputs automatically fires a pipeline_changed # event. try: n = reader.number_of_outputs except AttributeError: # for VTK >= 4.5 n = reader.number_of_output_ports outputs = [] for i in range(n): outputs.append(reader.get_output(i)) # FIXME: Only the first output goes through the assign # attribute filter. aa = self._assign_attribute aa.input = outputs[0] outputs[0] = aa.output self.update_data() self.outputs = outputs # FIXME: The output info is only based on the first output. self.output_info.datasets = [get_tvtk_dataset_name(outputs[0])] # Change our name on the tree view self.name = self._get_name() def _set_data_name(self, data_type, attr_type, value): if value is None: return reader_output = self.reader.output if len(value) == 0: # If the value is empty then we deactivate that attribute. d = getattr(reader_output, attr_type + '_data') method = getattr(d, 'set_active_%s'%data_type) method(None) self.data_changed = True return aa = self._assign_attribute data = None if attr_type == 'point': data = reader_output.point_data elif attr_type == 'cell': data = reader_output.cell_data method = getattr(data, 'set_active_%s'%data_type) method(value) aa.assign(value, data_type.upper(), attr_type.upper() +'_DATA') aa.update() # Fire an event, so the changes propagate. self.data_changed = True def _point_scalars_name_changed(self, value): self._set_data_name('scalars', 'point', value) def _point_vectors_name_changed(self, value): self._set_data_name('vectors', 'point', value) def _point_tensors_name_changed(self, value): self._set_data_name('tensors', 'point', value) def _cell_scalars_name_changed(self, value): self._set_data_name('scalars', 'cell', value) def _cell_vectors_name_changed(self, value): self._set_data_name('vectors', 'cell', value) def _cell_tensors_name_changed(self, value): self._set_data_name('tensors', 'cell', value) def _get_name(self): """ Gets the name to display on the tree view. """ fname = basename(self.file_path.get()) ret = "VTK XML file (%s)"%fname if len(self.file_list) > 1: ret += " (timeseries)" if '[Hidden]' in self.name: ret += ' [Hidden]' return ret mayavi-4.1.0/mayavi/sources/vtk_file_reader.py0000644000175100001440000000620411674464502022446 0ustar ischnellusers00000000000000"""A VTK file reader object. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import basename # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports. from mayavi.core.pipeline_info import (PipelineInfo, get_tvtk_dataset_name) from vtk_xml_file_reader import VTKXMLFileReader ######################################################################## # `VTKFileReader` class ######################################################################## class VTKFileReader(VTKXMLFileReader): """A VTK file reader. This does not handle the new XML file format but only the older format. The reader supports all the different types of data sets. This reader also supports a time series. """ # The version of this class. Used for persistence. __version__ = 0 # The VTK data file reader. reader = Instance(tvtk.DataSetReader, args=(), kw={'read_all_scalars':True, 'read_all_vectors': True, 'read_all_tensors': True, 'read_all_fields': True} ) # Information about what this object can produce. output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ###################################################################### # Non-public interface ###################################################################### def _file_path_changed(self, fpath): value = fpath.get() if len(value) == 0: self.name = 'No VTK file' return else: self.reader.file_name = value self.update() # Setup the outputs by resetting self.outputs. Changing # the outputs automatically fires a pipeline_changed # event. try: n = self.reader.number_of_outputs except AttributeError: # for VTK >= 4.5 n = self.reader.number_of_output_ports outputs = [] for i in range(n): outputs.append(self.reader.get_output(i)) self.outputs = outputs # FIXME: Only the first output goes through the assign # attribute filter. aa = self._assign_attribute aa.input = outputs[0] outputs[0] = aa.output self.update_data() self.outputs = outputs # FIXME: The output info is only based on the first output. self.output_info.datasets = [get_tvtk_dataset_name(outputs[0])] # Change our name on the tree view self.name = self._get_name() def _get_name(self): """ Gets the name to display on the tree view. """ fname = basename(self.file_path.get()) ret = "VTK file (%s)"%fname if len(self.file_list) > 1: ret += " (timeseries)" if '[Hidden]' in self.name: ret += ' [Hidden]' return ret mayavi-4.1.0/mayavi/sources/array_source.py0000644000175100001440000002234011674464502022016 0ustar ischnellusers00000000000000"""A simple source that allows one to view a suitably shaped numpy array as ImageData. This supports both scalar and vector data. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. import numpy # Enthought library imports from traits.api import Instance, Trait, Str, Bool, Button, DelegatesTo from traitsui.api import View, Group, Item from tvtk.api import tvtk from tvtk import array_handler # Local imports from mayavi.core.source import Source from mayavi.core.pipeline_info import PipelineInfo def _check_scalar_array(obj, name, value): """Validates a scalar array passed to the object.""" if value is None: return None arr = numpy.asarray(value) assert len(arr.shape) in [2,3], "Scalar array must be 2 or 3 dimensional" vd = obj.vector_data if vd is not None: assert vd.shape[:-1] == arr.shape, \ "Scalar array must match already set vector data.\n"\ "vector_data.shape = %s, given array shape = %s"%(vd.shape, arr.shape) return arr _check_scalar_array.info = 'a 2D or 3D numpy array' def _check_vector_array(obj, name, value): """Validates a vector array passed to the object.""" if value is None: return None arr = numpy.asarray(value) assert len(arr.shape) in [3,4], "Vector array must be 3 or 4 dimensional" assert arr.shape[-1] == 3, \ "The vectors must be three dimensional with `array.shape[-1] == 3`" sd = obj.scalar_data if sd is not None: assert arr.shape[:-1] == sd.shape, \ "Vector array must match already set scalar data.\n"\ "scalar_data.shape = %s, given array shape = %s"%(sd.shape, arr.shape) return arr _check_vector_array.info = 'a 3D or 4D numpy array with shape[-1] = 3' ###################################################################### # 'ArraySource' class. ###################################################################### class ArraySource(Source): """A simple source that allows one to view a suitably shaped numpy array as ImageData. This supports both scalar and vector data. """ # The scalar array data we manage. scalar_data = Trait(None, _check_scalar_array, rich_compare=False) # The name of our scalar array. scalar_name = Str('scalar') # The vector array data we manage. vector_data = Trait(None, _check_vector_array, rich_compare=False) # The name of our vector array. vector_name = Str('vector') # The spacing of the points in the array. spacing = DelegatesTo('change_information_filter', 'output_spacing', desc='the spacing between points in array') # The origin of the points in the array. origin = DelegatesTo('change_information_filter', 'output_origin', desc='the origin of the points in array') # Fire an event to update the spacing and origin. This # is here for backwards compatability. Firing this is no # longer needed. update_image_data = Button('Update spacing and origin') # The image data stored by this instance. image_data = Instance(tvtk.ImageData, (), allow_none=False) # Use an ImageChangeInformation filter to reliably set the # spacing and origin on the output change_information_filter = Instance(tvtk.ImageChangeInformation, args=(), kw={'output_spacing' : (1.0, 1.0, 1.0), 'output_origin' : (0.0, 0.0, 0.0)}) # Should we transpose the input data or not. Transposing is # necessary to make the numpy array compatible with the way VTK # needs it. However, transposing numpy arrays makes them # non-contiguous where the data is copied by VTK. Thus, when the # user explicitly requests that transpose_input_array is false # then we assume that the array has already been suitably # formatted by the user. transpose_input_array = Bool(True, desc='if input array should be transposed (if on VTK will copy the input data)') # Information about what this object can produce. output_info = PipelineInfo(datasets=['image_data']) # Our view. view = View(Group(Item(name='transpose_input_array'), Item(name='scalar_name'), Item(name='vector_name'), Item(name='spacing'), Item(name='origin'), show_labels=True) ) ###################################################################### # `object` interface. ###################################################################### def __init__(self, **traits): # Set the scalar and vector data at the end so we pop it here. sd = traits.pop('scalar_data', None) vd = traits.pop('vector_data', None) # Now set the other traits. super(ArraySource, self).__init__(**traits) self.change_information_filter.input = self.image_data # And finally set the scalar and vector data. if sd is not None: self.scalar_data = sd if vd is not None: self.vector_data = vd self.outputs = [ self.change_information_filter.output ] self.on_trait_change(self._information_changed, 'spacing,origin') def __get_pure_state__(self): d = super(ArraySource, self).__get_pure_state__() d.pop('image_data', None) return d ###################################################################### # ArraySource interface. ###################################################################### def update(self): """Call this function when you change the array data in-place.""" d = self.image_data d.modified() pd = d.point_data if self.scalar_data is not None: pd.scalars.modified() if self.vector_data is not None: pd.vectors.modified() self.data_changed = True ###################################################################### # Non-public interface. ###################################################################### def _image_data_changed(self, value): self.change_information_filter.input = value def _scalar_data_changed(self, data): img_data = self.image_data if data is None: img_data.point_data.scalars = None self.data_changed = True return dims = list(data.shape) if len(dims) == 2: dims.append(1) img_data.origin = tuple(self.origin) img_data.dimensions = tuple(dims) img_data.extent = 0, dims[0]-1, 0, dims[1]-1, 0, dims[2]-1 img_data.update_extent = 0, dims[0]-1, 0, dims[1]-1, 0, dims[2]-1 if self.transpose_input_array: img_data.point_data.scalars = numpy.ravel(numpy.transpose(data)) else: img_data.point_data.scalars = numpy.ravel(data) img_data.point_data.scalars.name = self.scalar_name # This is very important and if not done can lead to a segfault! typecode = data.dtype img_data.scalar_type = array_handler.get_vtk_array_type(typecode) img_data.update() # This sets up the extents correctly. img_data.update_traits() self.change_information_filter.update() # Now flush the mayavi pipeline. self.data_changed = True def _vector_data_changed(self, data): img_data = self.image_data if data is None: img_data.point_data.vectors = None self.data_changed = True return dims = list(data.shape) if len(dims) == 3: dims.insert(2, 1) data = numpy.reshape(data, dims) img_data.origin = tuple(self.origin) img_data.dimensions = tuple(dims[:-1]) img_data.extent = 0, dims[0]-1, 0, dims[1]-1, 0, dims[2]-1 img_data.update_extent = 0, dims[0]-1, 0, dims[1]-1, 0, dims[2]-1 sz = numpy.size(data) if self.transpose_input_array: data_t = numpy.transpose(data, (2, 1, 0, 3)) else: data_t = data img_data.point_data.vectors = numpy.reshape(data_t, (sz/3, 3)) img_data.point_data.vectors.name = self.vector_name img_data.update() # This sets up the extents correctly. img_data.update_traits() self.change_information_filter.update() # Now flush the mayavi pipeline. self.data_changed = True def _scalar_name_changed(self, value): if self.scalar_data is not None: self.image_data.point_data.scalars.name = value self.data_changed = True def _vector_name_changed(self, value): if self.vector_data is not None: self.image_data.point_data.vectors.name = value self.data_changed = True def _transpose_input_array_changed(self, value): if self.scalar_data is not None: self._scalar_data_changed(self.scalar_data) if self.vector_data is not None: self._vector_data_changed(self.vector_data) def _information_changed(self): self.data_changed = True mayavi-4.1.0/mayavi/sources/image_reader.py0000644000175100001440000001113611674464502021725 0ustar ischnellusers00000000000000"""An Image file reader object. """ # Author: KK Rai (kk.rai [at] iitb.ac.in) # R. Ambareesha (ambareesha [at] iitb.ac.in) # Chandrashekhar Kaushik # Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. from os.path import basename # Enthought library imports. from traits.api import Instance, Str, Dict from traitsui.api import View, Group, Item, Include from tvtk.api import tvtk # Local imports. from mayavi.core.file_data_source import FileDataSource from mayavi.core.pipeline_info import PipelineInfo ######################################################################## # `ImageReader` class ######################################################################## class ImageReader(FileDataSource): """A Image file reader. The reader supports all the different types of Image files. """ # The version of this class. Used for persistence. __version__ = 0 # The Image data file reader. reader = Instance(tvtk.Object, allow_none=False, record=True) # Information about what this object can produce. output_info = PipelineInfo(datasets=['image_data']) # Our view. view = View(Group(Include('time_step_group'), Item(name='base_file_name'), Item(name='reader', style='custom', resizable=True), show_labels=False), resizable=True) ###################################################################### # Private Traits _image_reader_dict = Dict(Str, Instance(tvtk.Object)) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): d = {'bmp':tvtk.BMPReader(), 'jpg':tvtk.JPEGReader(), 'png':tvtk.PNGReader(), 'pnm':tvtk.PNMReader(), 'dcm':tvtk.DICOMImageReader(), 'tiff':tvtk.TIFFReader(), 'ximg':tvtk.GESignaReader(), 'dem':tvtk.DEMReader(), 'mha':tvtk.MetaImageReader(), 'mhd':tvtk.MetaImageReader(), } # Account for pre 5.2 VTk versions, without MINC reader if hasattr(tvtk, 'MINCImageReader'): d['mnc'] = tvtk.MINCImageReader() d['jpeg'] = d['jpg'] self._image_reader_dict = d # Call parent class' init. super(ImageReader, self).__init__(**traits) def __set_pure_state__(self, state): # The reader has its own file_name which needs to be fixed. state.reader.file_name = state.file_path.abs_pth # Now call the parent class to setup everything. super(ImageReader, self).__set_pure_state__(state) ###################################################################### # `FileDataSource` interface ###################################################################### def update(self): self.reader.update() if len(self.file_path.get()) == 0: return self.render() ###################################################################### # Non-public interface ###################################################################### def _file_path_changed(self, fpath): value = fpath.get() if len(value) == 0: return # Extract the file extension splitname = value.strip().split('.') extension = splitname[-1].lower() # Select image reader based on file type old_reader = self.reader if self._image_reader_dict.has_key(extension): self.reader = self._image_reader_dict[extension] else: self.reader = tvtk.ImageReader() self.reader.file_name = value.strip() self.reader.update() self.reader.update_information() if old_reader is not None: old_reader.on_trait_change(self.render, remove=True) self.reader.on_trait_change(self.render) self.outputs = [self.reader.output] # Change our name on the tree view self.name = self._get_name() def _get_name(self): """ Returns the name to display on the tree view. Note that this is not a property getter. """ fname = basename(self.file_path.get()) ret = "%s"%fname if len(self.file_list) > 1: ret += " (timeseries)" if '[Hidden]' in self.name: ret += ' [Hidden]' return ret mayavi-4.1.0/mayavi/sources/volume_reader.py0000644000175100001440000000460111674464502022151 0ustar ischnellusers00000000000000"""A Volume file reader""" # Author: Suyog Dutt Jain # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Str from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports. from mayavi.core.source import Source from mayavi.core.pipeline_info import PipelineInfo ######################################################################## # `VolumeReader` class ######################################################################## class VolumeReader(Source): """A Volume reader. """ # The version of this class. Used for persistence. __version__ = 0 file_prefix = Str('', desc='File prefix for the volume files') # The VTK data file reader. reader = Instance(tvtk.Volume16Reader, args=(), allow_none=False, record=True) # Information about what this object can produce. output_info = PipelineInfo(datasets=['image_data']) ######################################## # View related code. # Our view. view = View(Group(Item(name='reader', style='custom', resizable=True), show_labels=False), resizable=True) ###################################################################### # `Source` interface ###################################################################### def __init__(self, file_prefix='', configure=True, **traits): super(VolumeReader, self).__init__(**traits) if configure: self.reader.edit_traits(kind='livemodal') self.file_prefix = self.reader.file_prefix def update(self): if len(self.file_prefix) == 0: return self.reader.update() self.render() ###################################################################### # Non-public interface ###################################################################### def _file_prefix_changed(self, value): if len(value) == 0: return else: self.reader.file_prefix = value self._update_reader_output() def _update_reader_output(self): self.reader.update() self.reader.update_information() self.reader.on_trait_change(self.render) self.outputs = [self.reader.output] self.data_changed = True mayavi-4.1.0/mayavi/sources/metadata.py0000644000175100001440000002432111674464502021101 0ustar ischnellusers00000000000000""" Metadata for all sources. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran Enthought, Inc. # License: BSD Style. # Local imports. from mayavi.core.metadata import SourceMetadata from mayavi.core.pipeline_info import PipelineInfo BASE = 'mayavi.sources' open_3ds = SourceMetadata( id = "3DSFile", class_name = BASE + ".three_ds_importer.ThreeDSImporter", tooltip = "Import a 3D Studio file", desc = "Import a 3D Studio file", help = "Import a 3D Studio file", menu_name = "&3D Studio file", extensions = ['3ds'], wildcard = '3D Studio files (*.3ds)|*.3ds', output_info = PipelineInfo(datasets=['none'], attribute_types=['any'], attributes=['any']) ) open_image = SourceMetadata( id = "ImageFile", class_name = BASE + ".image_reader.ImageReader", menu_name = "&Image file (PNG/JPG/BMP/PNM/TIFF/DEM/DCM/XIMG/MHA/MHD/MINC)", tooltip = "Import a PNG/JPG/BMP/PNM/TIFF/DCM/DEM/XIMG/MHA/MHD/MINC image", desc = "Import a PNG/JPG/BMP/PNM/TIFF/DCM/DEM/XIMG/MHA/MHD/MINC image", extensions = ['png', 'jpg', 'jpeg', 'bmp', 'pnm', 'tiff', 'dcm', 'dem', 'ximg', 'mha', 'mhd', 'mnc'], wildcard = 'PNG files (*.png)|*.png|'\ 'JPEG files (*.jpg)|*.jpg|'\ 'JPEG files (*.jpeg)|*.jpeg|'\ 'BMP files (*.bmp)|*.bmp|'\ 'PNM files (*.pnm)|*.pnm|'\ 'DCM files (*.dcm)|*.dcm|'\ 'DEM files (*.dem)|*.dem|'\ 'Meta mha files (*.mha)|*.mha|'\ 'Meta mhd files (*.mhd)|*.mhd|'\ 'MINC files (*.mnc)|*.mnc|'\ 'XIMG files (*.ximg)|*.ximg|'\ 'TIFF files (*.tiff)|*.tiff', output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) ) open_poly_data = SourceMetadata( id = "PolyDataFile", class_name = BASE + ".poly_data_reader.PolyDataReader", menu_name = "&PolyData file (STL/STLA/STLB/TXT/RAW/PLY/PDB/SLC/FACET\ /OBJ/BYU/XYZ/CUBE)", tooltip = "Import a STL/STLA/STLB/TXT/RAW/PLY/PDB/SLC/FACET/OBJ/\ BYU/XYZ/CUBE Poly Data", desc = "Import a STL/STLA/STLB/TXT/RAWPLY/PDB/SLC/FACET/OBJ/BYU/XYZ/\ CUBE Poly Data", extensions = ['stl', 'stla', 'stlb', 'txt', 'raw', 'ply', 'pdb', 'slc', 'facet', 'xyz', 'cube', 'obj', 'g'], wildcard = 'STL files (*.stl)|*.stl|'\ 'STLA files (*.stla)|*.stla|'\ 'STLB files (*.stlb)|*.stlb|'\ 'BYU files (*.g)|*.g|'\ 'TXT files (*.txt)|*.txt|'\ 'RAW files (*.raw)|*.raw|'\ 'PLY files (*.ply)|*.ply|'\ 'PDB files (*.pdb)|*.pdb|'\ 'SLC files (*.slc)|*.slc|'\ 'XYZ files (*.xyz)|*.xyz|'\ 'CUBE files (*.cube)|*.cube|'\ 'FACET files (*.facet)|*.facet|'\ 'OBJ files (*.obj)|*.obj', can_read_test = 'mayavi.sources.poly_data_reader:PolyDataReader.can_read', output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) open_ugrid_data = SourceMetadata( id = "VTKUnstructuredFile", class_name = BASE + ".unstructured_grid_reader.UnstructuredGridReader", menu_name = "&Unstrucured Grid fil (INP/NEU/EXII)", tooltip = "Open a Unstrucured Grid file", desc = "Open a Unstrucured Grid file", help = "Open a Unstrucured Grid file", extensions = ['inp', 'neu', 'exii'], wildcard = 'AVSUCD INP files (*.inp)|*.inp|'\ 'GAMBIT NEU (*.neu)|*.neu|'\ 'EXODUS EXII (*.exii)|*.exii', output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) open_plot3d = SourceMetadata( id = "PLOT3DFile", class_name = BASE + ".plot3d_reader.PLOT3DReader", menu_name = "&PLOT3D file", tooltip = "Open a PLOT3D data data", desc = "Open a PLOT3D data data", help = "Open a PLOT3D data data", extensions = ['xyz'], wildcard = 'PLOT3D files (*.xyz)|*.xyz', output_info = PipelineInfo(datasets=['structured_grid'], attribute_types=['any'], attributes=['any']) ) open_vrml = SourceMetadata( id = "VRMLFile", class_name = BASE + ".vrml_importer.VRMLImporter", menu_name = "V&RML2 file", tooltip = "Import a VRML2 data file", desc = "Import a VRML2 data file", help = "Import a VRML2 data file", extensions = ['wrl'], wildcard = 'VRML2 files (*.wrl)|*.wrl', output_info = PipelineInfo(datasets=['none'], attribute_types=['any'], attributes=['any']) ) open_vtk = SourceMetadata( id = "VTKFile", class_name = BASE + ".vtk_file_reader.VTKFileReader", menu_name = "&VTK file", tooltip = "Open a VTK data file", desc = "Open a VTK data file", help = "Open a VTK data file", extensions = ['vtk'], wildcard = 'VTK files (*.vtk)|*.vtk', output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) open_vtk_xml = SourceMetadata( id = "VTKXMLFile", class_name = BASE + ".vtk_xml_file_reader.VTKXMLFileReader", menu_name = "VTK &XML file", tooltip = "Open a VTK XML data file", desc = "Open a VTK XML data file", help = "Open a VTK XML data file", extensions = ['xml', 'vti', 'vtp', 'vtr', 'vts', 'vtu', 'pvti', 'pvtp', 'pvtr', 'pvts', 'pvtu'], wildcard = 'VTK XML files (*.xml)|*.xml|'\ 'Image Data (*.vti)|*.vti|'\ 'Poly Data (*.vtp)|*.vtp|'\ 'Rectilinear Grid (*.vtr)|*.vtr|'\ 'Structured Grid (*.vts)|*.vts|'\ 'Unstructured Grid (*.vtu)|*.vtu|'\ 'Parallel Image Data (*.pvti)|*.pvti|'\ 'Parallel Poly Data (*.pvtp)|*.pvtp|'\ 'Parallel Rectilinear Grid (*.pvtr)|*.pvtr|'\ 'Parallel Structured Grid (*.pvts)|*.pvts|'\ 'Parallel Unstructured Grid (*.pvtu)|*.pvtu', output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) parametric_surface = SourceMetadata( id = "ParametricSurfaceSource", class_name = BASE + ".parametric_surface.ParametricSurface", menu_name = "&Create Parametric surface source", tooltip = "Create a parametric surface source", desc = "Create a parametric surface source", help = "Create a parametric surface source", extensions = [], wildcard = '', output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) point_load = SourceMetadata( id = "PointLoadSource", class_name = BASE + ".point_load.PointLoad", menu_name = "Create Point &load source", tooltip = "Simulates a point load on a cube of data (for tensors)", desc = "Simulates a point load on a cube of data (for tensors)", help = "Simulates a point load on a cube of data (for tensors)", extensions = [], wildcard = '', output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) ) builtin_surface = SourceMetadata( id = "BuiltinSurfaceSource", class_name = BASE + ".builtin_surface.BuiltinSurface", menu_name = "Create built-in &surface", tooltip = "Create a vtk poly data source", desc = "Create a vtk poly data source", help = "Create a vtk poly data source", extensions = [], wildcard = '', output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) builtin_image = SourceMetadata( id = "BuiltinImageSource", class_name = BASE + ".builtin_image.BuiltinImage", menu_name = "Create built-in &image", tooltip = "Create a vtk image data source", desc = "Create a vtk image data source", help = "Create a vtk image data source", extensions = [], wildcard = '', output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) ) open_volume = SourceMetadata( id = "VolumeFile", class_name = BASE + ".volume_reader.VolumeReader", menu_name = "&Volume file", tooltip = "Open a Volume file", desc = "Open a Volume file", help = "Open a Volume file", extensions = [], wildcard = '', output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) ) open_chaco = SourceMetadata( id = "ChacoFile", class_name = BASE + ".chaco_reader.ChacoReader", menu_name = "&Chaco file", tooltip = "Open a Chaco file", desc = "Open a Chaco file", help = "Open a Chaco file", extensions = [], wildcard = '', output_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['any']) ) # Now collect all the sources for the mayavi registry. sources = [open_3ds, open_image, open_plot3d, open_vrml, open_vtk, open_vtk_xml, parametric_surface, point_load, builtin_surface, builtin_image, open_poly_data, open_ugrid_data, open_volume, open_chaco, ] mayavi-4.1.0/mayavi/sources/unstructured_grid_reader.py0000644000175100001440000001015111674464502024413 0ustar ischnellusers00000000000000"""A Unstructred Grid file reader object. """ # Author: R.Sreekanth # Suyog Dutt Jain # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. from os.path import basename # Enthought library imports. from traits.api import Instance, Str, Dict from traitsui.api import View, Group, Item, Include from tvtk.api import tvtk # Local imports. from mayavi.core.file_data_source import FileDataSource from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.common import error ######################################################################## # `UnstructuredGridReader` class ######################################################################## class UnstructuredGridReader(FileDataSource): # The version of this class. Used for persistence. __version__ = 0 # The UnstructuredGridAlgorithm data file reader. reader = Instance(tvtk.Object, allow_none=False, record=True) # Information about what this object can produce. output_info = PipelineInfo(datasets=['unstructured_grid']) ###################################################################### # Private Traits _reader_dict = Dict(Str, Instance(tvtk.Object)) # Our view. view = View(Group(Include('time_step_group'), Item(name='base_file_name'), Item(name='reader', style='custom', resizable=True), show_labels=False), resizable=True) ###################################################################### # `object` interface ###################################################################### def __set_pure_state__(self, state): # The reader has its own file_name which needs to be fixed. state.reader.file_name = state.file_path.abs_pth # Now call the parent class to setup everything. super(UnstructuredGridReader, self).__set_pure_state__(state) ###################################################################### # `FileDataSource` interface ###################################################################### def update(self): self.reader.update() if len(self.file_path.get()) == 0: return self.render() ###################################################################### # Non-public interface ###################################################################### def _file_path_changed(self, fpath): value = fpath.get() if len(value) == 0: return # Extract the file extension splitname = value.strip().split('.') extension = splitname[-1].lower() # Select UnstructuredGridreader based on file type old_reader = self.reader if self._reader_dict.has_key(extension): self.reader = self._reader_dict[extension] else: error('Invalid file extension for file: %s'%value) return self.reader.file_name = value.strip() self.reader.update() self.reader.update_information() if old_reader is not None: old_reader.on_trait_change(self.render, remove=True) self.reader.on_trait_change(self.render) old_outputs = self.outputs self.outputs = [self.reader.output] if self.outputs == old_outputs: self.data_changed = True # Change our name on the tree view self.name = self._get_name() def _get_name(self): """ Returns the name to display on the tree view. Note that this is not a property getter. """ fname = basename(self.file_path.get()) ret = "%s"%fname if len(self.file_list) > 1: ret += " (timeseries)" if '[Hidden]' in self.name: ret += ' [Hidden]' return ret def __reader_dict_default(self): """Default value for reader dict.""" rd = {'inp':tvtk.AVSucdReader(), 'neu':tvtk.GAMBITReader(), 'exii':tvtk.ExodusReader() } return rd mayavi-4.1.0/mayavi/sources/builtin_surface.py0000644000175100001440000001023511674464502022476 0ustar ischnellusers00000000000000""" A module that allows a user to create one of several standard VTK poly data sources. """ #Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Enum, Dict, Str from traitsui.api import View, Item, Group from tvtk.api import tvtk # Local imports from mayavi.core.source import Source from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `BuiltinSurface` class. ###################################################################### class BuiltinSurface(Source): # The version of this class. Used for persistence. __version__ = 0 # Flag to set the poly data type. source = Enum('arrow','cone','cube','cylinder','disk','earth','line', 'outline','plane','point', 'polygon','sphere', 'superquadric','textured sphere', 'glyph2d', desc='which poly data source to be used') # Define the trait 'data_source' whose value must be an instance of # type PolyData data_source = Instance(tvtk.PolyDataAlgorithm, allow_none=False, record=True) # Information about what this object can produce. output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) # Create the UI for the traits. view = View(Group(Item(name='source'), Item(name='data_source', style='custom', resizable=True), label='Surface Source', show_labels=False), resizable=True) ######################################## # Private traits. # A dictionary that maps the source names to instances of the # poly data sources. _source_dict = Dict(Str, Instance(tvtk.PolyDataAlgorithm, allow_none=False)) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): # Call parent class' init. super(BuiltinSurface, self).__init__(**traits) # Initialize the source to the default mode's instance from # the dictionary if needed. if 'source' not in traits: self._source_changed(self.source) def __set_pure_state__(self, state): self.source = state.source super(BuiltinSurface, self).__set_pure_state__(state) ###################################################################### # Non-public methods. ###################################################################### def _source_changed(self, value): """This method is invoked (automatically) when the `source` trait is changed. """ self.data_source = self._source_dict[self.source] def _data_source_changed(self, old, new): """This method is invoked (automatically) when the poly data source is changed .""" self.outputs = [self.data_source.output] if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) def __source_dict_default(self): """Default value for source dict.""" sd = {'arrow':tvtk.ArrowSource(), 'cone':tvtk.ConeSource(), 'cube':tvtk.CubeSource(), 'cylinder':tvtk.CylinderSource(), 'disk':tvtk.DiskSource(), 'earth':tvtk.EarthSource(), 'line':tvtk.LineSource(), 'outline':tvtk.OutlineSource(), 'plane':tvtk.PlaneSource(), 'point':tvtk.PointSource(), 'polygon':tvtk.RegularPolygonSource(), 'sphere':tvtk.SphereSource(), 'superquadric':tvtk.SuperquadricSource(), 'textured sphere':tvtk.TexturedSphereSource(), 'glyph2d': tvtk.GlyphSource2D()} return sd mayavi-4.1.0/mayavi/sources/__init__.py0000644000175100001440000000013211674464502021052 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/sources/point_load.py0000644000175100001440000000360111674464502021447 0ustar ischnellusers00000000000000"""A source object that computes stress tensors on a volume. The tensors are computed from the application of a point load on a semi-infinite domain. """ # Authors: KK Rai (kk.rai [at] iitb.ac.in) # R. Ambareesha (ambareesha [at] iitb.ac.in) # Prabhu Ramachandran (prabhu [at] aero.iitb.ac.in) # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.core.source import Source from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `PointLoad` class. ###################################################################### class PointLoad(Source): # The version of this class. Used for persistence. __version__ = 0 point_load = Instance(tvtk.PointLoad, args=(), allow_none=False, record=True) # Information about what this object can produce. output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) # Create the UI for the traits. view = View(Group(Item(name='point_load', style='custom', resizable=True), label='PointLoad', show_labels=False), resizable=True) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): # Call parent class' init. super(PointLoad, self).__init__(**traits) # Call render everytime source traits change. self.point_load.on_trait_change(self.render) # Setup the outputs. self.outputs = [self.point_load.output] mayavi-4.1.0/mayavi/core/0000755000175100001440000000000011674464502016212 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/core/pipeline_base.py0000644000175100001440000002037511674464502021372 0ustar ischnellusers00000000000000"""The base class for all objects in the MayaVi pipeline. This class basically abstracts out the common parts of the pipeline interface. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import List, Event, Bool, Instance # Local imports. from mayavi.core.base import Base from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `PipelineBase` class. ###################################################################### class PipelineBase(Base): """ Base class for all the Source, Filters and Modules in the pipeline. """ # The version of this class. Used for persistence. __version__ = 0 # A list of outputs for this object. outputs = List(record=False) # The actors generated by this object that will be rendered on the # scene. Changing this list while the actors are renderered *is* # safe and will do the right thing. actors = List(record=False) # The optional list of actors belonging to this object. These # will be added to the scene at an appropriate time. Changing # this list while the widgets are renderered *is* safe and will do # the right thing. widgets = List(record=False) # Information about what this object can consume. input_info = Instance(PipelineInfo) # Information about what this object can produce. output_info = Instance(PipelineInfo) ######################################## # Events. # This event is fired when the pipeline has changed. pipeline_changed = Event(record=False) # This event is fired when the data alone changes but the pipeline # outputs are the same. data_changed = Event(record=False) ################################################## # Private traits. ################################################## # Identifies if `actors` and `widgets` are added to the `scene` or # not. _actors_added = Bool(False) # Stores the state of the widgets prior to disabling them. _widget_state = List ###################################################################### # `object` interface. ###################################################################### def __get_pure_state__(self): d = super(PipelineBase, self).__get_pure_state__() # These are setup dynamically so we should not pickle them. for x in ('outputs', 'actors', 'widgets', '_actors_added', ): d.pop(x, None) return d ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. Note that when start is invoked, all the other information for the pipeline should be already set. """ if self.running: return # Add any actors. self.add_actors() # Call parent method to set the running state. super(PipelineBase, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return # Remove any of our actors from the scene. self.remove_actors() # Call parent method to set the running state. super(PipelineBase, self).stop() def render(self): """Invokes render on the scene, this in turn invokes Render on the VTK pipeline. """ s = self.scene if s is not None: s.render() elif self.running: # If there is no scene and we are running, we flush the # pipeline manually by calling update. for actor in self.actors: if hasattr(actor, 'mapper'): m = actor.mapper if m is not None: m.update() for widget in self.widgets: if hasattr(widget, 'input'): input = widget.input if input is not None: input.update() if hasattr(self, 'components'): for component in self.components: component.render() ###################################################################### # `PipelineBase` interface. ###################################################################### # Normally, you will not need to override the following methods # but you can if you really need to for whatever reason. def add_actors(self): """Adds `self.actors` to the scene. This is typically called when start is invoked. You should avoid calling this unless you know what you are doing. """ scene = self.scene if scene is not None: scene.add_actors(self.actors) scene.add_widgets(self.widgets) self._set_widget_visibility(self.widgets) self._actors_added = True def remove_actors(self): """Removes `self.actors` from the scene. This is typically called when stop is invoked. You should avoid calling this unless you know what you are doing. """ scene = self.scene if scene is not None: scene.remove_actors(self.actors) scene.remove_widgets(self.widgets) self._actors_added = False ###################################################################### # Non-public interface ###################################################################### def _outputs_changed(self, new): self.pipeline_changed = True def _outputs_items_changed(self, list_event): self.pipeline_changed = True def _actors_changed(self, old, new): if self._actors_added: self.scene.remove_actors(old) self.scene.add_actors(new) self.scene.render() def _actors_items_changed(self, list_event): if self._actors_added: self.scene.remove_actors(list_event.removed) self.scene.add_actors(list_event.added) self.scene.render() def _widgets_changed(self, old, new): self._handle_widgets_changed(old, new) def _widgets_items_changed(self, list_event): self._handle_widgets_changed(list_event.removed, list_event.added) def _handle_widgets_changed(self, removed, added): if self._actors_added: scene = self.scene scene.remove_widgets(removed) scene.add_widgets(added) self._set_widget_visibility(added) def _scene_changed(self, old_scene, new_scene): if self._actors_added: old_scene.remove_actors(self.actors) old_scene.remove_widgets(self.widgets) new_scene.add_actors(self.actors) new_scene.add_widgets(self.widgets) self._set_widget_visibility(self.widgets) def _backup_widget_state(self): # store the enabled trait of the widgets # in the _widget_state list state = [] for w in self.widgets: state.append(w.enabled) self._widget_state[:] = state def _restore_widget_state(self): if len(self._widget_state) != len(self.widgets): # someone has played with the widgets # we just enable all of them for w in self.widgets: w.enabled = True else: for i in range(len(self.widgets)): self.widgets[i].enabled = self._widget_state[i] def _visible_changed(self,value): if value: # restore the state of the widgets from the # backed up values. self._restore_widget_state() else: self._backup_widget_state() # disable all the widgets self._set_widget_visibility(self.widgets) # hide all actors for a in self.actors: a.visibility = value self.render() super(PipelineBase , self)._visible_changed(value) def _set_widget_visibility(self, widgets): if not self.visible: for widget in widgets: widget.enabled = False mayavi-4.1.0/mayavi/core/off_screen_engine.py0000644000175100001440000000174711674464502022233 0ustar ischnellusers00000000000000""" An off-screen mayavi engine. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. from traits.api import Callable, Str from tvtk.pyface.tvtk_scene import TVTKWindow from mayavi.core.engine import Engine from mayavi.preferences.api import set_scene_preferences def off_screen_viewer_factory(size=(400, 350)): """A factory that creates an offscreen viewer.""" win = TVTKWindow(off_screen_rendering=True) # Set all preferences. set_scene_preferences(win.scene) # Set the size. win.scene.set_size(size) return win ################################################################################ # `OffScreenEngine` class. ################################################################################ class OffScreenEngine(Engine): # Overriding the scene factory trait of Engine. scene_factory = Callable(off_screen_viewer_factory) # Our name. name = Str('Mayavi offscreen Engine') mayavi-4.1.0/mayavi/core/scene.py0000644000175100001440000001701211674464502017662 0ustar ischnellusers00000000000000"""A scene object manages a TVTK scene and objects in it. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import List, Str, Instance from traitsui.api import View, Group, Item from apptools.persistence.state_pickler import set_state # Local imports. from tvtk.pyface.tvtk_scene import TVTKScene from mayavi.core.base import Base from mayavi.core.source import Source from mayavi.core.common import handle_children_state, exception from mayavi.core.adder_node import SourceAdderNode ###################################################################### # `Scene` class. ###################################################################### class Scene(Base): """ The Mayavi scene class. """ # The version of this class. Used for persistence. __version__ = 0 # The scene (RenderWindow) associated with this component -- we # redeclare it here just to be able to record this scene, we don't # want it recorded on all objects since the scene is shared # (although it isn't an error to register an object twice with the # recorder). scene = Instance(TVTKScene, record=True) # The source objects associated with this object. children = List(Source, record=True) # The name of this scene. name = Str('TVTK Scene') # The icon icon = Str('scene.ico') # The human-readable type for this object type = Str(' scene') # The objects view. view = View(Group(Item(name='scene', style='custom'), show_labels=False) ) # The adder node dialog class _adder_node_class = SourceAdderNode # The dispatch, to register callbacks on mouse pick _mouse_pick_dispatcher = Instance( 'mayavi.core.mouse_pick_dispatcher.MousePickDispatcher', record=False) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): # Base removes the scene, but we need to save it! d = super(Scene, self).__get_pure_state__() d['scene'] = self.scene d.pop('_mouse_pick_dispatcher', None) return d def __set_pure_state__(self, state): handle_children_state(self.children, state.children) # Now set our complete state. Doing the scene last ensures # that the camera view is set right. set_state(self, state, last=['scene']) ###################################################################### # `Scene` interface ###################################################################### def on_mouse_pick(self, callback, type='point', button='Left', remove=False): """ Add a picking callback on mouse click. When the mouse button is press, object picking is called, and the given callback is invoked with the resulting pick as an argument. **Keyword arguments** :type: 'point', 'cell', or 'world' The picker type used for picking. :button: 'Left', 'Middle', or 'Right' The mouse button triggering the picking event. :remove: boolean If remove is True, the callback is removed from the list of callbacks. **Returns** picker: a tvtk picker The picker that will be used to do the picking. **Notes** The callback must accept one argument: the TVTK picker. The same callback can be added multiple times. """ key = (callback, type, button) if remove: self._mouse_pick_dispatcher.callbacks.remove(key) else: self._mouse_pick_dispatcher.callbacks.append(key) return self._mouse_pick_dispatcher._active_pickers[type] ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. """ # Do nothing if we are already running. if self.running: return # Start all our children. for obj in self.children: obj.start() # Disallow the hide action in the context menu self._HideShowAction.enabled = False super(Scene, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return # Disable rendering to accelerate shutting down. scene = self.scene if scene is not None: status = scene.disable_render scene.disable_render = True try: # Stop all our children. for obj in self.children: obj.stop() finally: # Re-enable rendering. if scene is not None: scene.disable_render = status super(Scene, self).stop() def add_child(self, child): """This method intelligently adds a child to this object in the MayaVi pipeline. """ self.children.append(child) def remove_child(self, child): """Remove specified child from our children. """ self.children.remove(child) def remove(self): """Remove ourselves from the mayavi pipeline. """ if self.parent is not None: self.stop() self.parent.close_scene(self) ###################################################################### # `TreeNodeObject` interface ###################################################################### def tno_can_add(self, node, add_object): """ Returns whether a given object is droppable on the node. """ try: if issubclass(add_object, Source): return True except TypeError: if isinstance(add_object, Source): return True return False def tno_drop_object(self, node, dropped_object): """ Returns a droppable version of a specified object. """ if isinstance(dropped_object, Source): return dropped_object ###################################################################### # Non-public interface ###################################################################### def _children_changed(self, old, new): self._handle_children(old, new) def _children_items_changed(self, list_event): self._handle_children(list_event.removed, list_event.added) def _handle_children(self, removed, added): for obj in removed: obj.stop() for obj in added: obj.set(scene=self.scene, parent=self) if self.running: # It makes sense to start children only if we are running. # If not, the children will be started when we start. try: obj.start() except: exception() def _menu_helper_default(self): from mayavi.core.traits_menu import SourceMenuHelper return SourceMenuHelper(object=self) def __mouse_pick_dispatcher_default(self): from mayavi.core.mouse_pick_dispatcher import \ MousePickDispatcher return MousePickDispatcher(scene=self) mayavi-4.1.0/mayavi/core/component.py0000644000175100001440000001223311674464502020567 0ustar ischnellusers00000000000000"""The base class for all MayaVi components. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import List # Local imports. from mayavi.core.pipeline_base import PipelineBase ###################################################################### # `Component` class. ###################################################################### class Component(PipelineBase): # The version of this class. Used for persistence. __version__ = 0 # A list of inputs for this component. inputs = List(record=False) # A list of sources for this component. sources = List(record=False) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): super(Component, self).__init__(**traits) # Let the filter setup its pipeline. self.setup_pipeline() def __get_pure_state__(self): d = super(Component, self).__get_pure_state__() # Remove dynamically set things. for x in ['inputs', 'sources']: d.pop(x, None) return d ###################################################################### # `Component` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* its tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. """ pass def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ raise NotImplementedError def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Invoke render to update any changes. self.render() # Propagate the data_changed event. self.data_changed = True ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. Note that when start is invoked, all the other information for the pipeline should be already set. """ # Do nothing if we are already running. if self.running: return # Setup event handlers. self._setup_event_handlers() # Update the pipeline. self.update_pipeline() # Call parent method to set the state. super(Component, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return # Teardown event handlers. self._teardown_event_handlers() # Call parent method to set the state. super(Component, self).stop() ###################################################################### # Non-public interface ###################################################################### def _inputs_changed(self, old, new): if self.running: self.update_pipeline() self._setup_events(old, new) def _inputs_items_changed(self, list_event): if self.running: self.update_pipeline() self._setup_events(list_event.removed, list_event.added) def _sources_changed(self, old, new): if self.running: self.update_pipeline() self._setup_events(old, new) def _sources_items_changed(self, list_event): if self.running: self.update_pipeline() self._setup_events(list_event.removed, list_event.added) def _setup_event_handlers(self): self._setup_events([], self.inputs) self._setup_events([], self.sources) def _teardown_event_handlers(self): self._setup_events(self.inputs, []) self._setup_events(self.sources, []) def _setup_events(self, removed, added): for object in removed: object.on_trait_event(self.update_pipeline, 'pipeline_changed', remove=True) object.on_trait_event(self.update_data, 'data_changed', remove=True) for object in added: object.on_trait_event(self.update_pipeline, 'pipeline_changed') object.on_trait_event(self.update_data, 'data_changed') mayavi-4.1.0/mayavi/core/common.py0000644000175100001440000001076011674464502020060 0ustar ischnellusers00000000000000"""Common utility functions and classes. This includes error/warning messages etc. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. import sys import traceback import logging # Enthought library imports. from apptools.persistence.state_pickler import create_instance from traits.etsconfig.api import ETSConfig if ETSConfig.toolkit in ('null', ''): pyface = None else: from pyface import api as pyface # Setup a logger for this module. logger = logging.getLogger(__name__) ###################################################################### # Utility functions. ###################################################################### def debug(msg): """Handle a debug message. """ logger.debug(msg) def warning(msg, parent=None): """Handle a warning message. """ logger.warn(msg) if pyface is not None: pyface.warning(parent, msg) def error(msg, parent=None): """Handle an error message. """ logger.error(msg) if pyface is not None: pyface.error(parent, msg) def exception(msg='Exception', parent=None): """This function handles any exception derived from Exception and prints out an error. The optional `parent` argument is passed along to the dialog box. The optional `msg` is printed and sent to the logger. So you could send extra information here. """ try: type, value, tb = sys.exc_info() info = traceback.extract_tb(tb) filename, lineno, function, text = info[-1] # last line only exc_msg = "%s\nIn %s:%d\n%s: %s (in %s)" %\ (msg, filename, lineno, type.__name__, str(value), function) # Log and display the message. logger.exception(msg) if pyface is not None: pyface.error(parent, exc_msg, title='Exception') finally: type = value = tb = None # clean up def process_ui_events(): """Process GUI events. This function merely abstracts the function so nothing is done when no UI is running. """ if pyface is not None: pyface.GUI.process_events() def get_engine(obj): """Try and return the engine given an object in the mayavi pipeline. This basically walks up the parent's of the object till the engine is found. """ from mayavi.core.engine import Engine while obj is not None: if isinstance(obj, Engine): return obj else: obj = obj.parent return None def get_object_path(object, parent, path='engine'): """Given a mayavi object on the tree view, this should find its "path" with respect to the parent object that contains it. """ def _get_child_trait(obj): if hasattr(obj, 'scenes'): return 'scenes' elif hasattr(obj, 'children'): return 'children' return '' def _finder(obj, to_find, path): if obj is to_find: return path else: child_t = _get_child_trait(obj) if child_t == '': return '' for i, o in enumerate(getattr(obj, child_t)): pth = _finder(o, to_find, '%s.%s[%d]'%(path, child_t, i)) if len(pth) > 0: return pth return '' return _finder(parent, object, path) def handle_children_state(children, kids): """Given a list of children (as `children`) of a particular object and their states in the `kids` argument, this function sets up the children by removing unnecessary ones, fixing existing ones and adding new children if necessary (depending on the state). """ # Make a copy of the list so adding/removing does not trigger events # each time. m_children = list(children) n_child, n_kid = len(m_children), len(kids) # Remove extra children we have. for i in range(n_child - n_kid): m_children.pop() # Now check existing children deleting existing ones and # creating new ones if needed. for i in range(n_child): child, kid = m_children[i], kids[i] md = kid.__metadata__ if (child.__module__ != md['module']) \ or (child.__class__.__name__ != md['class_name']): m_children[i] = create_instance(kid) # Add any extra kids. for i in range(n_kid - n_child): child = create_instance(kids[n_child + i]) m_children.append(child) # Now set the children in one shot. children[:] = m_children mayavi-4.1.0/mayavi/core/ui/0000755000175100001440000000000011674464502016627 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/core/ui/images/0000755000175100001440000000000011674464502020074 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/core/ui/images/preferences.png0000644000175100001440000000677411674464502023121 0ustar ischnellusers00000000000000PNG  IHDRa pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-gAMA|Q cHRMz%u0`:o_FIDATxtRklS=vcJ9`lE$c 0BL`LH8(FP!"ujha#1:bq׽sKN89_H7nޱ5$Vl'y}O~uyw7*LBM]dK'ޘΟ}o9'r{u$,R첈w)+/yjNz@Kĝt tk=w8H|DqBsZ'F$byAa,kHݛO n}6g^H0P 2plp#PaOzYg4vMŻAWϿ1>4˔3\ۂ7';jDϡ~Py50QRIȅ o*C= Z[u-%f|ұ=b52S;\G!sIipH j6TQސņ'2(.iFBAOHc)5ì5ǧJ[@*5bF`@%VkRRPC|э:SP4, $ugPU'.`yОڕHD*.6q-=P"xS2s:P^w'ʏ*WKQĨȣ~H?LB(">ք%s@ o4|m |ųN'd҉WY t *`jO?#A=#NGYIENDB`mayavi-4.1.0/mayavi/core/ui/images/add.ico0000644000175100001440000000217611674464502021326 0ustar ischnellusers00000000000000h(  3f$IEmYVDl]GGV_GGYbGF[fȣʤȣȣȣȣ¥¥=d@h@h~/u>>;a;b,x"%㴖~%]=OMsJ:uW|̂z9 [w^3m(B6eiveEт(FP$OdF{N K܈Qn*V3t^ B$׌aٺpȫzt 5<38ێ,CkHga;HSq%W˞qW^cU17:]`h4M+Q0Pq4ff9X MnZ:DøT 'qkdE3 1V2O櫛W!Pv!Sj= RD bT5-Oܨ,maWYQqn2ҍXhv/e7ԈuvÒf@S,rff~]ˍICc֞ v=4̩"=>:3/IƼYYYP:PSM[jdW\TaZRvg`E^WO4 #KICf~v^ncV/"kX3rnLiƃD0'QRJ}qqtD_[m~WhnǺ?x~tnƬiL^dn]*/$j_m]UD8jatWucZhHKdHpKW?fa9xl`]oLh{FheCXxQ[~V| x?(  LW=i& )+egzW e[OKT9}azp]+iY:\lLaWgq@%$OkF\[AL2(?(, AQ9,d~N_|Wjh|Woc-jie{cjsLrMmδoվp`zAbaAe0lstub97*Y$_h[w|nZG;LxrUQGEN=3T=;5,F9 W]U¹v9E@gy6 . &!+9)"E:1LA8TKBc]Uxjcc?'+ # (1 9 AIQaF/o^C²Gws, %"Yb\ye\L>c0uOjuBf}UǾACXR0/ *C:3uqlr^(gzE]c`Vk{q3 1 xztpl˰lxFbR]c]cmku}RPF9 C*!}urnҺlhzF]b]c^[CRF5oԾspC@j{uroԾols@bX_bbYfomδp`~oHM0%orqonniy_dG_iGRfG& btjŦmɯU*Ob[QazgfiZ \D457&;a{emLgJ&C;+!JS7^ki~Kq_)~t[MW:__f~IgoLEW9j\`hzZ??(0 b{Rd{PdlO?SfGlk6mjjĥ_p`\iFtUlmжoԼl{SU;nm?hnqrsp;cC)Z6(tvy{u\^PCOmr@MKBKM0%sN-"@[f]sWTK< 5 QQIlƽź_vC(1 *#"(0 <L8.TH>`XMxiaBSN9s/ ( "+ 5 9@GNW gT5r_DAnNJE80)#"jzn`pb]5 l>q\&fMjxSs]ld, )X_XwsoԽpsHnf0cO]cdSCXQDzK@71 0 }uqnѹlir=_[]c]cgu}{:7 SSJwspmͲkoeK]c]c]]isv^zn?= y~xtqnҹmiu@_[]d_Z9E-@OBIn϶psL&DTSH~|xtqlhxmxPjyMc[IaDpcjkȫnзfLK e_WbmhkƨZ>.R^N@`sehs`( ^;&93)01!1`uc}qY'lI}p`GO4~]gfRpb+kgGIQ5^`dMbpH>K1F\_a{W???( @ `|Td{KXmImOdEhwBmi3hfcnR?\pKnf0qn(U\[Mux{}~~e?[L?QM ufHF=MITH=yS9.E@h~ƼURNFB= : }o--(J.%: 5 3 HB;\ler}^vpKb]ou7 2 , '# !%*0 5 C(OA8WOEa[RφvqrޏND<0 *%%*0 5 ; AFLQ \B0eTA멒/ *$!- TXPXaYV[RUUKUPEM&MS\ h7s\)hd?{iUunTXO*&#"PRKyk`hXa7l>tUlk5dN^XSoq, )'7$xtqnиnnDq\&ir=bS]c]_`&1-y^nf/ - , p}vspnзlnh3gzE_\]c]ce{JQvmxD/&3 1 8ytroֿmͳj¢kn9cO]c]c]c^Xhu{u: 7 6 czq{usqnӺlʮk~gyC`Y]c]c]cdxV+4.oоsv{XbW=< ?|vtroֿmδmjs=cO]c]c]`iz[Pn`prtrFB@a}s~zvtrpnp_ml7fJ`ad^@R8VdnѷpqbHFI lqqpnmkjifzbjQkkI[eDRaD7B0"hlʮnзoֿU9+LJ\UK:F7_hjäkɬfRPU7* _q[fgi`H1WVg^S]y[dehie1a) bUA\_a{d}rT!nBgW5 \d_pj}Jq[%oa.83 %]d`\it>ko:xr\]c`XbPjoRYX\Z?O4B????(0` $boW_~R^rFZhGmpcw1>+LdPgxCko9hzeXrX}^]wThwBkm7og2hhidfo\byOlk6oa+ptCi¢jĥkƨkȫkçXoYdsJp`*sVoTkǪlʮḻmδmжnѸ[shoGtUrJmlFmͲmжnҹoԼoֿpp\zCcf@sJl>gS2nҺoվppqqrrYuBSS8m?f3`+nɳqqrssttt^((8g8`( Y dorstuuuvvv\{bF,Z UV/"tuvxyz{{{z^ti^P?UROjxz|~wIE;SOLUH<}lD U0$MJGf[{RNCKGDC {ź`xt771QI EB?J4*~jSE:C@< 9 YaWsEHJAA=: 7 4 ]kce UQG< 8 5 1 . 1ORKWc\_rkfzmty\lgG^X< 6 3 / , (%#!"#&), 0 3 8E,#MB8SMEXUNygcoasj5 1 - *&# "%(, 0 3 7 ; >BFJ S4'UC6ZPD|v#!;0 , (%!!%(, 0 4 7 ; ?BFJNQU^)cP3d\D{rdQpjl/ + ($ "&)477 8 < @CGKNRW_& g6pEsZ%flBrl]q=( + ($!!' Zd]znbwZOBTY a*i9rIsX!mg1hxB^yOmra)305w, )&#""$z~yusoϻfleA"l>tNq\&lk5gzEbT]d`nLb„- *(&%&dun}xusqoֿmγnyRuRoa+jp:e~I`Y]c]c]yPlŻ/ , *))1|wtsqoվmϵkɬqrAnf0hu@cO^^]c]c]cdsJ'409zm1 . - , , dwpzvtrpoӻmͳkǪlvll6gzEaU]c]c]c]c]_\|z~RSJ3 1 0 / 2}xusqpnѹl̰jƧj~ir=dL_[]c]c]c]c]cx_gw{95 4 3 2 XaX~zvtrqoվmϵlʭjĥjggyCbR]a]c]c]c]c\_'3-= =|ɾ~{xutrqpnҹlͱlmwEit>dL_[]c]c\_WrP &1)@nϸprstuYdXCB@@X`V}zwutsqpnӻnqSod/ko9f|Ga[]cYZ6G1`\zlnзoվprsrIFDDD u~~|zwtqonȺkid_~fbkKjjChqLXgENaC2?,Z`ḻnѷoվpqcJHGGTLB,;7U'41G'%9,  fkȫḻnзoԼpU9+LKJN$33,@-8-fhjäkǪl˰mϵfQONM]VMVkXghiãkƨlʮZ>.SRQV;.Zz`fghig~Z WUV"?:0p\jdeghdF'`( ]# [! YPC !axcdei}Ui9g4d0`P82:(`va{bfusNpEm@gT0 EU:_n`taymo;sVuQqX'xknSiH]g^ldZnf0pa*p`*gcJUhG]d_^gyCjq;ll6]cAShH^abTdLf}HZhCE_?]c^^_X[hJ. WWZV:J0^????( ;L68lWh|d69)`cT~rXKAw~k23"W[RVPEmaBX/7/rB$snm9aYgvcfZ[OnlhzZ`aDT87]sPmi;IB/7V}SkxY??mayavi-4.1.0/mayavi/core/ui/images/add_scene.png0000644000175100001440000000104411674464502022506 0ustar ischnellusers00000000000000PNG  IHDRasRGBbKGD pHYs  tIME 0SIDAT8˥?HQ?.GN)Rl*Xpp&qNMZ\B$:6`pi ApIqU&! : wzbQt=mL}9h$> @ I:' VАR 1Q*%DtUA V"aAmq+Xݳsj֬ݡ+%b?5\ 8dAag 9^ q @R\)sD AM,g7% G7aO38fw{FVXZ^T[^>e}&@::I3д>Wh ?XZM}}N繁f Nj\a=t:-k+yĿ"sA ]yl;9IENDB`mayavi-4.1.0/mayavi/core/ui/images/help-action.png0000644000175100001440000000162511674464502023011 0ustar ischnellusers00000000000000PNG  IHDRasBIT|d pHYsvv}ՂtEXtSoftwarewww.inkscape.org<IDAT8eML\Us3L#2TUc+jsa(+]]"*]&D7.\ш`Re w={ t闼y6(8:c٦d2!n^VJ;ݛ.G'&>/z (T.yАT%Ո"KҭKF;CE]J)S(vHJKC^!h"=p{\.WT)₈DkɅX4g]yZ]w??AF$hiiN.2Ý׳BɛVk"^,.3ҸgfˬT_Xm{#LPs̶D,/Nst3ܮccr)!cZ/Tx($phW{^(Qne=f+rrSSkjbbJLOйyIR8Tu""sW6_ym󑷹ܤ[Ԩ&TUM z>j罎c6bJ0gN5(6aeS1ጪ_~="QU~sb.D- r;ghn˘ K}]GA§gd~0i[l0o1on\Mf }Z=u_tw PVUy)IUNK[-h=!6x):@26BAle hgVgMx_XvG5MTW1 \D"~zyBKt+l_ V%>1IENDB`mayavi-4.1.0/mayavi/core/ui/images/image_LICENSE.txt0000644000175100001440000000150211674464502023057 0ustar ischnellusers00000000000000The icons are mostly derived work from other icons. As such they are licensed accordingly to the original license: Eclipse: Eclipse Public Licence Nuvola: LGPL Crystal: LGPL OOo: LGPL GV: Gael Varoquaux: BSD-licensed Unless stated in this file, icons are work of enthought, and are released under a 3 clause BSD-like license. Files and orginal authors: ---------------------------------------------------------------- add_filter.png | Nuvola add_module.png | Crystal add_scene.png | Crystal add_source.png | Crystal add.ico | GV help_action.png | GV m2.png | GV mv2.ico | GV preferences.png | Crystal mayavi-4.1.0/mayavi/core/ui/images/reader.png0000644000175100001440000000167211674464502022052 0ustar ischnellusers00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<LIDATxb?%  ~ɠ(3?~?QAVjG:1uf@ X@_@C20gd``'"K9ӫ/>?t  <4S~]M$5oBf9]2yKvivbBА:("L?xl#t42x|X #Ta( l T"gv1_TE3Q6 Oǐa###ݧ)|7?~ gˠ'!*@@E߿?~koh훫'z/_@'- ߯~A Rgcjo,G+H %  Ѡ}Aq"+_c ҦrOKs1 gdt~1>2 ث@ͻA^ w~o|)7Ms'8`1M {NOa81uE bfdg,lAڇeu5Xĸظ1z- gn`8=wow$݃>@DO7?OXߨGv@e^#rR'V? ^ӛ]+r#@!+ 0Js.dbg 1El@ -kIENDB`mayavi-4.1.0/mayavi/core/ui/api.py0000644000175100001440000000046211674464502017754 0ustar ischnellusers00000000000000from mayavi.tools.mlab_scene_model import MlabSceneModel from mayavi.core.ui.mayavi_scene import MayaviScene, \ DecoratedScene from tvtk.pyface.scene_editor import SceneEditor from mayavi.core.ui.engine_view import EngineView from mayavi.core.ui.engine_rich_view import EngineRichView mayavi-4.1.0/mayavi/core/ui/module_manager.py0000644000175100001440000000214711674464502022164 0ustar ischnellusers00000000000000""" Traits View definition file. The view trait of the parent class is extracted from the model definition file. This file can either be exec()ed or imported. See core/base.py:Base.trait_view() for what is currently used. Using exec() allows view changes without needing to restart Mayavi, but is slower than importing. """ # Authors: Prabhu Ramachandran # Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. from traitsui.api import Item, Group, View, EnumEditor from mayavi.core.module_manager import LUT_DATA_MODE_TYPES view = View(Group(Item('scalar_lut_manager', style='custom'), label='Scalar LUT', show_labels=False, selected=True), Group(Item('vector_lut_manager', style='custom'), label='Vector LUT', show_labels=False), Group(Item('lut_data_mode', style='custom', editor = EnumEditor(values=LUT_DATA_MODE_TYPES)), label='ModuleManager', selected=False), ) mayavi-4.1.0/mayavi/core/ui/engine_rich_view.py0000644000175100001440000001461711674464502022516 0ustar ischnellusers00000000000000"""A view of the pipeline with a panel to edit objects. """ # Author: Gael Varoquaux # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traitsui.api import Item, View, HSplit, InstanceEditor from traitsui.menu import Action, Separator from pyface.image_resource import ImageResource from pyface.api import GUI from mayavi.core.adder_node import SceneAdderNode # Local imports. from mayavi.core.scene import Scene from mayavi.preferences.preference_manager_view import \ preference_manager_view from mayavi.core.ui.engine_view import EngineView, \ EngineViewHandler class EngineRichViewHandler(EngineViewHandler): """ A handler for the EngineRichView object. """ def init_info(self, info): """ Informs the handler what the UIInfo object for a View will be. Overridden here to add a callback on the creation of the view. """ super(EngineRichViewHandler, self).init_info(info) info.on_trait_change(self.select_selected, 'initialized') return def select_selected(self, initialized): """ Force the tree editor to select the current engine selection, and eventually collapse other scenes. """ # We need to explore the editors to find the one we are # interested in, and to switch its selection to None, and then # back to what we are interested in. editors = self.info.ui._editors if editors is not None: for editor in editors: if editor.factory is self.info.object.tree_editor: tree_editor = editor break else: return else: return # We switch the selection to None, but we avoid # trait callback, to avoid changing the engine's # current_selection. tree_editor.set(selected=None, trait_change_notify=False) current_selection = self.info.object.engine.current_selection GUI.set_trait_later(tree_editor, 'selected', current_selection) # If we are selecting a scene, collapse the others if isinstance(current_selection, Scene) and \ hasattr(tree_editor._tree, 'Collapse'): # The wx editor can collapse, dunno for the Qt for scene in self.info.object.engine.scenes: if scene is not current_selection: tree_editor._tree.Collapse( tree_editor._get_object_nid(scene)) def _on_dclick(self, object): """ Called when a node in the tree editor is double-clicked. """ if isinstance(object, SceneAdderNode): self.info.object._perform_new_scene() else: # In this view, we want the dialogs not to be modals, so that # the EngineRichView window can be closed while leaving # objects dialogs open. object.edit_traits(view=object.dialog_view()) ############################################################################## # EngineRichView class. ############################################################################## class EngineRichView(EngineView): """ A view displaying the engine's object tree, alongside with a panel to edit the objects. """ ########################################################################### # `HasTraits` interface. ########################################################################### def default_traits_view(self): """The default traits view of the Engine View. """ view = View(HSplit( Item('engine', id='mayavi.engine_rich_view.pipeline_view', springy=True, resizable=True, editor=self.tree_editor, dock='tab', label='Pipeline'), Item('engine', id='mayavi.engine_rich_view.current_selection', editor=InstanceEditor( view='current_selection_view'), springy=True, resizable=True, style='custom'), show_labels=False, id='mayavi.engine_rich_view_group', ), id='mayavi.engine_rich_view', help=False, resizable=True, undo=False, revert=False, ok=False, cancel=False, title='Mayavi pipeline', icon=self.icon, toolbar=self.toolbar, handler=EngineRichViewHandler) return view def _actions_default(self): """ Append a preferences action to the toolbar: this view of the engine is meant to be a powerful view giving access to all of Mayavi's functionality. """ preferences_action = \ Action( image=ImageResource('preferences.png', search_path=self._image_path), tooltip="Modify Mayavi's preferences", checked=False, defined_when='True', perform=preference_manager_view.dialog_view, ) actions = super(EngineRichView, self)._actions_default() actions.extend((Separator(), preferences_action)) return actions ########################################################################### # EngineRichView interface. ########################################################################### def scene_editing_view(self, scene): # Selecting an object if good, because it forces the HSplit to # choose a sensible split ratio for mayavi_scene in self.engine.scenes: sc = mayavi_scene.scene # Support for the `MlabSceneModel` where the `scene_editor` # trait contains the scene. s = getattr(sc, 'scene_editor', sc) if s is scene: self.engine.current_selection = mayavi_scene return self.edit_traits() ### EOF ###################################################################### mayavi-4.1.0/mayavi/core/ui/engine_view.py0000644000175100001440000002706211674464502021507 0ustar ischnellusers00000000000000"""The MayaVi view in Envisage. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2009, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import join # Enthought library imports. from traits.api import Instance, HasTraits, Any, Delegate, \ List, Either from traitsui.api import (Item, TreeEditor, TreeNode, ObjectTreeNode, View, Handler, UIInfo) from traitsui.menu import ToolBar, Action, Separator from pyface.resource.resource_path import resource_path from pyface.image_resource import ImageResource from apptools.scripting.api import start_recording, stop_recording # Local imports. from mayavi.core.engine import Engine from mayavi.core.base import Base from mayavi.core.adder_node import ModuleFilterAdderNode, \ SourceAdderNode, ModuleAdderNode, FilterAdderNode, \ SceneAdderNode, AdderNode from mayavi.action.help import open_help_index, open_tvtk_docs class EngineViewHandler(Handler): """ A handler for the EngineView object. """ info = Instance(UIInfo) def init_info(self, info): """ Informs the handler what the UIInfo object for a View will be. Overridden here to save a reference to the info object. """ self.info = info return def _on_dclick(self, object): """ Called when a node in the tree editor is double-clicked. """ if isinstance(object, SceneAdderNode): self.info.object._perform_new_scene() else: object.edit_traits(view=object.dialog_view(), parent=self.info.ui.control) def _on_select(self, object): """ Called when a node in the tree editor is selected. """ self.info.object.engine._on_select(object) class AdderTreeNode(TreeNode): """ TreeNode for the adder nodes. """ children='' label='label' auto_open=True copy=False delete_me=False rename_me=False tooltip='tooltip' icon_path=resource_path() icon_item='add.ico' ############################################################################## # EngineView class. ############################################################################## class EngineView(HasTraits): """ A view displaying the engine's object tree. """ # The MayaVi engine we are a view of. engine = Instance(Engine, allow_none=True) # Path used to search for images _image_path = [join(resource_path(), 'images'), ] # The icon of the dialog icon = ImageResource('mv2.ico', search_path=_image_path) # Nodes on the tree. nodes = Any # TreeEditor tree_editor = Instance(TreeEditor) # Toolbar toolbar = Instance(ToolBar) # Toolbar actions. actions = List(Either(Action, Separator)) # Some delegates, for the toolbar to update scenes = Delegate('engine') current_selection = Delegate('engine') ########################################################################### # `object` interface. ########################################################################### def __init__(self, **traits): super(EngineView, self).__init__(**traits) ########################################################################### # `HasTraits` interface. ########################################################################### def default_traits_view(self): """The default traits view of the Engine View. """ view = View(Item(name='engine', id='engine', editor=self.tree_editor, resizable=True, show_label=False), id='mayavi.engine', help=False, resizable=True, scrollable=True, undo=False, revert=False, ok=False, cancel=False, icon=self.icon, title = 'Mayavi pipeline', toolbar=self.toolbar, handler=EngineViewHandler) return view def _nodes_default(self): """ The default value of the cached nodes list. """ # Now setup the view. nodes = [TreeNode(node_for=[Engine], children='children_ui_list', label='=Mayavi', auto_open=False, copy=False, delete=False, rename=True, ), ObjectTreeNode(node_for=[Base], children='children_ui_list', label='name', auto_open=True, copy=True, delete=True, rename=True, tooltip='=Right click for more options', ), AdderTreeNode(node_for=[SceneAdderNode], icon_item='add_scene.png', ), AdderTreeNode(node_for=[SourceAdderNode], icon_item='add_source.png', ), AdderTreeNode(node_for=[ModuleFilterAdderNode], icon_item='add_module.png', ), ] return nodes def _tree_editor_default(self): return TreeEditor(editable=False, hide_root=True, on_dclick='handler._on_dclick', on_select='handler._on_select', orientation='vertical', selected='object.engine.current_selection', nodes=self.nodes ) def _toolbar_default(self): toolbar = ToolBar(*self.actions) toolbar.image_size = (16, 16) toolbar.show_tool_names = False toolbar.show_divider = False return toolbar def _actions_default(self): add_scene = \ Action( image=ImageResource('add_scene.png', search_path=self._image_path), tooltip="Create a new scene", defined_when='True', enabled_when='True', perform=self._perform_new_scene, ) add_source = \ Action( image=ImageResource('add_source.png', search_path=self._image_path), tooltip="Add a data source", defined_when='True', enabled_when='len(scenes) > 0', perform=self._perform_add_source, ) add_module = \ Action( image=ImageResource('add_module.png', search_path=self._image_path), tooltip="Add a visualization module", defined_when='True', # isinstance doesn't work in enabled_when enabled_when=\ 'current_selection is not None and' '( hasattr(current_selection, "output_info")' 'or current_selection.__class__.__name__ ==' '"ModuleFilterAdderNode")', perform=self._perform_add_module, ) add_filter = \ Action( image=ImageResource('add_filter.png', search_path=self._image_path), tooltip="Add a processing filter", defined_when='True', enabled_when=\ 'current_selection is not None and' '( ( hasattr(current_selection, "output_info")' ' and not current_selection.type in (" module", ' ' " module manager"))' 'or current_selection.__class__.__name__ ==' '"ModuleFilterAdderNode")', perform=self._perform_add_filter, ) help = \ Action( image=ImageResource('help-action.png', search_path=self._image_path), tooltip="Help on the Mayavi pipeline", defined_when='True', enabled_when='True', perform=open_help_index, ) tvtk_docs = \ Action( image=ImageResource('reader.png', search_path=self._image_path), tooltip="Search the VTK class browser", defined_when='True', enabled_when='True', perform=open_tvtk_docs, ) record = \ Action( image=ImageResource('record.png', search_path=self._image_path), tooltip="Start/Stop script recording", style='toggle', checked=False, defined_when='True', enabled_when='engine is not None', perform=self._perform_record, ) # Check the record icon if the engine already has a recorder # set. if self.engine is not None and self.engine.recorder is not None: record.checked = True return [tvtk_docs, Separator(), add_scene, add_source, add_module, add_filter, Separator(), help, record] ########################################################################### # Private interface. ########################################################################### def _perform_new_scene(self): self.engine.new_scene() self.engine.current_selection = self.engine.current_scene def _perform_add_source(self): adder = SourceAdderNode(object=self.engine.current_scene) adder.edit_traits(view=adder.dialog_view()) def _perform_add_module(self): object = self.engine.current_selection if isinstance(object, AdderNode): object = object.object adder = ModuleAdderNode(object=object) adder.edit_traits(view=adder.dialog_view()) def _perform_add_filter(self): object = self.engine.current_selection if isinstance(object, AdderNode): object = object.object adder = FilterAdderNode(object=object) adder.edit_traits(view=adder.dialog_view()) def _perform_record(self): e = self.engine if e.recorder is None: start_recording(e, known=True, script_id='engine') else: stop_recording(e, save=False) def _recorder_changed_for_engine(self, recorder): """Called when the recorder trait on the engine trait of this object changes. This basically toggles the recording action when someone attaches a recorder to the engine. """ record_action = None for action in self.actions: if hasattr(action, 'tooltip') and \ action.tooltip.endswith('recording'): record_action = action break if record_action is not None: if recorder is not None: record_action.checked = True else: record_action.checked = False ### EOF ###################################################################### mayavi-4.1.0/mayavi/core/ui/__init__.py0000644000175100001440000000013211674464502020734 0ustar ischnellusers00000000000000# Author: Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/core/ui/mayavi_scene.py0000644000175100001440000000623211674464502021647 0ustar ischnellusers00000000000000""" A viewer for mlab scene. Adds a button to open up the engine. """ # Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports from os.path import join # Enthought library imports from tvtk.tools.ivtk import IVTK from tvtk.pyface.api import DecoratedScene from traits.api import Callable from pyface.api import ImageResource from pyface.action.api import Action, Group from pyface.resource.api import resource_path # Local imports from mayavi.core.common import error from mayavi.preferences.api import set_scene_preferences, \ get_scene_preferences ############################################################################### # A decorated scene with an additional button. ############################################################################### class MayaviScene(DecoratedScene): """ A scene UI, similar to a decorated scene, but with more buttons. """ image_search_path = [join(resource_path(), 'images'), ] ########################################################################## # Non-public interface. ########################################################################## def show_engine(self): """ Open the engine view corresponding to the engine of the scene. """ from mayavi.core.registry import registry from mayavi.core.ui.engine_rich_view import EngineRichView try: engine = registry.find_scene_engine(self) except TypeError: error('This scene is not managed by Mayavi') return EngineRichView(engine=engine).scene_editing_view(scene=self) ###################################################################### # Trait handlers. ###################################################################### def _actions_default(self): actions = [ Group( Action(tooltip="View the Mayavi pipeline", image=ImageResource('m2', search_path=self.image_search_path), on_perform=self.show_engine, ), ), ] actions.extend(DecoratedScene._actions_default(self)) return actions def mayavi_scene_factory(parent): """A mayavi scene factory that creates a scene with preferences appropriately set.""" p = get_scene_preferences() s = MayaviScene(parent, stereo=p['stereo']) set_scene_preferences(s, p) return s ############################################################################### # A viewer making use of the MayaviScene ############################################################################### class MayaviViewer(IVTK): """ A viewer window for mlab. """ _scene_factory = Callable(mayavi_scene_factory) def _size_default(self): return (400, 300) def viewer_factory(size=(400, 350)): viewer = MayaviViewer() viewer.menu_bar_manager = None viewer.size=size viewer.open() return viewer if __name__ == '__main__': from mayavi.tools.show import show viewer_factory() show() mayavi-4.1.0/mayavi/core/ui/lut_manager.py0000644000175100001440000001117611674464502021505 0ustar ischnellusers00000000000000""" Traits View definition file. The view trait of the parent class is extracted from the model definition file. This file can either be exec()ed or imported. See core/base.py:Base.trait_view() for what is currently used. Using exec() allows view changes without needing to restart Mayavi, but is slower than importing. """ # Authors: Prabhu Ramachandran # Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. from traitsui.api \ import Item, Group, View, ImageEnumEditor, InstanceEditor, HGroup from mayavi.core.lut_manager import lut_mode_list, \ lut_image_dir # The view of the LUT Manager object. view = View(Group(Item(name='lut_mode', editor=ImageEnumEditor(values=lut_mode_list(), cols=6, path=lut_image_dir)), Item(name='file_name', visible_when="lut_mode=='file'"), Item(name='number_of_colors'), Item(name='reverse_lut'), Item(name='lut', show_label=False, editor=InstanceEditor(label='Edit LUT properties', id='mayavi.core.lut_manager.edit_lut')), Item(name='scalar_bar_representation', show_label=False, visible_when='scalar_bar_representation is not None', editor=InstanceEditor(label='Edit Legend representation', id='mayavi.core.lut_manager.edit_represetation')), Item(name='create_lut', show_label=False), Group(Item(name='show_legend'), Group( Item(name='number_of_labels'), enabled_when='show_scalar_bar==True', ), Group( Item(name='shadow'), Item(name='use_default_name'), Item(name='data_name', enabled_when='not object.use_default_name'), HGroup( Item(name='_title_text_property', show_label=False, editor=InstanceEditor(label='Edit bar Title', id='mayavi.core.lut_manager.bar_title_text')), Item(name='_label_text_property', show_label=False, editor=InstanceEditor(label='Edit bar Text', id='mayavi.core.lut_manager.bar_label_text'), label='Edit bar Text'), ), HGroup( Item(name='scalar_bar', show_label=False, editor=InstanceEditor(label='Edit bar Actor', id='mayavi.core.lut_manager.bar_actor'), ), Item(name='scalar_bar_widget', show_label=False, editor=InstanceEditor(label='Edit bar Widget', id='mayavi.core.lut_manager.bar_widget'), ), ), enabled_when='show_scalar_bar==True', ), show_border=True, ), Group( Item(name='use_default_range'), Item(name='data_range', enabled_when='not object.use_default_range'), show_border=True, ), label='LUT (Look Up Table) Manager', ), # Delete this once we're sure we want to keep the new integrated format. # Group(Item(name='_title_text_property', # style='custom', # resizable=True), # show_labels=False, # defined_when='show_scalar_bar==True', # label='Title'), # Group(Item(name='_label_text_property', # style='custom', # resizable=True), # enabled_when='show_scalar_bar==True', # show_labels=False, # label='Labels'), resizable=True, ) mayavi-4.1.0/mayavi/core/images/0000755000175100001440000000000011674464502017457 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/core/images/scene.ico0000644000175100001440000030717611674464502021266 0ustar ischnellusers00000000000000hf  00%@@(B.D(V( ecݒ]͕qetVPNœHXQBSOٕ(hmޘyq}!XYxk`epʇqbg,#4oyם{ߗc]MbߙfLD:zvx֙5UNߗdxdU?8sgBhcGS}n~b~lAWQ@Ek`Vߗe*C@Q7?gPt6"1T~xsvrSNsmPGMfQqdkog^2pq?1:+8@jRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRYYYYYYYYYYYYYYYYYYYK=4!OZF Ea6YYI:]LPWN'M_.YY+ `\TGYY *^B531YY/" > #UHYY;)8,@CS2YY&[D?(V$QYY9%7-J0li7YX5㴴( A(3[IJkaczlry]MY|p`gbVf;:5TEBV>T㴴ʧtjR UO`H`m/76#$)",#&LB\]Pl<;E[Xp$1H㴴ʛʺ|}_WY."@VY qjjkjkkkmlnnpopuN㴴=pkljlmlmnnmnnopuM㴴My4u.t.s.s.s-s-s/u0u0t0v1v1w0y2~9_㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴( @ 㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴 * ## % .!4+08-N/#0)+K/2/ 9C)'/%9 5) 㴴H.T7W<O8G-N8YNONKM?#K@m^hW]GY@WEdS^IO3E Q4P1QRim_MZN;(㴴Q;(|h,{`(se&wg$z71~w!{p0|'}-zmWaHI0ZJVBR=+u`H=}.wg3nG0fd+{o"r_gQ㴴W5FzU_rNWgwenzmlͳmto_hozueicelqT=B]9e㴴ҭѽغk<4) VVj\Up3OU'*!7+4162LFOgSrg[x24:`XpYHk6OO?HR_r㴴ὖ˪԰ٷ|{[TS/':E?Xx<)-  cmjcf]tyljge]yePpBXX㴴‘̢̤ͥͤܧȢzwt}3(8CC 4'uXg~`H=W;9\qrXUmaltDQS!2-㴴ˆΞ͞ϞΞΝ¢lxiPbsgzkeG.Š淉smcW")+-0G07M!-0㴴|ĖƔŔȔǔȔޗpxxhzG;Wx͘Ȕʔɖ՝ВYjQ/,"㴴uŽ‹ȎϊquipIŽđĐÐđďőқɋ㴴kćɁFHkn͒ń㴴a}|}|~yʼn~v㴴Xvvvvttvvuxwvxxwyx|{z}|}}~}l㴴Qpppnonpnpnoqprrqsrtvsuvuwud㴴Imkiikkkkkklllkmmlnooopoqso]㴴ޅEjghhgghgggihhjiiiijjlkmlnjW㴴?c`bbߗdb`bޗbdߗcߗcߙefdfcfeeeefihfU㴴|:ݔ_^ݕaޕaޓaޓ_ܕ`ݕ`ݕ`ݕ`ݓ`ݕ_ەaݕbݔbݖaޖdޕaߗcߗcߖaޘbdeffS㴴Ys/t1u2u2s2t0t1q/t0r.r-r/q.s0s2s1s3u1t1v3v5w7y4{6z7y7o#㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴(0` $㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴 C0>3B7K85,4+@4>2DFB9=9GFI;@8K?MKPIMSMaJKM=Q:H2O:F6XC`GNCP?KASYMPG>N<N:K>QBWBP;PDI8 C.B.C1M:W?OAI>H6MBK@FEQSF@ QSZSO?QKVU]m _aVRXGT@RATC]PeP`K[FZH^MVFVHWGQCMH `U eWXGYQWH I2P;YAS>U:X>T=[BT>XH`L^U]R ]]KII9UDUHcNiQ_KXBYAV=Q@WG[L[K\JZCP5P5R9T6\K VXdj_WYKdVSD [GhTkVgLeNhUiWp`pa%yp&~p&t"{rykwjscudwenZlVqWhO aPjYk[dNcMlWiSl\n\p[ tdr\%yl#nla\oal\mYlW vpltqqfgeutpwzhntfik^qmT|uZooly}guuarrui|}OhkbtxK`^QfhB?:>=20,%@?9>5.C6-bVOqkg~Z]`]`ehvwV[]PFHRMMdhdXbaCPL cU\scfoY]N:>kZ\oZ_dQOudaZPNjahw}g`c>21wyVJPniitwtzZXYZX^urtohomnoRYX;=9),!ED:[UNme]GC8WZZlvxPQObcgFBKniq[ZU~~x~L`Z{mctMPUGIO_^fZXbE5I./0"3$DGPUMd]Tmeiw'-&!' WXf{tqME\WWt êԸxudVkbMB<+ !  "#$"755<;<<:CDIOKGS?LU:8)#%$&G?Hb_pYZhYXkBY[193&%+,"*( ("724632350ZUcdZt\Qgg_wUNcOM\b\w_WtQLcBQTAORGWZ ǦȩɨɨɨɨʪҰʬpp|SKMA845,',#70-A@APgnGZ[=:9)%!%*",&!-+"XYcwwmh_Tkb\rj`ye^wge|e^y`Us]PjMS^0J> Ȥʦʦʦ˦˦˧̦Ш̲{\X\4+%*"?HDOW]KAH6"*%&"!)#-#71(TO^c[whZqh^uuqskkf\WmNQ]U]gTfj;KN ˠ̣̣͢ΣΣΣϢϣӣw~w_YaD97HHB92-)-XD0~_ƥ}ղ׵z}jcVJX]Sfmh|}wpdb{HIR]dnOX`.;2 ˜̞̞͞͞͞͞͞Ξϟ̝|kWho`t[AQoQxhլ̜ݪڨ٧٧ݩҠѯ}ai`YMKODDIe`n]Vh\[k>DD>D@ ǖȘȘȘɘɘɘʗʘɘ͙輔}rcw}[9?zۤסӟϜ͛ΛϛϜϜѝ֠آ͘ǤxhKNB/-, %(&,$+.+ ÐēēĒŒŒŒŒŒƒŒʓٯ{{q}vi]odO͕ȔȔɔɔɕʕʕʕʖ˖˖˗Θӝ֟Őɠtdw[>ZI3 Žō}xO2B.L2O7L; dG\KUKOHM8N8NARBPASQTUME KP ]_URF9MBPVWS\cid_TSUWGO7SAS?Q?TD\M^GdN^I^I^I^GX>U?VFR@T>UIOL aS fX^PWHb[^WUGP; YEV=YES<V:[CV@[BX@Q>UI^J gZ_W_R ]Z ]cD>H9]GVEZIePlUjQ`KZEZA[@T<R@N=aOYNYK_O\I]DM2P8O5W?S7`E YT_d)kzdc ^U]Ol^XKTEQ7W>ZAY9L/T7N7T@aLcP_OcUhU lY k\ k^iUcQ]JcOaJZF`JbMYAbEcH`GbN\J]G`MaMaJ`JdNeIcJ[JX?fMcF`J\@eJ `Y ^\UOZJhU`MbK\E]DbZ}W~UrZ`\`T~SPmvkn`ZkkG_lS@xJK{s?k_9hX,RC2VIIso;cY2UBIgVRk\^~rd`xrb`adqjV~TuuX{{XS~MyIwJ{nWZfTVVADlWVsch[HMZJNZKNZMKktrO`WHGDhbigbdhaajffqjpyz}IA>A=?z|D54L>>QTY?.21E1)2!)++ @$!D/-qbbxhjxrqIE@VMPRNTIIHUSRD47K25O;:H77|ni^VZ>24H:7uhk{~iUWG5;g[ZrghsageWU_QN}omMDDrmx_\]* z_\eQJJuux~|zpy\][[^b{vaaehopZej270=GB{NX^%+#'$[a^;3-qjPRP)0-$*)df_TQFK:9.% TKG_YQxojzppzC<5ihkp|~ZTVjis\ZcZTOeke|CTHnyykfrR[[SXahittrsZTaC,D;3<$/$"4"BJMXWlPE][Qf>?@!*$0)^an{NE[RPiYYzwePXH8>;8BG6E69<+60:=>RG^M?Pd_yUMhHF\,"+:3?H>SGOWcia`G?YNKdccȯ˱ԽtqeR@<.'"  #%(%!(#11*723?9BIBODGMC@LJXW.1.:>@5<83+)' %*#'!$;15[UfpnTZddcyIPXA]\9C?(- (%*,!)'& +'"3102-*350885jdyaXq_UnbXnh`xXPgNK[_Zpb^w^YuYNkJM]C;20(*$+R?,sUǣ|ŖʜԤ˜}}j]TGQZPaeYnkbwzowqieMHXOOY_bp\_o)6,@DC ̞̞͟͟͟͞Ο͟͟ΟΟΞӡͮp\kdO`vkmZmL',}ZyY}_GqSǠw˛۩ک֥֥եץݪפѰvZ`WKNKRYYcTV^b_gxqg\tf]tjiADK:A9XY_ ɛʛɛʛʚ˛˛˛˛̚̚˛̛͛}yl`HY}wpawU% ܦעؤ֣ПΝϞϞООООҠףڤΚɧ|uVF<-*&540=@>7=65<40;/6=9+0&ƗƗƖƖǖȖǖȕȕȕȖȖȖʗŔ|{v{q}xaHX_D՞˖˗˗˗˘̙̙͙͙͘ΚΚϚΚϚќסآȖœrw]BH;&2*)"#!,-)621ēēĒĒđĒđőđĒĒŒőŒɒ|~wp_q~{wp]65̖ȓȓȓȓɓȔɔɕɕʕʕʕʕʕʕ˖̖̖Ι՝՝Ǔ٬|lz]@aK2G9'ŽŽŽŽŽŽŽŽŽŽŽǏԩ|}r_JWo?3ďĐĐĐďĐđĐŐđőőƑŒƒƒǒǓȓȓȓȓɓ˕ϘқϙΘËҩx}yyr>wRɓŽŽŽÏŽÎÏďďĐŐŐŐƑőƑŒȓӦ}[DN\HŽŽŽŽϡu}x}}rr}ܫݧ{||{{{{{{{zz{{{|{{|{|||}||}}||}~~~~~zxxxxxwwxwwwwwxwwxxyxxxxyxyyyzyzz{||}||}}}}}~~~~utttttttssrsttsstttttutuuuuvvvvvwwwwxyyyxyzyz{{{{{}~}}rrqqqqqqppopqpqppqpppqrrqrrrsssstststuvvvuuvwwwwwxyyzzpoooonmmmnnnnnnmmonmnmooooopopqppqqqqrssrrrsssttttvvvvmlmllkjjkkkklkkkkkkllmmlmmmmmmnmmnooopqqoppppqqqqqssstlkjijiihijjiiiiiiiiiijjjjjkklkkkklllmmnmmnnmnnoooppqopiigghggghhgghghhggghghhhhiiiiijiiijjjkklkkkllmmmmmmonmggfffeefffeeedffefefffgffgghhghhgggghhhiiiiiiiijjjklmldecccbcccdddccbcdcbdedeeߘdeeefeeeefefffffffgggghihhijjjb````ߖ`ߖ`ߖ`ߖaߕaޕ`ߕaߖ`ߖ`ߕ_ޕ`ޕ`ޖaޖaޕaޕbޖaޖaޖaޕaޕcޖaޖcޖbޖcޖbߖcߖcߖcߗbߗcccccdcbcedefffghhiߔ^ߔ]ޓ^ޔ\ޔ]ޔ^ݔ^ޔ^ޔ^ݓ^ܓ^ܓ^ݔ]ݓ]ݓ]ݓ^ݔ^ݓ]ݓ^ܓ^ܔ]ܓ^ܓ]ܔ^ܓ^ܓ_۔_ܔ_ݔ_ܔ`ܔ_ݔ`ݔ`ݕaݔ`ݕ`ޕ`ޕ`ޖaߖaߖ`ߖ`ߖ`ߖaߗaabddcdfffܒ[ܑ[ۑZܑZݒ\ܒ\ے]ܒ^ܒ]ے\ۑ\ۑ[ۑ[ے[ۑ\ۑ]ۑ[ڑZۑ[ڑ[ڑ[ۑ[ڑZڑ[ۑ\ڒ\ڒ\ے\ے^ے]ڒ]ۓ]ۓ]ے_ے^ܓ^ܓ^ܓ^ܓ^ݓ_ݓ_ݔ]ݔ^ݔ^ޔ_ޕ_ޖ`ߖ`ߖ`ߗ`ߗabdeېZڏXڏWڏXې[ۑZڑ[ې[ۑ[ڐ[ِYُZِZِYِZُZُYِYِXُXُY؏YُX؏XِYِZِZِYِ[ِ[ِ[ؐ[ِ[ِ\ڐ[ڑ[ڑ[ڑ\ۑ[ۑ\ے\ے[ے]ے]ܓ]ܓ]ܔ]ݔ]ݔ^ݔ^ݔ^ޕ`ޖbߗbsDrCrCsCsEsEsEsFsFsFsErErDrDrErCrCrCrCqCqCpBpCqCqCqCrCrCrDrDrDrFsErFsFsEsDsEsEsFsFsFsGsHtGtGuFuGvIvHvIvJwKwK (                                !!! iii|iii    TLTL [X eXQ:G7LCaLX64%/"><JIGD@2C;BDOK`L`H]=_O]IB(J1aX io]MU; l^ nl lgH= ULULN?M9MDHCD6 ]P`MU;V?B:?@D994>?;2IBGEN;=/F@FGHL ]^[TI<3 5-KH\XLM52^T cRR>C>DC IATHXRPUJX MWSHYO ]a X_JXVmT^ `e Y]B=RDYN_PaLM3N8L7<+V<[AN>OEG2F5WHjIhIeIV>H;I@aZTAY@YCO@[\ah \^EOUS TQ PNQFI7I9TCU>W@VBO>SENBUGWGV?^HT<P5S=D1M@ ZRbQSFI= TKYZID K<K<P7J-GDFK;/ RL WN\D iQF<9:<07*64)!HAKIR9R>RGHJKS eccZRB7"6'MC^_LO*%HBTGVJMD30.*UGk^ddBI?ERIeXaaM\B`PjN^ \a[[KDM;R<^QYCL/O8G8H=oS hOP@UKV=K8TGmShQt^XME@PL kmK>G1V@TGV\^mgc^\]f]gKKM@K<I=_OiQYBL<SE\OVIYI^OS?ZKbM WA aQM9 QGbYcQG?4&A3=;=: D+D+K4F4>04-0+/*>5V>T?A57%5%@1=1@/@->1K9N?F:E>>CGHB<>7HL TX \]XRC=A;H9=2G? dXJBD:]LgNeL dOXIN=dSZKHEKO N^\q ^cRPMLUI^KbKWDWA\BYAaEZ?IBL?W?\G^XYQ f_n`p]nZbMSGXP\NUAQCG>IFZh ^gVL]`aqTWRCM?RETH`O[NRL_O`NTJ[K[JUMXJnZxh jW_INID>UAWCQ<H5D3<* E*E*M=NCG5:%2"A1-#72ZBS5H.B2M@K4@2C6F5B:0&:0@3F= RYB7;/;E2< 1< NTW_USG>@4;5NUYYPMUP`RQ>N?TLXPYNUU \S c]Xt%][}[yWXNETZaZcP\AUHXOYHN@K=S@RELB\NbTdUq^fQjP_GW@\K_SU@\GUFVGYRYL^KNDD@LC^ObMfY YTRFIBC<=,BEHPVKZHlg f[\J[ML:M>JA[RXRL3^G`NcZOK =!=!D;F>G/E*M7S>@*E4O:[DdE[EQRTFNLHOF>J9M7M7G7TGUKS@M7F4H5JC N^PXJFGDA>26 GTXV^X\XPMB>:4MFVOKS Wi`b V] Mj\z bg `^ e\XS U^ Ze`aPAD/N<OCKAN=Q9T<UHXGXJXU_T_IhTiVgPY=S>_I\JVO[C]EZDV?R>SAXHYH]ZTTF8G6H9K9J8HHH?V=hK`HTFRBR:]F\LhV l_``a]ZUN@WQSR B/B/E1D);!A)aIeHO4J:TD ]DkR fLXK[F[M `VNCJ8Z<fNL8P@PG\M`J bS^P[PbSVMPP c[UG [W_i^bZU ]\TQSAM8F8K@GIP[MFO:XGaZ rcmUfP[PUMHCSB\CR8R2U9ZFWCUAYDL?TCV=UF[HY=Q:dNaM_NbK_E`GcJaI`RdL^?V9^EU:G2G6N6R1]<gNh` c[QIPOYX fZm` h[h\ e\YJPC\Y]]cUXIL@\E]HUI F2F2I7Q;I1L/R?O3P0ZKdS`>]?\CR8UE[MS>[C[?T:I-J+L.:*94RM YMXB`Qnngg\OYK[N\M KSZsegLI3-=-Q>`FYEVCTE_T_JhLpWpYiOlReM^LXGY;[<[<W8\D_HW>O9S:H2D1L9gPjO]ISLXH_PcUi\c[T<]@dGV;G-S7_EXAR8fIiO]DS2fCePZS ^Tmj.v8u/n{mvgiollh]S ^V d]pe a[RJPEVN]MP= `?`?S=MBbUfP`WVFO0VB WED5C1L/Y6\<eCM1M7^E[F^KfT_Q`J eQ`RYIeM kTg] Z\bRcW \Z\WbZ ebTZJJB22+NIdR\DcMYEXQbK^C[UhagIgP p\jPdM`N^P\HZC^EW:S6Q>ZQZIL<THaVaXUJ[\\RO<Q?]JVHYCbQW>F)N1G1G4G/I,WDQAR4O;ZB_E RSNN MDU[)g{%ovmg[RMDP< VB cSt\t\YLR>VC\LhX\O [D[DP6U<S7R8 ZE hU`F]A\?L1R:Y:[? X@K9TH g[bHi[ fPaL[I cZ fY^F]IfLgXeQcN]JeUrf mZjXeTaNYJbOkWcKgLdHZIJ<WG]HeUbgYIiGdN^IgPbNT?]NZBdRXLR9[AX@]I`KYK]L]IVL`LdQh\_HfLiP[?bG^HWTI>P3bQbXT;P1T>Q<H-M8^?bGfe QUV`X` U[E@;5UM dY gVZGcRcMYE[FYCN9]FZFSD C0C0@(L4[?W;R8Y=T8\=^8U5U3W;U;S=G5P5W:R9aNcT[?X9VB[L[Nh\ p\iOgRpYqaiadKcIcJhSkUaJ_H]EaKjX^GdI jYjRdIjKb<J-R:a=Z8V7[=a?[8\B]T]QUE^I]M]I]N`LfNdGfJcD^AePdHiJfKcLgTeR_JgLiN w\iFnHqVj`iRgH`@oPdJbT hafY^UVMH<S@kTqXiPRB\J]FP:_EYBZAcFbI_C cZcZeYgXqajWiSlWpXiSfU`KZKg]j`i`f\0sl5xu%nh+yk8|zEGT]aiYWymhZMI5C:1/7*|wqts|r%w%x{owc vf)zm&uk(uc.o,n'ug&qy`mTlTdKr[({tj segU`FgKkN"nR!aI&fM(pW0m6x2o0|k5nF6z.m1y3x6uCA<8{FHQE?B;}8uC@AEC7~4y?5z3{5{3t4z6y4z,{p sfa^U\|w{|vRboYpewxqbmi\i`ZcXTXPOMFND@JE;VRIpply}zTTKK?1aVKo`Yx^Xrloj|a\~~~qots{}v|giqhalh\enfpjepnpvps{mu{ox}hss_tt[utZso\yv`{e^ vw{~muorxpsujrlknrxyruxlry~z~yrwshlsinyryvoojlTlauxnjn]WY][]W[Za`emhq~zz|qoo>@HhjuvnrR?AT::meffklOBIWGKB"%, 0V<2U89) 3/101$6G$'@%)`KPXDI}ptLCIripj]i}wvo}icmmhqonusqsllsh`flajg[dmbhk^eymsvmwqmo~idnc^gidkqkoigmppo cXYcXY=/+3A',PDB8*"( 4 ?,"7*"`PXJ/,* 5 .$ /0@.06":/*_Z_LFA5* ! 1!#HAG[\YE>;\PPuji=71vxTNI_XZ\SS:3, "+B;BLX[@F],'7 % ' C53COK &3!!7"<5!D<.$ #%4.!  7' 0% D70;5,KC=0"%(nsg'>&/7'DHD>BC!+$ 26(++$B"44 )T?)ypf[^ % #!}bM uxux,(NBEja^-""B+,6!$/xmusrnWOM;4/<2,M>>I:?\QTRQG'#9+&WKG[VN&% JDPUX^|uhtg^b¾ZMV(2*!*% ]ji57@ smoJHL4==JNNR31UB8,%%%-8A8)%!zy}fcd6@0~}w|SVVfhjgt}7B6(4"8A38I@/4$s-LJ0(.8/*   5A80% [A6JUN)"0 "  {?,:1qmf}ykdXGG:%3($ kje~y~xu{|yz{k^[XNLZPVNQJaV`w$*V_]UGX;K>0J2)3)t|W]Y_f{vz8&;@,D8"56-6>>)4-&!4$!1+=,Q[alpGNdK?ROAURCZc[lDBMKG@9?8#1('=-8OK^cpwwSDaD7MSLhXSobbSLoE8SD:~o_\SH$& caiivvLWd_gpFNP("2((>=BUVoDEM .,-;3:tqu(&bfbžsxeh}wvPc_)73'/&>VTdyghpyystg_n0(;1?3HH$-'37=49;024;6;?:E<@D6?BC3IJ8PC-C;)78==*,*%2 (C-+1&JMZ;?JPMWjbzSIfN?VSI^JBXTPgKS_ )$"NEVbjsmqdc}aay_b}bcTNlG1;7215'5-!- ?<@[NhF@P=3AWI[jeccVPmM@UXPiPSj2>B!0!)+!,;3AD7NA6GETRZcrdb{fgot]XxI=X>;PACVWUn`^]^RNkA9X ŮŮƮǮǮֺ|}f}rski:%)44$08%/7#,4!'+*+)'-(./$1;0)'(&(1 "&/%""/2"-1$,2(19-*2%&,$"<.<:8<@CH|yJFM7A?/B;;7A@8I9A?IY]CKMHCJbbu;DI3<=EpsJux@`dHOZ;/9JKORRaH?T>/=E:K<8B2*/0*,=57XIYSL^LBTRBXMIZ34:>;E8EI.-+;>D+B80:0WLb[QjUH]OAUI8K`Vjlj\XxOF]XXpFJ^%.')+081FBQK:THASME]>;J4H@dqzirjp]XxQIdOGfI>XXXoa`~il^a}EC_ ªªëëëƮ̲ɮ|xn{pXA1$<5'.8''(((,),()""" )&!.&  "&$33+46+)+ !"-,&(,!960D2=HAMCKP>AGLARNCRNKU)4*<>ARH_WMg;.Tc\tXOlNB]RF_NQc!-.'/%TJdZOlQE`QE_ZVnSKdLEWU]ifr~l|JWlNNfSMhLF`_cygjpuuSXs ĪĪĬĬĬīĩêūϸȱĬǮʰ̱Ū׹гw_[RB<5(&$$ &"($'("%  "'"& $&!'#,( ') & /(%6,/F/CGDOAFGTM^OR]69;QBWZOgM:P,%$RmyHbe.C5*'$'!+-(171.4)0;1/=10:/-1++.$++%($1&$8-0C=D=28L>O=@D32087>+-&-0)E:F_Sk\Pg`SnTG]RAWR?Uh`v]TnZQhVJgTJb/<3 +C=I`ZwXOi[SoSIcUNiRE^VNc199NJC``:NF9FA;GE08.&&//&5?4.2&03)191,+%,+$$")+!.1)0+':254236/2282"#=:7nf}f]r]Ql^Snf_yYNgXI]kaug^wYJbhc|JDZ6<>JHT[Qhheza^z_Ys[Rp[WqULhOBZWQhHal#I9.P;<>?;C.+',("+' 6;38=6.1'A@=rnnj`YraVpe\ubZsZRif]qwt_RncZq[SkULcec|YRiYVege}_[u^Zub_yc_VLgL>VUQfXSiCDL/N?*K=)11&9/AYRLdh_kwdv ƩƩǨǩƪǩƩǪǩȧǩȪɧǧǧǨƨƧƨũŨƨǨ˩{vyru|fhlDA?2)%('&%,#,$0&'/(#73.964DBI`akIDIMBPKUYSnuZkv_kvLai=ROOmr) !2# ,0#$!$#+,$#& ("'-)%6/02/+.+%*&.*!:9.&/%,30&- ɥɥʦʥ˥ʤʦʥʥ˥ʥ˥˥˥̥̥̦̦̥ͥͥͥΤΥΥͤФDz}||bMY^\a9/(5*%1(82(>A7>=6@?=_\d\ZaAC?L0!8('& ).%3*0'+ !  # E5"K=)O=*G4%lX?fU=[I4A1#..'NMVlbxgVpeUmcVjdSexk|i]rtprnnf}xqvmh_xwpstfgGHV3<9*>+/I;4F<6K<>REOif1C=(:/-C7&2-)B/]nv ̢̢̢̤̣̤ˣ̢̤̣̣ͣͣ͢Σͤͣ΢ϢΤϢΣϢϣϣϢϢңîufziXdv~}ylnyIED8*"5-!71$AF=EOJBGA@I:DA:5$1$,+( 0%/$)7(O8%kOa߸ʛʝ•ݫ٨بkj]DHD?O?MbO_n^riXp|n|m[pk_vkbwmdxtl~pcykax}}ge_TrNE[?IKADIABI:7=SU]io}DLR+B:%1(&6H@QR^?CH ΟΟ͢͢͡Ρ͢͢ΡϡΠΡΡΡ΢ϡϢϡϡϠϡϡϡϢϢϡϡТѢ~~dHWshj[krg}yy\JUPD>[Qh849!  ̟͟͟͟͟͟͟͠͠Π͠ΟΟΟΟΟϟϠΟΠΠΟΠΠΟϟΠ͟ѡš~xiyW8A^DQ{wzwiTe\?Krdw6 fItSE4"9$5"3=-M9&w[?fÑ٧ץեңңӢӣӤԣԣդԤգդ֤ޫܪɨ~\FB-63-784GIOd`pc[of`lw^Uboisummje]raSjfYukez~rtPPbBBM&+#@I<{ynauWQc$5)  ˝˝˞˝̞˞̝̞̞̞̞̞͟͟͟͝͞͞͞͞͞͞͞ΞΞΞ͟͟͞Ѡz}pYiZ:DT4BeUldKUwp{qfP^U4:.`齍ի~Útuǡv轎ԣڨգѢѡѠѡѡѠҠҡҢӢӢҢӡӢԢӣԢۨۨÓmjJ\S>?@;>DEDAGNQXDFL /#  ʜʜʜʜʜʜ˛ʜʛ˛ʛʛ˜˜˜˝˜̝̜˝̛̛̛̜̜̜̜̝͛̚˛vzk|cIYeQgAmZi|wzos`wP*1<џw֢ҟգץפ֥ӣОΝϝΟΟПНПРОПџѠѠџџѠҟҠѡѠҟդۧڥϞn~fHNC.01$*)%((!*( 680HFGOMMVY]LNU;@=6=68;59D=15.&/ /71340*5) )-B,'  əəɚɛɚɚɚəȚɚʚșʘəʙʙʙʚʙ˙˚˙˙˙˙ʙʚʚʚʙ͚ĖrzxxeObm]mwomUj>t@*ќ͚͙͙͙͚͙͚͚͚͛͜͜ΜΜΝΝϜϝϜϝНМОНѝОООММўҞԡڤ֢ƓqoU>?-(#/,/0!/3$7:.6;-5<01@/4?16@73A54>=4:3/5+"('$)  ǖǖƘǗǘǘǘǗǖǗǗȘȗɗɘɗȘɗɗɕɗʕɗɗɖɘȘɗɘɘȗ̚ḑ}uymm\i|w{tykRa4^֠ʖ̗̗̘̗̗̘̙̘̙̙͙͚͙͛ΛΚϚΚΚΛϛϛϛϛϚϚϛϚЛϜϜНМўԠآ֡đhkO:9+   ('.-6/&/&1&30(13$:B9HDLTKWIFM..$"0 ŕŕƖƔƖŖŖƕŖƕŕƕǔǕƕƕƔǔǔǔƖǕƔƔƕǖǕȔǔǕƖƕ̕ҭ}z|~|qjVf~}vruk~xdtR+3]%͗ʕʕ˗˖˖ʖ˖˗˖ʖ˗ʗ˗̗̘̗̘̗̗̙͙͘͘Θ͙Ι͙͘ΙΘΙΙΙΛΙΛΚϜҞסѝԩ|^}cEH4$+-",'( %'*)-)3,$00$+.!) ĒĒŔēĔĔÕēŒēœŒēĔŔĒŒŒŒŒĒĔēœœœƒƑœœƓƓƓ̔žww}yysbHY~u{yt{rdKSGњlҚȓȓɔɕɔɕɖɔɔʕʕɔɖʕ˕ʖʖ˕ʕ˖̖̕˖̖˗̗˘˖˘˗˖˖˗˗̗̖̖̖˗˘̘Θӛ՞՝pdkJrZ=@+#" +.'% ÏÏőĒÓÑÒđĐĐĐÑĒĐđĐđđđĐĐđđđőőđĐőđđőĐőȑ~w~~wiQdgRayszqztp`jR14](ȔƒƒƓǓǒƑǓǒǒȒȒǒǓȒȓɔȓɔȔȔɕɔɔɔȔȔȕȓɔɓɓʔʖʔ˕˖˕̖̖̖̗̕̕̕̕ϙԜԜҙҚܪ~ySjAYjR2. $ ŽŽ‘ÐÏАŽÏÏÏÎϏÏÏϏÐώŽŽĎÏÐÐÏÎƑ븈}|||quizkU`X5?vC3ŐŒđđĐđőđďőőŐƒƒƒƑƑƒœœŒŒŒŒƒƑƑƓǒǑǒǓȔɓɓɓɓɔɔɕȕɔɔȔȔɔȔɓɔȔʔɔΚҜљԝϞѩ˨x ŽƎܭ{{z|veQ]9 EjL˔ŽÎÎÏÏÏÏÏÏďВÏÐĐÏÐÏĐđŏŏŐŐőĒőőőőőőŒœƒƒǒǓƓǒǒȒǒɒȓȒȔɔɔɓȔǔǕǓɓϙЙ̑ Ê۰|wyyzl|umdLW.N }ՌŒŒÎŽÍŽŽÏÏÎÏÐÏÐÐÏÏÏĐďđđŐƏƐŐƑŐŐŐƑƑƑƒƒƒǑƒƒœŒőƒƓË Ë紆xh{{jx}panxl|S+/0oMɒŽŽÎÏÎÎďÏÏďďĐďďÏŐŐŐŏĐĐŐĐĐĐđŒ 䰁u}hS[`EL?u=&ŠŒŒŽÍŽÎÎĎÎÏώΎÏÏĐ ܩw~uhTYL-1km|ŒŒŽŽ ֣prdskxxvosMIު wawsqnmprx{pʝ~ ﴀ汅ͬ~dۤ{ ~~|~~}}}~}}}~~~~}~~~}~~}~|~ }}}|}}|||{{||}}{z||||{z|{{}{|}{||}}}}|}|||}}|||}}~~}|}|~}}}|~~~ zz|{}{{zzz{zzzzz{zzyxzxxyyz{zzz{{zyy||zz|{||{z|{{{{|{{|||}{|{|}}|~}}~~~}|~~~{ xxz{zzyyzyzyywyxyxxxyxvxyxxxxvxzzyvxyxzxzzzyyyyzyyyzyyyz{zz{{z{{z|{{{{||}}}~}}||~}}~}}~~~~z xxyzxxxwwwvxwvxxuvuxwwuvwvuuwuvwwuvvwwvvwxwwywwwvxxxxxxxxxxywyxyxyxyzzz{{zz{{z{{||||||||}{{|~}}|}~~~}}x uuwxutvtuutwuuututuvtusstssvvtuttsuuttuuuutuuuvttuvvvuvwvwxwuwvvwwvwxxwxyyyyzzzzyz{yyyz{{yz{{|{z{||||||}~~~}~w rrvvsrutssrtrssrusrrqtrqprrssstsrssrsstrrsstrstututstutttuutuuvuutvwvwvwvvwwxwxxxxywwxyyyxzyzzyzz{{zy{{{{||{||||t rrssrqtrrrrrqrtqqsrqqpqonqrqqrpqsqoqrrqqpppqpqrsqsrqrssrtssstussstsuutsttuttuvwvwxwuxxvvwwwyxwywwyxxxxyzyzz{y{z{r rrsrrqqqqppqqppppppoopooooqqopqoqponpqpppopppoqpqrrqqprqqrqrrrrssqrsssssssrstttuvvutvtttuuuvvvvvvvuwvwwxxxyyxyyyp ooqqqpppponpppoonnmnoomoomopoonnnnomoppponnqomnopoponppppppooqqrqqrqqrrqprrsrsrrttsssssqsstrtstvuuuuttuvwxwwwwwvm nnooppmooonnonmmmlllmmmmmllmmmmmnlmllnnmllmnmmnnnmnnnnonoppoooooppnoppoqqpqpqqrrssqqrrrrrrsrsrsssrussrtuvvtuvutvm kknonmmnmllkllkjjjkkkklkklllllllmmmlkkllllmmmmnmnmmmnnnmmmnnnmoonomnonpoppppoqqqqrppqpoqoqqppqrrrrqqsrrsstttstuuk jjlmllkllllkkkjjjijjkjkkkklklkjikljllkkkkkllllmklllllllmlmllmmlnllmmlmmnmnnnnpnoqponooppnnooppqqpppqqppsrrrtttssi jjllllklkjkjjkhhjihiijkkjjjijjjijkiijiijijkijjkjjjjjkklmjkkmllklllklklllllmmmmnnnonnmooonnnmonononpqopopqrsppqrrh iilkkkjiihijjiiighghijjhijhhihiiiiihiihhiiihjhijihjjijijjkkkkjjjlkjjllllklklllklnmlmnmmonnmmnnnoooponpponpronmopg ggjjjkigghghighghhgiihhghgghhhhhhhghhhhhhhihghiihihiijihijjjkijijjiiijjkkkjkkkklllllklllllllmnmonomnmmnommppmmnoe ffihgijggggggfgggghhfgigfffggggghhfffggfggggghhhhhhhhhhihgiihgiijiiihhhikjjhjjjjkklkjkkkjllkkllmkkllmmllnnnonnnne eegghfhgffggfgfeegfgefgeeegfecefeffefffeffgfggfhgffgggfghgihgggihghhhghgihghiihihiiiijjjijkiijijkkjkkkljmmmnmnllc ccgffgffefffffeedfffffddedeeddfddeeeeefeefeefffgffegeffefgihghhghhgggggfffghhhfghhhhhhhjhhhiijiiiikjijkkkklmmlklb ddeecfeedcdedbcccdeddeddecddcdcdceedeecccdfeeedefffefffffߙeffggffegfdfdefefggggffgggfgfghgghhghihhihjigiiijkkkkkja ccccdec`aabaaaaccaacabecdebbbaaaߘ`ߗacbߗcaߖcߗbߘbߘacddccߘdߘdߘedޘcߘdߘfߗeߗeߗeߘdeddfߘcߙdcdddefgdefefefedeeefffeffggfeggfihhgghhhhjiijjjj_ ``edca``_`a`ab_`b``ߖcߖbߖaߕbߖ`ޖbb`aaߖaaaߕaޖ_ߗaޗaߖ`ޖ`ޖaޖaޖbޖaޖbޖbߖcߖbߖbޗbޗcޗbߖaޖaޖbߖcߗcޖdޗbߗcߖcޗcޗcbߗdߖdޗaߗdecߘdߗceߗcߗcdbcedbcdbdbddbddbdgdeeffeffgggghhhgiiii] __dc``^ߖ_ߖ_ߕ``_`ߕ`ߕ`ߖ_ߕaߕ_ߕ_ߖ`ߕaޕaߕ`ޕ^ޕ^ޕaߕ`ߕ_ޕ`ޖ`ߕ^ߕ_ޔ_ޕ_ߕ`ޕ_ޕbݕ`ޕ`ݕ_ݕ`ߔbޔaޕ`ݕbߕaݕaݕaݕ^ޕaݕaޕaޕbޔcݕbݕ`ޖ`ݖbޖdޖ`ޖcޖaޖdߖaݖ`ޖcߕdߕcݕcޖaߖbߖaޗbޖbߗbߗ`bacdcߘcbcdaabbacdddceedffedfefhhhhiii^ ߔ]ߔ]`ߕ_ߕ_^^ߔ_ޔ^ߔ]ߕ]ޕ\ޕ\ޔ^ޔ_ޔ^ޔ]ޔ^ޔ^ޔ`ޓ^ݔ]ݔ_ޔ_ܔ^ܓ^ݓ_ݔ]ݔ^ݕ_ݔ^ݔ\ݔ_ݔ_ݓ^ܔ^ݔ_ݓ_ݔ^ݔ\ݔ]ܓ`ܓ_ݔ]ݔ]ޓ^ݔ]ܓ_ޔ^ݓ^ݔ]ܕ^ܔ_ݔaݔ_ܔ`ݔaܔ`ݕaݕ`ݔ`ݔ`ݕ`ޖ`ݕ_ܕaݔaޔ`ߔbޕcݕaߕ`ޕ`ݕ`ݖbޕ``ޖ`ߔbߖaߗaߖbߖ`ߖ_ߖac`_`ߖadacaabceededeeeefgffgf\ ޓ[ޓ[ޔ]ߓ[ޓ^ޔ\ݓ]ݓ^ݒ^ޓ\ޔ\ݔ\ݓ]ޓ^ޔ]ݔ]ݓ^ݓ_ݔ]ݔ]ݔ^ݔ^ܓ_ܒ^ܒ]ܒ]ܒ\ܓ]ܒ^ܓ]ܓ^ܒ]ܓ^ܓ^ۓ^ܓ^ܓ^ܓ\ܔ\ݓ^ܒ]ܓ]ܓ\ܓ[ܓ\ܓ]ܓ\ܒ^ܓ[ܒ]ܓ_ۓ]ے]ܔ^ܓ^۔_۔^ܔ^ܓ^ܓ^ݔ_ܓ^۔_ܔ_ܔ^ܓ_ݔ_ܔ`ݔ^ܕ_ܔaܔ`ܔ`ݕ`ݔ`ޔ`ޔ`ݔ_ޔ_ݕ`ݖ_ߕbޕaޕ_ߕ`ޕ`ޖ`ߕ`ޕ`ޖ_ޖaޖ`ߗ`ߗa`b`ߗadccbbadeggfffe\ ޒ\ޒ\ޓ^ݓ\ܒ\ܒ\ݑ[ܑZܑ[ݑ[ܒZݓ\ݒ^ݒ]ݓ]ܓ]ܓ^ܓ`ݓ]ܔ\ܓ^ܒ]ے]ܒ]ܒ]ܒ\ܒ\ے[ے[ܒ[ܒ[ܒ[ܒ\ܑ]ܒ]ܒ\ۓ[ۓZے[ܒ\ۑ\ܑ]ۑ]ڒ[ۑ\ܑ[ۑ[ڒYۑ[ۑ\ے]ܑ\ܑ\ڒ]ړ]ڒ^ۓ]ۓ^ے[ۓ]ܓ_ۓ]ۓ]ۓ^ۓ_ۓ_ܓ^ܓ^ړ_ܔ^ܓ_ۓ_ܔ]ܔ]ܔ^ܔ]ܔ_ݔ]ݔ]ݔ^ݕ^ޔ_ݔ_ݔ`ޔ_ݕ]ޔ_ޕ_ݕ_ޖ^ݕ_ޔaޕ_ޖ_ߖ`ߗaߖ`ߖ`ߖbߗ`ޖaߗbߘaߘaߘbߘccdfffeޑZ ݑYݑYݓ]ܒ\ۑZܑZܐZڑYېZܐYݑZݒ[ܒ[ܒZܑ[ے[ے]ܒ\ܒ]ܒ]ܒ]ۑ\ۑ[ۑ[ۑ\ۑ[ڑZڑZےZڑZے[ۑ[ۑ\ڑ\ۑ\ۑ\ۑ[ڑZڑZڑZڑ[ڐ[ڐ[ڑZڑYې[ې[ّZڐ\ڑZڑZڐ[ۑ[ڑZڒ\ڑ]ڑZۑ[ۑ\ے]ۑ^ڒ^ے]ڒ]ۓ\ڒ]ڒ]ڒ[ْ]ڒ_ۑ^ڑ^ڒ]ܓ^ܒ^ܒ]ܓ^ۓ]ܓ]ۓ]ܓ[ۓ_ܒ_ݓ_ݓ^ܔ\ܔ]ܔ]ܔ]ݔ^ޔ^ޔ_ݔ_ޔ]ݕ_ޖ`ޕ^ޔ_ߕ_ߖ_ޕ`ߗ_ߗ_ߖ`ߖ``ߖccdddߘdސX ېXېXܒ[ܒZۑY܏Y܏YڐYڏYڐWېZܑ\ܑ[ۑ[ۑZڑZې]ۑ[ۑ[ܑ\ۑ\ڑ\ۑ\ڐZُYِXِ[ڐZّZِYِYڐZې[ُZِZڐZڐZِYِZڐZِXِXِYڐ[ُX؏ZُZڐXِX؏YُZڐ[ڑZڐYڐ[ُ\ڐYِYّZڐ\ڐ]ِ\ڑ\ْ[ّ\ّ\ّ\ّ\ّZڐ]ڐ\ڑ[ۑ[ې]ۑ\ۑ[ڒ\ڒ\ڑ\ے]ܒ\ܒ[ے\ے]ۑ\ے[ܓ[ܓ\ۓ]ۓ]ܔ]ܓ^ܓ^ݓ]ܓ_ܔ^ܕ^ݔ^ݔ^ݕ^ޕ^ޕ_ޕ_ޕ_ޔ`ߖ`ޕbߗaޘbߘdߗcߗbސX ܐXܐXܑYۑZڐXڎYڎXڎWڎWُVُZڐ[ې[ڐ[ڐZې[ڑ[ڐ[ې[ېYڑ[ڐZڏ[ِZؐXُZُZُY؏ZُYِXُYِX؏ZُXُYُXُY؏X؏X؏W؏X؎X؏X؏W׎W؎W؎V؏W؏X؎XُYُYُZُY؏Z؏[ُYُYُYُZؐXِZُ\ِZؐZؐ\ؐZِYِZُ[؏[ڐZِYّ[ڑ[ڐ\ِ\ڑZڐ[ېZۑ\ڑ[ڑ\ڒZڑ[ے[ۑ]ۑ^ڒ\ے\ܒ]ۓZے]ۓ]ۓ[ۓ\ۓ[ܓ]۔]ܔ]ܔ]ݔ^ܔ^ܔ_ݔ^ܕ_ݖaޖaߖbޖaݕb܏V ڎWڎWېZېYېXُWَVَVَWڏXڏWڏZڏXِYُYُZّZِXُZُZُYڏ[ُY؏YؐZُZ؎Y؏X؎X؎XَYُY؏Y؎Y؏V؏WُXَX؎W؏V؎V׍W׎V׎U׎W׍U׍T׍V׍V׍V؎W؎V׏W׎W؎X؎X؎XُXَX؎X؎X؏X؏Y؏Y؏Y؏X׏[׏ZُY؏Y׏Z׎[ؐYُYُYِX؏XُYُ[ِYِXِ[ِZڐ[ڑXڑYڐ\ڑ\ڑ[ّ[ڑ\ڑ\ڒ[ڒ[ڒ[ۓZےZےZܓ[ۓ]ۓ`ے\۔[ۓ]ܓ\ܓ\ܔ^ܔ_ݕaݕ_ޖ`ݖ_ۏT ڎUڎUڐWۏWۏVڏVُU؎T؏WُWِWڎW؏VَXُWَVَYِXُXُYُX؏YُZ؎X؏W؏Y؎X׏W׎W؎X؎V؏X؎X؎V׎T׎V؎W؎V׍V׍U׎U׍U׍V֌V֌T֌T֌S׍T֍U׌W׌V׍U֍V֍U׍W׍V׍U׎U׍X׎V׎W؎X׏W׏V׎W؍Y؏X׏W׎X׏X׎Z׏X׏X؏YُY؏W؎XُWُY؏W؏X؏YِXِZُYِYِZِZّZّ[ّ[ڑZّZؒYڒZۑYڒZڒ[ے[ڒ[ٓ\ۓ[ٓ\ۓZۓ\ڒ]ܒ]ܓ\ܓ\ܕ^ݕ_ܔ_ۍR ڍQڍQ܏U܏V܎UێTۏTڏSڏTڏSڎTڎTڎVێTَTَSڎTَUڍVڎUڎUڎUڎVَUَUڎUڎT؎T؎U؍U؎SڎTٌVٍT؎T،S،S؍S،S׌S؍R׍Q׌Q׌R׋Q׋P׌Q،Q׌R׋S׋R׋R׌R׌R،T׌S׌R׌S،T،S،U؍T؍R؎S؍S؍U؍W؍U؍T؍T؍U؍U؍W؎U؎U؎TَUَVُTَUڎTڏUُVڏVڎUُVِWڐVڐWۏWڏVِWِVڑXڐXۑWڑXېXۑXۑYےYےYےZۑZܑZܒYܑ[ݒYݓXޓ\ޓ[ޔ[܌Q kDkDmFmFmFmFmEmEmDlFmEmDmFlFlFmEmEmFkFlFmFmFlFmClFlFmEmElElElDlDlElElFlEkElElElClCkCkCkCjCjBkBjDkEkDkDjDkDkDkEkFkFkEkElFlElElDlElDlEmEmFmFmFmElEmFmFmFmFlFmFmFmFmGmFmFmFmFmFmFmFnGmFmFnFnGnInGnGnGnFnGnHnGnGnGnHnHnHnHnHoHpIpJoJƃP    @@@-xxxIuuuHvvvHuuuHtttHtttHtttHsssHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHsssHtttHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHwwwHwwwHxxxHxxxHxxxHxxxHyyyHyyyHzzzHzzzHzzzH~~~IDDD.mayavi-4.1.0/mayavi/core/images/add_module.png0000644000175100001440000000136011674464502022262 0ustar ischnellusers00000000000000PNG  IHDRasRGBbKGD pHYs  tIME2OpIDAT8˥[HQ3;3gf+q+j("znPHCEXkB^)&aAemflvqT8w>$byAa,kHݛO n}6g^H0P 2plp#PaOzYg4vMŻAWϿ1>4˔3\ۂ7';jDϡ~Py50QRIȅ o*C= Z[u-%f|ұ=b52S;\G!sIipH j6TQސņ'2(.iFBAOHc)5ì5ǧJ[@*5bF`@%VkRRPC|э:SP4, $ugPU'.`yОڕHD*.6q-=P"xS2s:P^w'ʏ*WKQĨȣ~H?LB(">ք%s@ o4|m |ųN'd҉WY t *`jO?#A=#NGYIENDB`mayavi-4.1.0/mayavi/core/images/filter.ico0000644000175100001440000030657611674464502021461 0ustar ischnellusers00000000000000hf  00%@@(B.C(V(  &5 .,}mm얉%||%п}}& vv}}{q,.z||y^^tty[{zhgoWWsd"DY![ytubtl[n_JT WiZ/()*(;]Cw #(,,,,%1]%}od%,,,,-# < Zumm(,,,,,*0eSDmff1,,,,,*3 &6%I>?pii,,,,,*3 &B1"E<>VRR.(,  /H;rrLCC_) >C;;_ͼ>77_~~cμ<55_lljjWν>60~rCpp|``|dd[Ÿ|EI>SppddqXXs]\Wx&@c=Srkf_z_XnUOrWBY6m\@ c?Dvfl\{eVu`RpD3R8ͻQd]@Jr....,47l'B}22223- .*5y|x($_222221) /5U#ƺ`2222210- 4<ທ`222221/2,"C4+W^ 222221/1/+)0#"W! H222221/1/+/#(\C>>/222221/1/+/#% Vf``;222221/1/+/#% '!!aZTT=??( @  .D99://v!]LL:11z# ZII6,,x wbbͺ}}2**x"ͻzz0((y ssͺyy/&&w"vvggͻxx*##z;@4 vvmm}cc~~ͻxo'%7`XYvvmmiiv]]zzCqr [vvmmii|ccpYYvv f9>[yyppllffybblUUuu#n>\]= Y x^PqWImSEgN@bK=\F9U=*rhw&eagU\= 6oooopppfV]>rZ@9999999.v{g\9qb:55555552,~v>B&x"55555553/*waeri` w 5555555310)~|%nkff y#55555553110,w+fλfbb x 555555531111-}+ZZwͻd``x#55555553111257.&-^PMλb^^z!555555531112576/:+WKLͺ_]]w555555531112576/:5'$#TIIͻgcc.555555531112576/:5'$,""NDDzpp 555555531112576/:5'$,""'JBB 555555531112576/:5'$,""'  F>>555555531112576/:5'$,""'  ;44 ????(0` $  >kf5 ;11_vccgUUv6 =22_jjdRR v6 ?33`oo}}aPP v6 @44_ttɹͻ||[KK v6 bPPFzzͺzzZKK v6 }}̻{{WGG v6 xxͻxxRDD v6 pp~ͺwwPBB v6 ll~̻xxM?? v8jj~ͻ|uuI=< |Y[cP+ ff~ͺ{tsJ;5 )bcD q6 zbb~̻dk+-7fv6 w``~LPdv6 r\\~d_2P!g v6 mXX~it3X[T! gv6 kVU~emChZVZS"dq+mUJ}\^U~gWVZR!g R:Zo|gXVZT!Dd5 2T{gXW[P7f[5/e-|fZW2.cX2b0o ~h3P( !{7 1l/o$C^M\d~&v6 2l-r$Utckgf!v6 1t-o omiieev6 1l/o-T^e̺liiv6 2l/e[!^ͻfccv6 2c/26nrͺebbv6 56 6<.(~{nm̺iee v6 7*)~xllͻc`` v6 0%%~ynnͺa__v5 ,!!~shh̺ebbg* ~pggͻcaa l#~rhhͻǶ{{> ~kbb`~h``_~kbb¿`~ldd ^ B ????(@ @ ">mrH<11bv``s^^.&&M=22b|dd}}xxxcc-$$M?33b}ee||u``,##M@44bggͻ{{r^^+##MA55bii˸{{o[[*""MD77akkп˹ñzzmXX( MeRRGppĵ̺ñyyjVV' OwwᴠͺñxxhUU&Qxx̺°}uudPP-$$O}}ttп˸wllbNN$Mzz ppп˸{ss_OO#Mxx nnп˸}uu\LL"Muu kkп˸{uuYJJ!N"("ss iiп˸±zttVGG!c_ xsR' pp eeп̺ñyrsYG?&Z oBi. nn }dd}}̺ñwqs9' "\k*kk yaass̺dz4a5@ \c$v__zzս[CA  L^"~gg t^^mXR 7Y6z F^${ee p[[~~tec 8ZYZ6z Fc* xbb mWW}}~\f?[XWXZ6z Lk. u`` jUU{{wT\PvcWWWXZ6z \i'w^V nTJ}|L|ESbrbXWWXZ6z [SKKu 'EALtreYVWXZ6Bt#);@"ufYVWXZ6 r)5 9DMudXVWYY " y#668&rbWWZ7 a25>rc[8 @'Yb3 32=v? A6 , N3 32=Q RD5bjfg-# M32=bcW\jfg'!! M32=tfemֽdziff% M32=[\t̺ñhee" O32=%M"RS~ͺñgdd Q33>LFv̺°`]] O3 358E@AL}п˸OLLM3 269=8rfdп˸^\\M65 2)8v ?-k_`п˸caaMG7, 5**j^^п˸b``M@55 2''g\\п˸±b__ M=22 /$$eZZп̺ñ`__ M:// -##WLLǽ̺ñ_^^ H7-- ) F<<Ǽ̺ñ^]]t"'SII̹MLLo 2)) %]SSпʸ°JGG> /&& ZRRҿ_VVb+$$ YPPh__b)!! VNNphhb& TLLzvvb# QIIc  =66haa a  I????????(  ( 7hp@2))RnZZ}fflYY#E0((Pxaajjiihhu__#E/''Pxbbkkllzzmmfft__!E0''P{ddllnnll}eer]]"E1''Pzddmmoo~kk{ddq\\ F4**Q}ffnnqq|jjzbbo[[!F2((P~ggoorrͻı{iixaanZZ!E2))Phhppss˺ȶızhhw``lXXF4++Pjjqqvvν˹ɶIJxggu__kWW F2))Ojjrrwwƶν˺ɶIJwfft]]jVV E2))Okkssxx̾ν˺ɶIJvddr\\hTTF3**Ommttzzξ˺ɷIJuddp[[gTT F 7--Mmmuu||ξ˺ɷIJsccoZZeSSFPBB7ppww}}ν˺ɷIJsbbmXXeRRFuuzz~~ν˺ɷIJqaalWWbPPF||xxν˺ɷŲp``jUUbOOF{{ӕyyν˺ɷŲo__iTT`NNFyy7zzvvν˺ɶŲn^^gSS_MMFww8yyttν˹ɶIJl\\eRR]LLFuu5xxssν˹ɶIJk\\dQQ\LLFuu8wwrrν˹ɶıj[[bPPZIIFss5uuppν˹ɶıiZZaMMZIIFrr8ttooν˹ɶıgYY_LLXHHEqq8ssnnν˹ɶıfXX]LLVGGFpp6qqllν˹ɶIJeWW\JJTEEFnn9ppkkν˹ɶIJdVVZHHTDDFmm9oojjν˹ɶIJbTTYGGRCCF".52( ll6nnhh}}ν˹ɶŲaTTWFFQBBH$0S|"za=kk9mmgg||ν˺ɶIJ`SSVEEOAAu]~ rSZ'jj9kk~ff{{ν˺ɶIJ^QQTCCS@5@ # |Eh+hh9jj}eezzν˺ɷIJeTL>?b1+'# Yk,~gg9ii{cczzν˺ɷIJ_m730*%2& Zk,}ff7hhzbbxxν˹ɷIJr E;92+h  $ Zk,{ee9~ggxaaxxν˹ɷŲư NBA:2 ~ # Zk,{dd9|ffw``wwν˺ռ WJJC:  " Zk,ycc6{ddu__vvú _SSKD %Q? # Zk,xaa9ycct]]uu g\[UM &TZ\A # Zj+v``9xbbr\\ttped]V 'TYWW[@ " Yj,u``9vaaqZZssmhlf_ 'UYWWWW[A $ Yk,s__9u__oYYrrjbijg (TYWWWWWW[@ # Zk,r]]6s^^mXXooe]edd -\ZWWWWWWWW[A # Zk, p\\8r]]kVVppbX``_ 2ki^XWWWWWWWW[A # Yk, o[[8p[[jUUnn`S\[Z7yyng^XWWWWWWWW[@ "  Zk,mXX5oZZhTTllYOWVV<}vog^XWWWWWWWW[A #  Zh'lXX8mYYfRSqhVJRRPB}vnf^XWWWWWWWW[A $ Y[lXS8rYNYRmFHNMLG}vnf^XWWWWWWWW[@ "  F= UK[>HEIJG L}vnf^XWWWWWWWW[A  $  }a-5ADFD!R}vnf^XWWWWWWWW[@  $ T{(6z?B>h!!W~vnf^XWWWWWWWW[A  t37=@:#X~vnf^XWWWWWWWW\>& 56<=7#~vnf^XWWWWWWWZQ2".49<5$0~vnf^XWWWWWYT& ${"3T698J$+}vnf_XWWWYT&i#~S13695^ $-}vng_XWYU& ~+*'#]/2u4792_$+}vog^ZT' 220+!u#325792_ $-}voi\( ;;84/ 7G025792^ $+}zk- DCA<5 'I+ F025792_ $+y2 LLJB F\iC80(' E025792_ $-7 UURJ Mr}{{:44'& F325792_ $+< _][S W}{{:44%% F025792_ $+B gfc\_ư}{{933## E025792_ $,G djld gռIJ|{{711"" F325792_ $+L _dihpú˺ɷIJ|{{611   F025792` $,RZ`dbmν˺ɷIJ}{{500 F325792^ $+WV[_]jν˺ɷIJ}||4// F325792_ $0X PV[Xeν˺ɷIJ|{{3.. F025792_ $!!LRVSbν˺ɷŲ|{{2--F325792_$## GMRN`ν˺ɷŲ}{{1++F025794JgDIMJYν˺ɶŲ|zz/++F32569857:?EIIVν˹ɶIJ|zz-))F3 2469<=@BDFCν˹ɶIJ|zz-))F32w369<=?B@67QgVMν˹ɶı{zz,((F23S4676z.6,4c@I8)://[OOν˹ɶı|{{*'' FE6-8B779--\OOν˹ɶı{zz)&& EA558A557++[NNν˹ɶı{zz(%%  F@336?446++XLLν˹ɶIJ|zz'$$  F>229?334**XMMν˹ɶIJzzz&##  F=119=223((WKKν˹ɶIJzyy#!! F;006<111''VJJν˹ɶŲzyy#!! F://9://0%%UJJν˺ɶIJzyy! E8--99../$$TIIν˺ɶIJzxxF7,,97---##RGGν˺ɷIJyxxE5++96,,+!!QGGν˺ɷIJyyyF4**74***""NDDν˹ɷIJzyyA2))93**( OEEν˹ɷIJyxxq!1))91(('MDDν˺ɷIJyxx)/''60''%LCCξ˺ɷIJRQQi.&&9.&&#KBBξ˺ɶIJojj 7 ,%%9,$$"JAAν˹ɶŲƵ~uu P*##9+## I@@˿ν˺ P)""9)""G??˿ O(!!6(!!D<<ʿ O' 8&D==ʿO%8%D;;ʾ P$5#@88ʾ P"8"@99ɾ P!8 ?88  Q5>77ɽ  P7 =66ɽ  P5 B<<  P7 N6 8 ????????8????mayavi-4.1.0/mayavi/core/images/add.ico0000644000175100001440000000217611674464502020711 0ustar ischnellusers00000000000000h(  3f$IEmYVDl]GGV_GGYbGF[fȣʤȣȣȣȣ¥¥=d@h@h @ I:' VАR 1Q*%DtUA V"aAmq+Xݳsj֬ݡ+%b?5\ 8dAag 9^ q @R\)sD AM,g7% G7aO38fw{FVXZ^T[^>e}&@::I3д>Wh ?XZM}}N繁f Nj\a=t:-k+yĿ"sA ]yl;9IENDB`mayavi-4.1.0/mayavi/core/images/module.ico0000644000175100001440000030657611674464502021461 0ustar ischnellusers00000000000000(fh  00%%@@(BVK(   "$&''''''''%"   $-7CMV^djptx|~zuqlg`XOF;1'(9J[jz+ Oj|ŎМ!ئ#޵$%$'''''''''&%%#"ݐ}iQ4 {m_P?.  %:Ri1 cȨ#ܿ%'',27:? DT%T%e8j>h oZB,  !;Y9 x $'08Fa6ySoǯӾ޾ݽڹطʨubxMe6M;4+&"DdF) #G; $'3IqJxݼ޽޽߾ݻ׵ȤesFS"8-%JyR,  D-$h zL! %+2:@FJMRUURMJGA:3+% Qg#-MVѼصЩѪѪѪѪѪѪѪѪѪѪѪѪѪѪҫӬ֯۵ױǟn`02$Yb(&5FUan }#)9;>LMML>;9+" yncVF>'FhɟӨԮˠ̢̢̢̢̢̢̢̢̢̢̢̢̢̢̢̢̢̢̢ΥլϦ_.-d# 2Kd8Pap~}pbM7 b(g8ϣȜŘɠΥŘƚƚƚƚƚƚƚƚƚƚƚƚƚƚƚƚƚƚƚƚƚǛ͡˟[5M "CpEe1]0ŗŗɝ”˝\.ho;9o..DD[[kkzz܇݈xyjkXYEF./_g.m‘ʤ”M"z%9ax 99cc܈ut-0rT$#r! Xn 9 "[gF>Dˍ)Pcutrrrnݽwqrrrrrrrrrrrrrrrrrrrrrrsvnp7+? YeY(,q5OfmkjifڸlijjjjjjjjjjjjjjjjjjjjkmjX|@J'G$+2]![%r7xr7m4l3n3n5p6n4: $f ,ZBjX|`ۃY\ف]ځ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂ^ڂYtl)Cp7m4m4m4l4l4d*cřDKKKKJJJIGEC~@y!Hm5i2i2i2i2i2a(߽ܸٲ֬ԦР͙˓ǎŊÅzuoic`Dd/g1g1g1g1h1h1h1h2i2i2i2i2i2i2i2i2i2i2i2i2i2i2l4[()C +V*U*T*T Lvٍ2b4d5e5e5e5e4d4c3b3a2`1^0\.Z+V*U)S(R)S'T(TI(c.i3h2h2h2h2c,Q߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmgdBc/h2h2h2h2h2h2h2h2h2h2h2h2h2h2h2h2h2h2h2h2h2h2k4A#P"*U)T)S)SKw؎&Q(R)S)S)S)S)R)R(R(R(Q(Q(Q(R)R)R)R)S)S)SXd6Ji4f1f1f1f1e0d.ɯ߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmgc~@c/f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1h3[()G *S)R)Q)QIv׍%N'O(P(P(P(P(P(P(P)P)Q)Q)Q)Q)Q)Q)Q)Q)Q)Q&R,O=)a-e1d0d0d0d0[%t߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmgcv:`.d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0g2A#X&(Q'P'O'OGw֍$M&N'O'O'O'O'O'O'O'O'O'O'O'O'O'O'O'O'O'O'OTmz/Je1b.b.b.b._*o>߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmhbr8_,b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.d/Z))K (P'O'N'NFw֍$L&M'N'N'N'N'N'N'N'N'N'N'N'N'N'N'N'N'N'N'N$P/J6 *^,a/`.`.`._-['߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmh_m6],`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.`.c1B"a('O&N&M&MDvՌ#J%L&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&MQsq)Ha0]-]-]-]-V$a߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmh^h3Z+]-]-]-]-]-]-]-]-]-]-]-]-]-]-]-]-]-]-]-_.X))N 'M&L&K&KBwԋ#I%J&K&K&K&K&K&K&K&K&K&K&K&K&K&K&K&K&K&K&K&K"M0F2 +\,]-\,\,\,Z)b4ջ߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmi[c0Z+\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,_/? g* %L$K$J$JAvԋ!G#I$J$J$J$J$J$J$J$J$J$J$J$J$J$J$J$J$J$J$J$J$JNwj%G^.[+[+[+[+S"즃߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmiY`.Y*[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+[+\,V((S %J$I$H$H?v҉!E#G$H$H$H$H$H$H$H$H$H$H$H$H$H$H$H$H$H$H$H$H$HJ2A,)X*Z,Y+Y+Y+S$vM߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmjVZ+X*Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+\-?m. $G#G#F#F=wъ D"E#F#F#F#F#F#F#F#F#F#F#F#F#F#F#F#F#F#F#F#F#F#FKqcDZ,W*W*W*V(V)Ũ߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmjTU(W*W*W*W*W*W*W*W*W*W*W*W*W*W*W*W*W*W*X+S&'X #F"D"D"D;wЉA!C"D"D"D"D"D"D"D"D"D"D"D"D"D"D"D"D"D"D"D"D"D"DH##'* U)V)U)U)U)Lo߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmjRO%U)U)U)U)U)U)U)U)U)U)U)U)U)U)U)U)U)U)X+<n- "D"D"C"C:vω@!B"C"C"C"C"C"C"C"C"C"C"C"C"C"C"C"C"C"C"C"C"C"C#GiMCU*R(R(R(N$e<߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmjMM%R(R(R(R(R(R(R(R(R(R(R(R(R(R(R(R(R(S)N%%W !D B B B8vψ@A B B B B B B B B B B B B B B B B B B B B B B"F !x#( Q'Q&Q&Q&P%L!߾ݹڳ׮ըңϞ͘ʓǍň‚}xrmjIL#Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&Q&T)8i- !A A @ @6vΈ>? @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @"DjN@R(O&O&O&G~Xݹڳ׮ըңϞ͘ʓǍň‚}xrmjDJ#O&O&O&O&O&O&O&O&O&O&O&O&O&O&O&O&P'K## Y  @???5v͇<>??????????????????????!Cs' N%O%N%N%K"U-ήܹڳ׮ըңϞ͘ʓǍň‚}xrmj{AI"N%N%N%N%N%N%N%N%N%N%N%N%N%N%N%N%Q'7q1  >===3v̇;<======================!Ayi(=N&L$L$L$Dyݹڳ׮ըңϞ͘ʓǍň‚}xrmjp;G!L$L$L$L$L$L$L$L$L$L$L$L$L$L$L$M%I"#_ ==<<2vˆ9;<<<<<<<<<<<<<<<<<<<<<<? & J#J#I#I#CjF޾޻ڳ׮ըңϞ͘ʓǍň‚}xrmjk8D I#I#I#I#I#I#I#I#I#I#I#I#I#I#I#L%5o0 ;:::0vˆ89::::::::::::::::::::::= 9J%H#H#F!G"ڳ׮ըңϞ͘ʓǍň‚}xrmj_1C H#H#H#H#H#H#H#H#H#H#H#H#H#H#I$E !^~ 9888.vʅ578888888888888888888888; | ? G$G"G"F"F">ߊfڴ׮ըңϞ͘ʓǍň‚}xrmh[.AF"F"F"F"F"F"F"F"F"F"F"F"F"F"I$0m0} 8666,vɅ5566666666666666666666669 {46F#D!D!@W4Աݶ׮ըңϞ͘ʓǍň‚}xrndT+@D!D!D!D!D!D!D!D!D!D!D!D!D!E"@]} 6555*vȄ2455555555555555555555558 y4!"BCBA>뫆׮ըңϞ͘ʓǍň‚}xrnbP'?BBBBBBBBBBBBBE!-j.| 5444)uDŽ2344444444444444444444447 y43C!AA8sQدըңϞ͘ʓǍň‚}xrn^J$>AAAAAAAAAAAAB <Y{~ 3222 'vƄ/122222222222222222222225 ~x4 >@?<G&ȢܲըңϞ͘ʓǍň‚}xro\E!=????????????A*d *{~1000 %vŃ./00000000000000000000003 ~x40?<<5rըңϞ͘ʓǍň‚}xrpX=;<<<<<<<<<<<=9Ny|0/// $uŃ,.//////////////////////2 |w39<;4`?׮תңϞ͘ʓǍň‚}xrpT7;;;;;;;;;;;;=( Z (y|/... #vĂ,-......................1 |v4+;98:ۯңϞ͘ʓǍň‚}xrqQ199999999999:5Mx{-,,, vÁ)+,,,,,,,,,,,,,,,,,,,,,,/ {v4ݴ698/\ޱңϞ͘ʓǍň‚}xrpJ1888888888889$ V (wz+***u((**********************- zt4o'861L-͡զϞ͘ʓǍň‚}xrpG/666666666671Lwz)(((u%'((((((((((((((((((((((* zt4ز242.z۫ϝ͘ʓǍň‚}xrpr?,33333333335 U &ux('''u%%''''''''''''''''''''''* xr4l%42)iH٩О͘ʓǍň‚}xrpk:+2222222223,G ux '&&&u#%&&&&&&&&&&&&&&&&&&&&&&(xr4 ֯-1-: Ԣ͘ʓǍň‚}xro^2*0000000001I#tw %$$$u""$$$$$$$$$$$$$$$$$$$$$$&wr3f /.&ތfڧ͘ʓǍň‚}xroV.(......../) F tw} #"""u!""""""""""""""""""""""$wq 4ʬ*-%S6ОϚʓǍň‚}xroI'&,,,,,,,,-D#ru| "!!!u~!!!!!!!!!!!!!!!!!!!!!!#up4Y ,*+ﮂӞʓǍň‚}xslD#&+++++++,$ C ru{ !   u~                       "}uo4ǩ&* tQנʓǍň‚}xth= %))))))))@ qtz }u}   |to4U'!>&ƒ͖Ǎň‚}xtc5"&&&&&&'!z @ psy zu|  {sn3!% nӚǍň‚}xu`/"%%%%%%% 7psx y u|  y sm3H $ ]>љȎň‚}xv['!#####$ x < nqw    w t{ } ~                       y ql3   -̑ň‚}xvW! !!!!!! .vnqv } } } }v x~ | | } } } } } } } } } } } } } } } } } } } } } } ~w qk4 ?   فZҖň‚}xwQ   n: mpu | | | |t v{xyzzzzz { { { | | | | | | | | | | | | | | }v pk4   F,ȎNJ‚}xwN  ,rmqt { { { {sw}~$&$| zwsrt v xy z { { { { { { { |u pk4 7  q̍‚}xwvE     h5lns y y y yorwػϬƟjqGN$-|u ru x y y y y zt nh4  eDϐ‚}xvk?     hlor wws ot ٛЄikZ\IK=?.068353545686846FHVWde|}֏ڝݿȟuz\$dez ޖ e~j K'ghi&9EhDgBbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbAbDfFi,BmgVC m\;̂d  xIg)ggf$;YLsOwLrKpJpJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoJoKpLrOvNt@_-hff6v Ґ"uaC$e?ggbj$2KHlS|WXVUT~S}S}S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S}S}T~TVXXS}Kp4N)nbgeDzbV`pC ddgf`bm':8TFiT~Y\_bbaa`````````abb_]ZVIm:V)=!pc`eg\;F ƋI-Y7e)befda]aho )%7/G6R095AF.|Mj)0K+K;[A`>[)dc >&+c8 n~ %t!lDR b*t 3(, :NZ ^_[Q@! :DE:-LqXIľu˞ө֬եϖtąccGh/y 3N/{/BBGGDD,3wZ'ϟҤѠǔoIW(ʓ°nSȝbjjnn?A/[EoDy\ZReڳ֥bs6u:r9n3%  c΀:]G"MNh'[0ո߸Ȑt}=X(]+c/L (Te9X@#FK=5C˕zu!B 1@gDܴϙyh5G R(?(Ubv4K/69 +,7.֠vX-?G"|  ar0E&.0$ / }Y٣tH$70+\y`m-> &(.V:9Ȓv:-u r^g+7z}  -W fz- Rlgk@E*028 (z,X'S7s ^ (Nc:M%8);.@9K@T!., W J w%;5S?aCgCf=]1K!;y(9$X `]0c [ op ow it Qh_ G<tp)8>>~~<( @ ",48;;:71) .+mUl$}7!ÉD/͐M6ԑM5ՑL3ՊE,7n'W-rG$D ct{| xjTRX9쟈ڳԬΦjZ7Z {!7W##66@@CCAA:9#+LWfٱÔĖǙΠϠ}l&4JSs\tصqtuuuvq.#"mppqpyς[%r4ˢNRQOKD\&I   O9e9f:f9e+cr.T!r>۶ӤʓƄYg/j2h1f0f0i11 3 G$K$L%M%MPE!G#:A  &գ//11142 4}٧ƉrU+7>?3 Ӣ))+++. 1D>cBդȊpG$08,<| С##%%%' /s.ϐm<*/'y }Ρ  ! - wSՔj1% *mtuˠ~}yz |-$ja;$ˌh& h oxͣ˂CHAEEINR7>~t ,%* ii ]}P`=L."!() /*86E7F* NY9e \ t /H<_EiHlHmHmHmHlFjBe;Z!$lXuITag $5-D4M5P5P1J(<+ b2YYC$RcaV U*\8YCVIWGC>02W!,X|1'?(0` $  $')+++*(%" "7K[gqy~|ulaTA+ &K3 i#ʤ4ڹJ([9hGuRzUySxQuNjB^7M(<) uD ~]7 *;LZafhhe`UHPr@}\߼ݺٵձͨXS*")IDj"CU^dihb[M5>o׺ٷɝˠˡ̢ͣҧԪͣtFes"Pm22OObbppuvuwtwnpegVX5DsypƕϨƕV+w#~\fnLxpپŘpuuuuuuvwrIaj~|͇U!~BMtZ`````^XKl3A 9]MvW~^ۃ^ۃ^ۃ^ۃ^܄Yޅh-Y%l2m3ϷkBGEDAy>r8m4m4FS"!GNޖ1`:g9f9f8e6b*`R@Ij3_&{ݹ׭ПʒÅxj{>e/h1g0f0e0f0f0i2b-#F #GIܕݘH(P'P'P'O'O&O"RXU&a-vGݻ׭ОȐtes9`-c/c/c0c0c0c0f2AW#"CFڕۘC&L&M&M&M'M'M PM8B_.Y'޻׭Пɑ‚vek5\+^-^-^-^-^-`.X( E  ?@ؔٗ?$H$H$H$H$H$H#HJRL"S#[޼׭Пɑ‚vdb0W)Y*Y*Y*Y*Y*\-:T ";<֓ؖ;"D"D"D"D"D"D"DI8!8S(Z/ͯ׭Пɑ‚vbY+R'T(T(T(T(V)M#B 87Ԓ֖7 @ @ @ @ @ @ @!D !AB Ho׭Пɑ‚v^R(N%O&O&O&O&Q(1J 43ґԕ2<<<<<<< @/I,H!c=ڷٯПɑ‚wYL$J#K$K$K$L%E @ 1.Бҕ.8888888< G>B쪆ߵПɑ‚xSD E F!F!F!H#-= ~- *ΐД)44444447 K,y% ;qNܲѠɑ‚yL=@AAB :@ |) %̏Δ %00000003 Pa3E&šդȑ‚y}F4;;;=$ <}y& ʎ̓!,,,,,,,/ Oav = /ۄ`٨ɑÃys@.7780: x"ȍʒ'''''''* La% P2͝˒Ãxi:*222/pvƌȑ#######% Ka *uјÃw_5%-.%r 2t}ċǐ~!~Han_@ΕĄvT.!((]r y ‹Ďx    {Ga Ӱ0ȆtI( $ _+o {sƜ~)& {v y | ~x Fa Zތa΋s=!  Nms uėܡsuW[UXTWY\aedi:A} wt Fa D,džr1 K&i:@ck\e:C(  $&/2;@JEO#-qD^Cju${ImCV/H"9&=)@+A,B,C,C,C,C,B*A)@'>)@4L DFeAs H$e$?_HlKqJoHmHlGkGkGkGkHlImJpKpGj7R A=Y qB d` t,-C:WCdInNtNuNuMsHlAa6R*>"fQ| I-8c [MZ^dhjjjiea[THC ({+ edZ [ [ Y Pmt2????(@ @  #&()**)(&# "1@O[gpw||vpg[N>. "=Z|F mˢ&ڵ.4 ?HM"L!L M!H>4 -%݆g; mS7   AH ü2 \4~Y{ȱݽ׵ӰƢ{aoCJ*p#b5 %5DR[chlnmjd^UHCa|/qHݻ֯زٳ۶޹߻ڵҫfY, "|= ;]7IW_ejnkgbXK:h_3׳ҫȜɞɞɞɞɞʟˠΤӪͣvF< j#2s`z 55EEPPXXYYYYQRGH9:%& [$ŕȝə~/ y% q!$acqb1w˧{~~~~~~~~~~|:j dpQ!Mnnwzlnnnnnnnoppkw?!L5@}}zGN}@MQҵf]____``_]VJyؕ5VE#H#H#H#H#H#H#H#H#HL]x*5Z,W)V(ݹ׮ң͘Ǎ‚woIS&X*X*X*X*X*X*X*Y+X*, @ $G:֓4SA"D"D"D"D"D"D"D"D"D!G37J$V*L hݺ׮ң͘Ǎ‚woEM%T)T)T)T)T)T)T)W+C? #D7Ԓ4P>!A!A!A!A!A!A!A!A!A"D0M\2S)N$^6׷׮ң͘Ǎ‚wn{AK#Q'Q'Q'Q'Q'Q'R'Q') ?   A3Ӓ2N:>>>>>>>>> A. |)F!N$G쪇׮ң͘Ǎ‚wns;G M$M$M$M$M$M$P&?A ~ >0ё1K7;;;;;;;;;=,|M$,L%BuQدң͘Ǎ‚wlj7D I#I#I#I#I#J#J#& D | ;-А0I4888888888:)>E!K'ơܳң͘Ǎ‚xka2BF"F"F"F"F"H$8A z 7 (ΐ.F04444444446&"B :oң͘Ǎ‚xjX->B B B B B B !D y 4 &͐-D.2222222224$|43:]<֭ԥ͘Ǎ‚xiM&;>>>>@2? y 1 #ˏ-A+/////////1"z459:ﲊ٪͘Ǎ‚xgD!9;;;;;<w .ʏ+?(,,,,,,,,,.!w4,0{Xګ͘Ǎ‚yd=57779,> u *ȍ);$(((((((((*u4+/G*ɛϛǍ‚y^613333;~t'nj)9!%%%%%%%%%'t4%+r֠Ǎ‚zY0.//1%y = r~$ō'6 """""""""# r4&# bBҝȎ‚{T++,,+4zp|  }Č&4  p4 3̑‚{N$'(* s : oz z‹%1  o4 " ق\ѕ‚||H#$#-snx w +z } } } ~      |n4 I.Ȏă}qA !" k7lv |r=F%.&0$.)! zu wz | }ym4  rɈ|f; 'mltt s Ɲܡԏ{|ˁuwrtpsorlohmLR'/ xvvl4 hF̉{Y4 d4jpNTNJ{ЀNU%-    "),4AI\cSZ!)oj4 -~{N- fd.1iw.?(+0 2!3!3"4"4"4"4"4!3!3 21/+*;->f0u\~B&  ]1b$4Q3N4O4O4O4O4O4O4O4O4O4O4O4O4O4O4O4O4O3N8V';dtټN1{5^ewq+AFhKqKpHlGkGjFjFjFiFiFiFiFjGjGkGlJoLrHm8TX2 i g+U.hd}e(<iv 4xOm^? "e݂U𐽽3 `yyN$8,PQ utꎪO VhP #MS4(83,n6PlFqrʸl91~U0t(ܢ 0t( u| md\<΅C*؃c:zCXQx)k7ԼGΆ6fٌzTߪ(+ ʥadž"~89%1XQU{G=H@ãUOZ*ԾB/p""Ջ~T,&CaXUBc=4ut(5jUxeIY\?;ps3unΣߝEvB;uBb_C]gog 1xIENDB`mayavi-4.1.0/mayavi/core/images/modulemanager.ico0000644000175100001440000030253611674464502023004 0ustar ischnellusers00000000000000hV 00%f@@(B;(6}(   $=9H\'iH%  >h3,M-;L! [e!F-g*J+-$ZzA1X&-\(\0X- ,+bD" 4U @+MGxL3Y+)VvM(ovCI~1h-]'_h2CE~qoK3c!@ yr`k)L!!NL Ds?Y$Ild/J CC}}qqZY f~67""ÇhheeTS Wlʺ*x__oo??0& e Tc((X Qm o1X ( @  1HA$  $r"FzU2 5;&03D23bA .f^ *J5q3K44-XrP-O(1a5l3k3I3343y&X+ D s$E5l4i3f3k3I33335-IC6o3f3f3f3k2F333335 [3*$!%5i3f3f2e2h;\5 33342)aA +Wl!95f3f.b6iMQAg43341P>>qP-'J$82c,aN{h^OBy6k3734/ `{sS^< ,Gl%6=l~oZNAt5i3m3R4/ _yore0,jH!kk30kZM@s5h3f3l6i* _womppIg; )PDjYM@s4g5j4c - tRnmmntSAE!cLcWmZL?s6n-K%}ilmmp^ HEccGG 1z87UqO@q(/ ~Dmjmp_ HECC}}wwZZ !qA?G(.wip`KEoowwoollgg55 ) cn`LBII~~wwoohhggmmSS9[pY/ QQ~~vvnnggffllcc!!%{'Hb 2|N$$ww~~vvnniillGG gU 0EbFS OOzzssbb))4 -C&&Y w$lc''iiQQv^ +J)It J '^ - X%(G>!^4$?(0` $G \WQL!F$=`"4+]f \WQK@$c0'-Y(GIsjb \WMYN&13k4>30r); T zmhb [ Rj,O5m3n3:342/R"> smh_U_02d5k3f3l3:33331k*4{ Qy smd v(M6l4h3f3f3l3:3333321J49 `Wy sIp-1c5l3f3f3f3f3l3:33333333d+.m  M&hlyw&C6m4h3f3f3f3f3f3l3:333333334}.5C Vskh~/Z4i3f3f3f3f3f3f3l3:333333333,AJ6mumjho /W4i3f3f3f3f3f3f2k28233333333)BLU|upmjca]r0T4i3f3f3f3f2e1d"W QQzzuuppllggffffiillFFk$DqB$Y..yyuuppkkggffffffggssII|[*@C Qxx~~yyuuppkkggffffjjhh99w, /K&1ILf _LL~~yyttppkkffggmmWWT? /L0O0Ey)Yp9 ,>"""rryyttoojjjjhh;;3  /L0O.O.K6-G['w lKK}}ttppqqYY^C !2Q0O.O-O+N -F$]  q@0BE+%%jj}}rr??; "c1Q/Q-O+O*O+LB)K X&w pv BB##fD 1(E/R+O*O*P)O*I a t;1C\?O 0+O*O*P(P(P(LZ&M,???????(@ @KTQL G#D&?w!/$ ]ZTQLH- ?$K,g*A'FUh _XTQKrB#f0[400,N&2*hhb ^XU(LQ'53g4p3,42-c+;"<`vjeb ^XjPm.R5m3g3l3,3330|.G'-qu voieb#Z \ 33f4k3f3f3l3,33342/\-62w roiec]t*P5l4h3f3f3f3l3,3333330u0B') e Nv rogh02d5k3f3f3f3f3f3l3,333333320U/2)/ u yv s[k |'M6l4h3f3f3f3f3f3f3l3,3333333331o2<&$ X  N'[wE u,2c6k3f3f3f3f3f3f3f3f3l3,333333333322N.0Z0Bnpyd 3e5j3f3f3f3f3f3f3f3f3f3l3,3333333333321; |)kphl|` 1a4h3f3f3f3f3f3f3f3f3f3l3,333333333332.? yFtmjhl~Z 1a4h3f3f3f3f3f3f3f3f3f2l1'333333333332,A q,awnljhh >-1a3h3f3f3f3f3f3f3f3f1d7n@S523333333331)C Eo<J{wpnljE` .W2a3h3f3f3f3f3f3f2e1dAtPOCh63333333331'D 0z. @t.f~tspn ie 1"2a3h3f3f3f3f3f0c7kP[SLG~@t7;333333341$D di@!z 4N}wtsp?gx 2$2a3h3f3f3f0d0cIzaaXRLEx?s9s4S33333340"GgppS*5g 1m{ywt pk 3'2a3h3f3f-a:lgvf^XRK~Ex?r9l3k3e32333340 Ifnmrd9l+}{yw:n|4*2a3h/d-bW܀sd^WQK~Ex>r8k3f3h3l3N33340LgnmmpoM%*[ }{wq6,2a*bq8k3f3f3f3l3b3+34/Nhnmmmnra3 _#}4u7.-^_܄pc]WPJ}Dw>q8k3f3f3f3f3h3l3H4 /QhnmmmmmpnGOux:/ob]VPJ}Dw=q7j3f3f3f3f3f3f3l5c/#Qgnmmmmmmnr\, P*Qnb\VPJ}Cv=p7j3f3f3f3f3f4g6l,UoQyslmmmmmmmql@ j"}d<0x^mb\VPI|Cv=p6i3f3f3f3f5k3g3 ZY+ulmmmmmmmmot@8<{#J"la\UOI|Cv|mU-6 nn}}zzwwssookkhhffffffffffhhllOO_ F(<\08|BB~~yyvvssookkhhffffffffffhhllMMR #+Dʌ-WT,#Noo~~zzuurrookkggffffffffllaa**vI 00N0Ls0D {tK-.CC}}zzuurrookkggffffhhllKKg+v0N0O.N2/E)VdF[u`nn}}yyvvrrnnkkggffkkcc--P50O0O.N.O.K-F}]: 4#FF||yyvvrrnnjjiillMMo1| 0M2R.N.O-O,O@-F%[ iO#d{`o!!llyyuurrnnoodd//X: #)C1S.O-O,O+O+K*J | [< #89.HH||yyssttQQy6 11T.R,O+O*O*OL*G!` gM#f ~$$iiqq55a>U&C/T+O*O*P)P)K&MX;&=[9 ;0,Q*O*P)P(P(OX(JeiG #f w!G#e*O*P)P(P'P'P%'N C4I????( 6K G!F#D%A'@A)@( R'RPK G!F#D$B%A'>*A(<>&>UNLK G!FG"D#C$=%?&>(=+A, ^-XVQNLKHG!E!=&[/9.v'B':+@(9:$ B[TRQNLAKJDD(}+2f4X40)R'9*=,?)U+ E\WTRQ OMKC [-I4k3l3S333-k(>(:,?'66" G b`XWTR>QPKJ$}(1_4l3f3j3S33340*M(8+=-<$M*J c ^ \XW U~TRI_ +F4k4i3f3f3j3S333342,e(<*9.>&2w3 Lhg _ ^ \X6WWRP ~%1]5l3f3f3f3f3j3S33333330~*I)6-<-: G)Njcb _ ^ \wZXQ` (C4j4i3f3f3f3f3f3j3S333333342-^)9+8/=&/ l/Pnmecb _0 ^ ] X U#/[5l3g3f3f3f3f3f3f3j3S3333333333/x+C+6.;-7B&Spihecbs` ^Xe%@4j4i3f3f3f3f3f3f3f3f3j3S333333333332-X+6,81<$+ b.Vutkihe(dc_\".Y5l3g3f3f3f3f3f3f3f3f3f3j3S3333333333333/q+>,50:.6=%X wpokihmge^ i!=4i4j3f3f3f3f3f3f3f3f3f3f3f3j3S333333333333331-Q,4/71:#'Z,| z rpol#jifa-X6l3g3f3f3f3f3f3f3f3f3f3f3f3f3j3S3333333333333333/k,;-41:-29# v u rpohmkfn:4g5j3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j3S333333333333333331-L-30728!$R*Wv u rqolg+V6l3g3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j3S3333333333333333333/e-7/328+/|6! : n |v u^ s rls73f5j3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j3S333333333333333333331/F.11726J,{{wwY v so,T6l4g3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j3S3333333333333333333333/].40348'(R2 Cyyyw w43e5j3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j3S333333333333333333333332z/B04588 Azzzv3f5i3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j3S3333333333333333333333332.738: A{w2b4h3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j3S3333333333333333333333331-92:: EdN"I}y2b4h3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j3S3333333333333333333333331,:1;:Nlmf~z2b4h3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j3S3333333333333333333333331+;/<:Omgha{2b4h3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j3S3333333333333333333333331*=.>:Ponhgha|2b3h3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3j2Q3333333333333333333333331)>-?2Rpjihg hSbhn 2b3h3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f1h6[5233333333333333333333331(?+A 'f/V0Srrkjih9ggf\2b3h3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f2e3f@sMGg823333333333333333333340'@*@*C# G}3! Ssmmkj i{igba_2b3h3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f1d8kK~ROLHy<=3233333333333333333340%B)A+G&%|J(Uutnmmk0jjed`#2b3h3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f2e2eBuUVQNK~H|E~>Z523333333333333333340$C(B,F>@%#& ;p1Vvponmmwkjbv`92b3h3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f0c8kP\XTQNJ}GzDwBy>m61333333333333333334/#D'?OR}lU."%"n C'Wyxqpon+mmhea8 !2b3h3f3f3f3f3f3f3f3f3f3f3f3f3f1d1dDw]`ZWTPMJ}GzDwAt>s;s6M33333333333333334/"E%@YT|hpg>#"$/g/Yytsqpoponewb8!"2b3h3f3f3f3f3f3f3f3f3f3f3f3f0c9lVea]ZWTPMJ}GzDwAt>q:m7p4`3)333333333333334/!G$AXT{hlooQ* # _=&Z}|ttsq$qplgc8##2b3h3f3f3f3f3f3f3f3f3f3f1d1dJzikc`]ZVSPMJ}GzDwAt=p:m7j4j3j3C3 3333333333334/H#BYT{hlmmqd8 !"!],[~wvttskrqixd8$%2b3h3f3f3f3f3f3f3f3f3f.c:ke{qfb`]YVSPMJ}GzCv@s=p:m7j4g3f3l3[3"333333333334.I!DYT{hlmmmpnL&" W;$ \xwvt"tspke8&%2b3h3f3f3f3f3f3f3f0d/cP}~xnfb`\YVSPMJ}GzCv@s=p:m7j4g3f3f3j3i3=33333333334.K EZT{hlmmmmnq`3 T*]zyxwvbutlyf8''2b3h3f3f3f3f3f3f-b9krwnfb_\YVSPMI|FyCv@s=p:m7j4g3f3f3f3g3l3W3333333334.LGZT{hlmmmmmmpmG"  L6! {zyxwwsng8)(2b3h3f3f3f3f0d-bV~vmfb_\YVSPLI|FyCv@s=p:m6i3f3f3f3f3f3f3j3g3733333334.MH[T{hlmmmmmmmnq[/M) a}|{zyZxxqzh8*)2b3h3f3f3f,a9j~vmfb_\YVSOLI|FyCv@s=p9l6i3f3f3f3f3f3f3f3g3l3Q3333334-NI [T{hlmmmmmmmmmqjA=v2 !}|{zzwqi8,*3b3h3f0d*a[ڀ~vleb_\YUROLI|FyCv@sq;n8k5h3f3f3f3f3f3f3f3f3f3f3f4i5j&Ej UYW`NvkmmmmmmmmmmmmmmmmmmmmnsVA&~~$o92+'^C{ric`^ZWTQNK~H{ExAt>q;n8k5h3f3f3f3f3f3f3f3f3f3f5l0`)\ Z \ [nvkmmmmmmmmmmmmmmmmmmmmmoe$@&~Ds. 0%9zqhca]ZWTQNK~GzDwAt>q;n8k5h3f3f3f3f3f3f3f3f4h6k%G q\` ^cJ~vkmmmmmmmmmmmmmmmmmmmmmoc%@&~ %t22)([Jzqhc`]ZWTQNJ}GzDwAt>q;n8k4g3f3f3f3f3f3f3f6l0a,d`b`nvkmmmmmmmmmmmmmmmmmmmmmoc'@&~Le/.%:wyphc`]ZWTPMJ}GzDwAt>q;n8k4g3f3f3f3f3f4h6l%IybfdfF|vkmmmmmmmmmmmmmmmmmmmmmoc(!@&~ $&{&0()VN}pgc`]ZWTPMJ}GzDwAt>q:m7j4g3f3f3f3f5k1b.jgifnvkmmmmmmmmmmmmmmmmmmmmmod)"@&~ VX/,%9!gshc_]ZVSPMJ}GzDwAt=p:m7j4g3f3f4h5k(LilkiB{timmmmmmmmmmmmmmmmmmmmmod+$@&~ OOjj)) +(.')LPge`]YVSPMJ}GzCv@s=p:m7j4g3f5k2d0 rnplo|vhmmmmmmmmmmmmmmmmmmmmod,&@&~**kk}}yyvvGG  ^M.*%7&Y~c]YVSPMJ}GzCv@s=p:m7j4i5l*N o r rm?{jkmmmmmmmmmmmmmmmmmmod.'@&~ LL~~}}wwuussxxaa 5(-&*GS][VSPMI|FyCv@s=p:m8o3d"2y tv qpurimmmmmmmmmmmmmmmmmoe/)@&~))mm{{xxwwuussqqttoo==fA-)%7,S{YSPLI|FyCv@s=r(P!Zjkmmmmmnf;7#@&~ss~~{{yywwvvttrrqqoommkkiiggffffffffffffffllZZ""l` N;)R"Xrjmmmmnf=9%@&~HH~~}}||yywwvvttrrppoommkkiiggffffffffffffffffiihh<<l` U"<+R#Wjlmmng?;%@&~tt~~}}{{yywwvvttqqppoommkkiiggffffffffffffffffffggllUUma U" ;,R$Wpjmng@<'@&~GG~~||{{zzwwvvttrroonnmmkkiiggffffffffffffffffffffffjjff77na U" ;-Q&W{jmgA>(@&pp~~||zzyyxxvvttrrppnnmmkkiiggffffffffffffffffffffffffggllQQob U"!;/Q&WpeC@)@&%%~~||zzyywwvvttrrppnnllkkiiggffffffffffffffffffffffffffffjjff22oc V""90Q)QwCA*A&CCC~~||zzxxwwvvttrrppnnlljjiiggffffffffffffffffffffffffffffkkcc00rt`Q'Au0Q.K'h k@D+Aruu~~||zzxxvvuuttrrppnnlljjiiggffffffffffffffffffffffffhhkkNNjtx20(A1P.N/I-KVAFEG,FII~~||zzxxvvuussrrppnnlljjhhggffffffffffffffffffffffkkbb..tozj,$#90N /N.N.M.D$k uJFLAN!""zz~~||zzxxvvttssrrppnnlljjhhffffffffffffffffffffhhllOOmvzOL $:0N/N/NE.N-O.H+P]HMN1 L UQQ~~}}zzxxvvuurrqqppnnlljjhhffffffffffffffffffkkdd00yr}m1$&=0N/N/N/N.N-O,L-E s|TNTHc"&(({{~~||zzxxvvuussqqoonnlljjhhffffffffffffffhhllQQqz~UP ,~2R/N/N/N.N.NT-O,O-G(TfQUV8 !O &sc VV}}||zzxxvvuussqqoonnlljjhhffffffffffffjjee33}ur5' 81R1R/N.N.N.O-O,O+L,G{[V\Q$k#&&..||}}{{zzyyvvuussqqoonnlljjhhffffffffggllRRu|YS 8#;1R.N.N.O.O-Oa,O*O+G&XiU[^@(S &rp[[}}{{zzxxwwuussqqoommlljjhhffffffjjff55xw8* 80R0R.O.O-O,O%,O*O*L)I[V_U(q'r244||}}{{yyxxvvuussqqoommkkjjhhffggllTTy`Y 9#;0S.O-O,O,O+Oo*O)O*G#] i TY]A)V &q __}}{{yywwvvuussqqoommkkjjhhjjgg77||<- 90S/S,O,O+O+O3*O)P)L'K Z T \V*u*q?99{{{{yywwvvttssqqoommkkjjmmVV~f\ 9";.S,O+O+O+O *O)P(O)H b  kRW \B.\&qbb{{wwuuttrrqqoommoojj;;@/!;.S-S+O+O*O*O@)P(P(K&OYR[V)t,qM>>yy}}uussrrqqppss\\ k` 9 ;-S+O*O*O*P)P(P'N(IgkQU[ C/^&dd||ttqqttqq@@F3 ;-S*O*O*P*P)PL(P'P'K$S ZOXT ,|0&q\CCvvzzbb%%p f 9,S,S*P*P)P)P(P'P&O&JlnOSYC9c%$$BB I6 9 ;,T*P)P)P(P(P['P%Q&K!V Z NVS ,} 3%piv"i 9,T+T)P(P(P'P'P&Q%O$M#O"Q!WC9g$*M4 79)P(P(P'P'P'Pd&Q$Q#Q!P+| 12`{-BL(P(P'P'P'P&Q7&R$P"bP?????????mayavi-4.1.0/mayavi/core/images/add_source.png0000644000175100001440000000105411674464502022275 0ustar ischnellusers00000000000000PNG  IHDRasRGBbKGD pHYs  tIME4gIDAT8˥=hSQ~7+ .NAAH.vVı[q\ܚ\t"*h J+(R*J4{3p|ҼZXD1 "EPKW3N@(P5 P#z;xy8$vHIT15sE󎬗B\Exq.hqa)Dqd|[sؚZsa0rWtwOĔG+؟a0'e]$xGHW.^_0;3CJIUzZpkqB`40xDn|yl6=Ygco`j9b/ f)|nݓGT**@.nE;OJ׭Rt7KIENDB`mayavi-4.1.0/mayavi/core/images/image_LICENSE.txt0000644000175100001440000000157211674464502022451 0ustar ischnellusers00000000000000The icons are mostly derived work from other icons. As such they are licensed accordingly to the original license: Eclipse: Eclipse Public Licence Nuvola: LGPL Crystal: LGPL OOo: LGPL GV: Gael Varoquaux: BSD-licensed Unless stated in this file, icons are work of enthought, and are released under a 3 clause BSD-like license. Files and orginal authors: ---------------------------------------------------------------- add.ico | GV add_module.png | Crystal add_scene.png | Crystal add_source.png | Crystal file.png | GV filter.ico | Nuvola module.ico | Crystal modulemanager.ico | Crystal scene.ico | Crystal source.ico | Crystal mayavi-4.1.0/mayavi/core/images/source.ico0000644000175100001440000002335611674464502021464 0ustar ischnellusers00000000000000hF F(  ྶĺ˾˾˾˾˾˾˾˾˾˾˾˾˾˾Ÿ˾˾˾˾­ĸ˾˾˾˾íƺ˾˾˾˾˾˾˾˾˾˾˾˾˾˾Įƹ˾˾˾˾ö˾˾˾˾ɾĶ˾˾˾˾˾˾˾˾˾˾˾˾˾˾ɽƷ˾˾˾˾ʽƸ˾˾˾˾̾øø¸¸¸ùùĺõ´P񾴦øø¸¸¸ùùĺ(, qrdЁpápàoào`̀n`̀n_m^l]~l]~l]̀n^͂p`~l]~l]~l]́p_̅pb˃pb̓qbђt|mps{t|s{r{rzryqxptpspsornqnqmpnrmż¹¶¹ƽȾrwż¹¶¹ƽƽzਜ਼q|lzjxhvetdqaq`o]n\n\o]p_qasbud{k}n|mᑀp笞(0 ~~}}}|{{{{~{{{~yʾǻʶöŹǻɾʾǻʶöŹǻɾ樛᧚ᥙᤗᤖᢔᡓᡓ᠑|ថ|ថ|᠑|᠒~ᡓᢕᤗ᨜᧛᧜᫟( @ a_ŽǺøķķöºöüʿɿȽǽǼŻźƹĹĹźźŻǼǼȽȿɿöüѾϼͺ˸ʵǴǴʵ˷̹λнöjhmayavi-4.1.0/mayavi/core/api.py0000644000175100001440000000063011674464502017334 0ustar ischnellusers00000000000000 from .registry import registry from .metadata import SourceMetadata from .pipeline_info import PipelineInfo from .pipeline_base import PipelineBase from .engine import Engine from .null_engine import NullEngine from .off_screen_engine import OffScreenEngine from .module_manager import ModuleManager from .source import Source from .scene import Scene from .filter import Filter from .module import Module mayavi-4.1.0/mayavi/core/dataset_manager.py0000644000175100001440000002231211674464502021703 0ustar ischnellusers00000000000000""" Code to help with managing a TVTK data set in Pythonic ways. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. from traits.api import (HasTraits, Instance, Array, Str, Property, Dict) from tvtk.api import tvtk from tvtk.array_handler import array2vtk ###################################################################### # Utility functions. ###################################################################### def get_array_type(arr): """Returns if the array is a scalar ('scalars'), vector ('vectors') or tensor ('tensors'). It looks at the number of components to decide. If it has a wierd number of components it returns the empty string. """ n = arr.number_of_components ret = {1: 'scalars', 3: 'vectors', 4: 'scalars', 9:'tensors'} return ret.get(n) or '' def get_attribute_list(data): """ Gets scalar, vector and tensor information from the given data (either cell or point data). """ attr = {'scalars':[], 'vectors':[], 'tensors':[]} if data is not None: n = data.number_of_arrays for i in range(n): name = data.get_array_name(i) t = get_array_type(data.get_array(i)) if len(t) > 0 and name is not None: attr[t].extend([name]) def _mk_first(lst, value): """Makes the specified `value` the first item in `lst`.""" lst.remove(value) lst.insert(0, value) attr1 = attr.copy() for a in attr: v = getattr(data, a) if v is not None: name = v.name if name is not None: try: _mk_first(attr[a], v.name) except ValueError: # Sometimes we have a multi-component scalar. attr1[a].insert(0, name) return attr1 def get_all_attributes(obj): """Gets the scalar, vector and tensor attributes that are available in the given VTK data object. """ point_attr = get_attribute_list(obj.point_data) cell_attr = get_attribute_list(obj.cell_data) return point_attr, cell_attr ################################################################################ # `DatasetManager` class. ################################################################################ class DatasetManager(HasTraits): # The TVTK dataset we manage. dataset = Instance(tvtk.DataSet) # Our output, this is the dataset modified by us with different # active arrays. output = Property(Instance(tvtk.DataSet)) # The point scalars for the dataset. You may manipulate the arrays # in-place. However adding new keys in this dict will not set the # data in the `dataset` for that you must explicitly call # `add_array`. point_scalars = Dict(Str, Array) # Point vectors. point_vectors = Dict(Str, Array) # Point tensors. point_tensors = Dict(Str, Array) # The cell scalars for the dataset. cell_scalars = Dict(Str, Array) cell_vectors = Dict(Str, Array) cell_tensors = Dict(Str, Array) # This filter allows us to change the attributes of the data # object and will ensure that the pipeline is properly taken care # of. Directly setting the array in the VTK object will not do # this. _assign_attribute = Instance(tvtk.AssignAttribute, args=(), allow_none=False) ###################################################################### # Public interface. ###################################################################### def add_array(self, array, name, category='point'): """ Add an array to the dataset to specified category ('point' or 'cell'). """ assert len(array.shape) <= 2, "Only 2D arrays can be added." data = getattr(self.dataset, '%s_data'%category) if len(array.shape) == 2: assert array.shape[1] in [1, 3, 4, 9], \ "Only Nxm arrays where (m in [1,3,4,9]) are supported" va = tvtk.to_tvtk(array2vtk(array)) va.name = name data.add_array(va) mapping = {1:'scalars', 3: 'vectors', 4: 'scalars', 9: 'tensors'} dict = getattr(self, '%s_%s'%(category, mapping[array.shape[1]])) dict[name] = array else: va = tvtk.to_tvtk(array2vtk(array)) va.name = name data.add_array(va) dict = getattr(self, '%s_scalars'%(category)) dict[name] = array def remove_array(self, name, category='point'): """Remove an array by its name and optional category (point and cell). Returns the removed array. """ type = self._find_array(name, category) data = getattr(self.dataset, '%s_data'%category) data.remove_array(name) d = getattr(self, '%s_%s'%(category, type)) return d.pop(name) def rename_array(self, name1, name2, category='point'): """Rename a particular array from `name1` to `name2`. """ type = self._find_array(name1, category) data = getattr(self.dataset, '%s_data'%category) arr = data.get_array(name1) arr.name = name2 d = getattr(self, '%s_%s'%(category, type)) d[name2] = d.pop(name1) def activate(self, name, category='point'): """Make the specified array the active one. """ type = self._find_array(name, category) self._activate_data_array(type, category, name) def update(self): """Update the dataset when the arrays are changed. """ self.dataset.modified() self._assign_attribute.update() ###################################################################### # Non-public interface. ###################################################################### def _dataset_changed(self, value): self._setup_data() self._assign_attribute.input = value def _get_output(self): return self._assign_attribute.output def _setup_data(self): """Updates the arrays from what is available in the input data. """ input = self.dataset pnt_attr, cell_attr = get_all_attributes(input) self._setup_data_arrays(cell_attr, 'cell') self._setup_data_arrays(pnt_attr, 'point') def _setup_data_arrays(self, attributes, d_type): """Given the dict of the attributes from the `get_all_attributes` function and the data type (point/cell) data this will setup the object and the data. """ attrs = ['scalars', 'vectors', 'tensors'] aa = self._assign_attribute input = self.dataset data = getattr(input, '%s_data'%d_type) for attr in attrs: values = attributes[attr] # Get the arrays from VTK, create numpy arrays and setup our # traits. arrays = {} for name in values: va = data.get_array(name) npa = va.to_array() # Now test if changes to the numpy array are reflected # in the VTK array, if they are we are set, else we # have to set the VTK array back to the numpy array. if len(npa.shape) > 1: old = npa[0,0] npa[0][0] = old - 1 if abs(va[0][0] - npa[0,0]) > 1e-8: va.from_array(npa) npa[0][0] = old else: old = npa[0] npa[0] = old - 1 if abs(va[0] - npa[0]) > 1e-8: va.from_array(npa) npa[0] = old arrays[name] = npa setattr(self, '%s_%s'%(d_type, attr), arrays) def _activate_data_array(self, data_type, category, name): """Activate (or deactivate) a particular array. Given the nature of the data (scalars, vectors etc.) and the type of data (cell or points) it activates the array given by its name. Parameters: ----------- data_type: one of 'scalars', 'vectors', 'tensors' category: one of 'cell', 'point'. name: string of array name to activate. """ input = self.dataset data = None data = getattr(input, category + '_data') method = getattr(data, 'set_active_%s'%data_type) if len(name) == 0: # If the value is empty then we deactivate that attribute. method(None) else: aa = self._assign_attribute method(name) aa.assign(name, data_type.upper(), category.upper() +'_DATA') aa.update() def _find_array(self, name, category='point'): """Return information on which kind of attribute contains the specified named array in a particular category.""" types = ['scalars', 'vectors', 'tensors'] for type in types: attr = '%s_%s'%(category, type) d = getattr(self, attr) if name in d.keys(): return type raise KeyError('No %s array named %s available in dataset' %(category, name)) mayavi-4.1.0/mayavi/core/pipeline_info.py0000644000175100001440000000417511674464502021413 0ustar ischnellusers00000000000000""" Defines the class that describes the information on the inputs and outputs of an object in the pipeline. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import HasTraits, Enum, List # The list of datasets supported. DataSet = Enum('none', 'any', 'image_data', 'rectilinear_grid', 'poly_data', 'structured_grid', 'unstructured_grid') # Attribute type. AttributeType = Enum('any', 'cell', 'point', 'none') # Attribute. Attribute = Enum('any', 'none', 'scalars', 'vectors', 'tensors') ################################################################################ # Utility functions. ################################################################################ def get_tvtk_dataset_name(dataset): """Given a TVTK dataset `dataset` return the string dataset type of the dataset. """ result = 'none' if hasattr(dataset, 'is_a'): if dataset.is_a('vtkStructuredPoints') or \ dataset.is_a('vtkImageData'): result = 'image_data' elif dataset.is_a('vtkRectilinearGrid'): result = 'rectilinear_grid' elif dataset.is_a('vtkPolyData'): result = 'poly_data' elif dataset.is_a('vtkStructuredGrid'): result = 'structured_grid' elif dataset.is_a('vtkUnstructuredGrid'): result = 'unstructured_grid' else: result = 'none' else: result = 'none' return result ################################################################################ # `PipelineInfo` class. ################################################################################ class PipelineInfo(HasTraits): """ This class represents the information that a particular input or output of an object should contain. """ # The datasets supported by the object. datasets = List(DataSet) # The attribute types the object works on. attribute_types = List(AttributeType) # The attributes the object can use/produce. attributes = List(Attribute) mayavi-4.1.0/mayavi/core/traits_menu.py0000644000175100001440000001677711674464502021140 0ustar ischnellusers00000000000000""" Code related to traits UI menu items for the tree view of mayavi. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import splitext, isfile # Enthought library imports. from traits.api import HasTraits, Any, List from traitsui.menu import Action, Menu from pyface.api import FileDialog, OK, GUI # Local imports. from mayavi.core.registry import registry from mayavi.core.common import error, get_engine ################################################################################ # `MenuHelper` class. ################################################################################ class MenuHelper(HasTraits): # The object this menu generates the menus for. object = Any # The actions this helper generates. actions = List() ###################################################################### # Public interface. ###################################################################### def check_active(self, metadata): """Check if the `metadata` passed can be added to `self.object`. """ # FIXME: This should also have logic for checking the attributes # and attribute_types. output_info = self.object.output_info input_info = metadata.input_info if output_info is None: return True elif input_info is None: return True output_datasets = output_info.datasets input_datasets = input_info.datasets if 'none' in output_datasets: return False if 'any' in input_datasets: return True for d in input_datasets: if d in output_datasets: return True return False def open_file_action(self): wildcard = 'All files (*.*)|*.*' for src in registry.sources: if len(src.extensions) > 0: if wildcard.endswith('|') or \ src.wildcard.startswith('|'): wildcard += src.wildcard else: wildcard += '|' + src.wildcard dialog = FileDialog(parent=None, title='Open supported data file', action='open', wildcard=wildcard ) if dialog.open() == OK: if not isfile(dialog.path): error("File '%s' does not exist!"%dialog.path) return # FIXME: Ask for user input if a filetype is unknown and # choose appropriate reader. object = self.object engine = get_engine(object) engine.open(dialog.path, object) ###################################################################### # Non-public interface. ###################################################################### def _create_source(self, metadata, select=True): """Create source object given its metadata. If `select` is `True` make the created object the active selection. """ callable = metadata.get_callable() obj = callable() parent = self.object engine = get_engine(parent) engine.add_source(obj, parent) if select: self._make_active(obj) def _create_object(self, metadata, select=True): """Create mayavi pipeline object given its metadata. If `select` is `True` make the created object the active selection. """ callable = metadata.get_callable() obj = callable() parent = self.object engine = get_engine(parent) engine.add_filter(obj, parent) if select: self._make_active(obj) def _make_active(self, obj): """Make the object given, `obj`, the current selection of the engine.""" engine = get_engine(obj) if engine is not None: # This is required when running mayavi in envisage. GUI.set_trait_later(engine, 'current_selection', obj) else: print "No engine" def _build_source_actions(self): actions = [] a = Action(name='Open File ...', action='object.menu_helper.open_file_action', tooltip='Open a supported data file') actions.append(a) for src in registry.sources: if len(src.extensions) == 0: # The method that creates the source. setattr(self, src.id, lambda self=self, md=src, select=True: self._create_source(md, select)) a = Action(name=src.menu_name, action='object.menu_helper.'+src.id, tooltip=src.tooltip) actions.append(a) return actions def _build_filter_actions(self): actions = [] for fil in registry.filters: # The method that creates the object. setattr(self, fil.id, lambda self=self, md=fil, select=True: self._create_object(md, select)) # The method that checks if the menu can be activated or # not. setattr(self, 'check_' + fil.id, lambda self=self, md=fil: self.check_active(md)) a = Action(name=fil.menu_name, action='object.menu_helper.' + fil.id, enabled_when='object.menu_helper.check_%s()'%fil.id, tooltip=fil.tooltip) actions.append(a) return actions def _build_module_actions(self): actions = [] for mod in registry.modules: # The method that creates the module. setattr(self, mod.id, lambda self=self, md=mod, select=True: self._create_object(md, select)) # The method that checks if the menu can be activated or # not. setattr(self, 'check_' + mod.id, lambda self=self, md=mod: self.check_active(md)) a = Action(name=mod.menu_name, action='object.menu_helper.' + mod.id, enabled_when='object.menu_helper.check_%s()'%mod.id, tooltip=mod.tooltip) actions.append(a) return actions ################################################################################ # `SourceMenuHelper` class. ################################################################################ class SourceMenuHelper(MenuHelper): def _actions_default(self): actions = self._build_source_actions() return [Menu(name='Add Source', *actions)] ################################################################################ # `FilterMenuHelper` class. ################################################################################ class FilterMenuHelper(MenuHelper): def _actions_default(self): filter_actions = self._build_filter_actions() module_actions = self._build_module_actions() return [Menu(name='Add Filter', *filter_actions), Menu(name='Add Module', *module_actions)] ################################################################################ # `ModuleMenuHelper` class. ################################################################################ class ModuleMenuHelper(MenuHelper): def _actions_default(self): module_actions = self._build_module_actions() return [Menu(name='Add Module', *module_actions)] mayavi-4.1.0/mayavi/core/null_engine.py0000644000175100001440000000267111674464502021071 0ustar ischnellusers00000000000000""" A Null engine for Mayavi. The `NullEngine` class lets you create a full-fledged (almost) Mayavi engine without the need for it poping up a window. It is useful for testing or for using VTK as numerical engine. It does not allow for rendering. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. from traits.api import HasTraits, Any, Event, Callable from mayavi.core.engine import Engine def dummy_viewer_factory(): """Factory function for the dummy viewer.""" return DummyViewer() ################################################################################ # `NullEngine` class. ################################################################################ class NullEngine(Engine): """ This class represents a NullEngine which creates a DummyViewer with a scene set to None. This allows us to write full mayavi scripts without the need for a UI and this is perfect for testing, or to use Mayavi (and VTK) as a numerical engine. This engine does not allow for rendring. """ scene_factory = Callable(dummy_viewer_factory) ################################################################################ # `DummyViewer` class. ################################################################################ class DummyViewer(HasTraits): """Mimics the API of a viewer.""" scene = Any closing = Event activated = Event mayavi-4.1.0/mayavi/core/registry.py0000644000175100001440000001201511674464502020433 0ustar ischnellusers00000000000000""" A registry for engines, sources, filters and modules. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import splitext import logging # Enthought library imports. from traits.api import HasTraits, List, Instance, Dict, Str # Local imports. from mayavi.core.metadata import Metadata, import_symbol # A logger for this module. logger = logging.getLogger(__name__) ################################################################################ # `Registry` class. ################################################################################ class Registry(HasTraits): """ This class is a registry for various engines, and metadata from sources, filters and modules """ # The mayavi engines used. engines = Dict(Str, Instance('mayavi.core.engine.Engine')) # The metadata for the sources. sources = List(Metadata) # The metadata for the modules. modules = List(Metadata) # The metadata for the filters. filters = List(Metadata) ###################################################################### # `Registry` interface. ###################################################################### def register_engine(self, engine, name=''): """Registers a mayavi engine with an optional name. Note that we allow registering an engine with the same name as another already registered. """ engines = self.engines if len(name) == 0: name = '%s%d'%(engine.__class__.__name__, len(engines) + 1) logger.debug('Engine [%s] named %s registered', engine, name) engines[name] = engine def unregister_engine(self, engine_or_name): """Unregisters a mayavi engine specified either as a name or an engine instance.""" engines = self.engines if isinstance(engine_or_name, str): name = engine_or_name else: for key, engine in engines.iteritems(): if engine_or_name == engine: name = key break del engines[name] logger.debug('Engine named %s unregistered', name) def get_file_reader(self, filename): """Given a filename, find a suitable source metadata that will read the file. Returns a suitable source metadata object that will handle this. """ base, ext = splitext(filename) result = [] if len(ext) > 0: ext = ext[1:] result = [src for src in self.sources \ if ext in src.extensions] # 'result' contains list of all source metadata that can handle # the file. # If there is only single source metadata available to handle # the file, we simply return it. # If there is a conflict i.e. more then one source metadata objects # capable of handling the file then we check if they are capable of # actually reading it using 'can_read_function' which may be a class # method or a simple function which returns whether the object is # capable of reading the file or not. # Finally returns the most suitable source metadata object to the engine. If # multiple objects are still present we return the last one in the list. if len(result) > 1: for res in result[:]: if len(res.can_read_test) > 0: can_read = import_symbol(res.can_read_test)(filename) if can_read: return res else: result.remove(res) if len(result) == 0: return None return result[-1] def find_scene_engine(self, scene): """ Find the engine corresponding to a given tvtk scene. """ for engine in self.engines.values(): for s in engine.scenes: if scene is s: return engine sc = s.scene if scene is sc: return engine elif hasattr(sc, 'scene_editor') and \ scene is sc.scene_editor: # This check is needed for scene model objects. return engine else: raise TypeError, "Scene not attached to a mayavi engine." # The global registry instance. registry = Registry() # Import the metadata from the sources, modules and filters so they are # all registered. from mayavi.sources.metadata import sources registry.sources.extend(sources) from mayavi.filters.metadata import filters registry.filters.extend(filters) from mayavi.modules.metadata import modules registry.modules.extend(modules) # Do any customizations from either the `site_mayavi.py` or the # `user_mayavi.py` files. This is done by importing the customize.py # module here which in turn imports the necessary code from the users's # customization. from mayavi.core import customize mayavi-4.1.0/mayavi/core/customize.py0000644000175100001440000000721311674464502020611 0ustar ischnellusers00000000000000""" This module helps customize the mayavi install. It tries to import any `site_mayavi.py` (anywhere on `sys.path`) or `user_mayavi.py`. The `user_mayavi.py` script is found in the users `~/.mayavi2` directory and this directory is also injected into the path. It is the users responsibility to import the mayavi registry (mayavi.registry:registry) and register any new modules or filters into mayavi using suitable metadata. If the user desires to contribute any plugins then they may expose a function called `get_plugins()` which returns a list of plugins that they wish to add to the default mayavi envisage app. The user may expose one set of global plugins in the `site_mayavi` module and another in the `user_mayavi` module without any problems. The function `get_custom_plugins` returns a list of all the available custom plugins. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran, Enthought, Inc. # License: BSD Style. # Standard library imports. import sys import traceback from os.path import join, exists # Enthought library imports. from traits.util.home_directory import get_home_directory from mayavi.preferences.api import preference_manager # The functions that return the plugins. _get_global_plugins = lambda: [] _get_user_plugins = lambda: [] # First try the global mayavi customizations. try: # This will import site_mayavi, so any plugin registrations done # there will be run. from site_mayavi import get_plugins as _get_global_plugins except ImportError: pass # Now do any local user level customizations. # # The following code obtains any customizations and that are imported # from a `user_mayavi.py` provided by the user in their `~/.mayavi2` # directory. # # Note that `~/.mayavi2` is placed in `sys.path` so make sure that you # choose your module names carefully (so as not to override any common # module names). home = get_home_directory() m2dir = join(home, '.mayavi2') user_module = join(m2dir, 'user_mayavi.py') if exists(user_module): # Add ~/.mayavi2 to sys.path. sys.path.append(m2dir) # Doing an import gives user information on any errors. import user_mayavi try: # Now try and import the user defined plugin extension. from user_mayavi import get_plugins as _get_user_plugins except ImportError: # user_mayavi may not be adding any new plugins. pass # Now handle any contributions that the user has chosen via the # preferences. def _import_contrib(pkg): mod = None try: components = pkg.split('.') mod_name = '.'.join(components[:-1]) sym_name = components[-1] m = __import__(mod_name, globals(), locals(), [sym_name], level=0) mod = getattr(m, sym_name) except Exception: print "*"*80 traceback.print_exc(file=sys.stdout) print "*"*80 return mod def add_contributions(): """Import any contributions that the user has selected via preferences.""" for pkg in preference_manager.root.contrib_packages: _import_contrib(pkg + '.user_mayavi') def get_contrib_plugins(): """Get plugins requested by different contributions.""" plugins = [] for pkg in preference_manager.root.contrib_packages: mod = _import_contrib(pkg + '.user_mayavi') if mod is not None and hasattr(mod, 'get_plugins'): plugins.extend(mod.get_plugins()) return plugins # Import the contributions. add_contributions() def get_custom_plugins(): """Convenience function that returns all customization plugins as a list. """ return _get_global_plugins() + _get_user_plugins() + \ get_contrib_plugins() mayavi-4.1.0/mayavi/core/module_manager.py0000644000175100001440000003004711674464502021547 0ustar ischnellusers00000000000000"""Manages a collection of module objects. Also manages the lookup tables. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. import numpy # Enthought library imports. from traits.api import List, Instance, Trait, TraitPrefixList, \ HasTraits, Str from apptools.persistence.state_pickler import set_state # Local imports from mayavi.core.base import Base from mayavi.core.module import Module from mayavi.core.lut_manager import LUTManager from mayavi.core.common import handle_children_state, exception from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `DataAttributes` class. ###################################################################### class DataAttributes(HasTraits): """A simple helper class used to store some attributes of an input data object.""" # The version of this class. Used for persistence. __version__ = 0 # The name of the input data array. name = Str('') # The range of the data array. range = List def _get_np_arr(self, arr): data_array = arr.to_array() data_has_nan = numpy.isnan(data_array).any() return data_array, data_has_nan def compute_scalar(self, data, mode='point'): """Compute the scalar range from given VTK data array. Mode can be 'point' or 'cell'.""" if data is not None: if data.name is None or len(data.name) == 0: data.name = mode + '_scalars' self.name = data.name data_array, data_has_nan = self._get_np_arr(data) if data_has_nan: self.range = [float(numpy.nanmin(data_array)), float(numpy.nanmax(data_array))] else: self.range = list(data.range) def compute_vector(self, data, mode='point'): """Compute the vector range from given VTK data array. Mode can be 'point' or 'cell'.""" if data is not None: if data.name is None or len(data.name) == 0: data.name = mode + '_vectors' self.name = data.name data_array, data_has_nan = self._get_np_arr(data) if data_has_nan: d_mag = numpy.sqrt((data_array*data_array).sum(axis=1)) self.range = [float(numpy.nanmin(d_mag)), float(numpy.nanmax(d_mag))] else: self.range = [0.0, data.max_norm] def config_lut(self, lut_mgr): """Set the attributes of the LUTManager.""" rng = [0.0, 1.0] if len(self.range) > 0: rng = self.range lut_mgr.default_data_range = list(rng) lut_mgr.default_data_name = self.name # Constant for a ModuleManager class and it's View. LUT_DATA_MODE_TYPES = ['auto', 'point data', 'cell data'] ###################################################################### # `ModuleManager` class. ###################################################################### class ModuleManager(Base): """ The module manager node (represented as 'Colors and Legends'). """ # The source object this is connected to. source = Instance(Base) # The modules contained by this manager. children = List(Module, record=True) # The data type to use for the LUTs. Changing this setting will # change the data range and name of the lookup table/legend bar. # If set to 'auto', it automatically looks for cell and point data # with point data being preferred over cell data and chooses the # one available. If set to 'point data' it uses the input point # data for the LUT and if set to 'cell data' it uses the input # cell data. lut_data_mode = Trait('auto', TraitPrefixList(LUT_DATA_MODE_TYPES), desc='specify the data type used by the lookup tables', ) # The scalar lookup table manager. scalar_lut_manager = Instance(LUTManager, args=(), record=True) # The vector lookup table manager. vector_lut_manager = Instance(LUTManager, args=(), record=True) # The name of the ModuleManager. name = Str('Colors and legends') # The icon icon = Str('modulemanager.ico') # The human-readable type for this object type = Str(' colors and legends') # Information about what this object can consume. input_info = PipelineInfo(datasets=['any']) # Information about what this object can produce. output_info = PipelineInfo(datasets=['any']) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(ModuleManager, self).__get_pure_state__() # Source is setup dynamically, don't pickle it. d.pop('source', None) return d def __set_pure_state__(self, state): # Do everything but our kids. set_state(self, state, ignore=['children']) # Setup children. handle_children_state(self.children, state.children) # Now setup the children. set_state(self, state, first=['children'], ignore=['*']) self.update() ###################################################################### # `ModuleManager` interface ###################################################################### def update(self): """Update any internal data. This is invoked when the source changes or when there are pipeline/data changes upstream. """ self._setup_scalar_data() self._setup_vector_data() ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. """ # Do nothing if we are already running. if self.running: return # Setup event handlers. self._setup_event_handlers() # Start all our children. for obj in self.children: obj.start() for obj in (self.scalar_lut_manager, self.vector_lut_manager): obj.start() # Call parent method to set the running state. super(ModuleManager, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return # Teardown event handlers. self._teardown_event_handlers() # Stop all our children. for obj in self.children: obj.stop() for obj in (self.scalar_lut_manager, self.vector_lut_manager): obj.stop() # Call parent method to set the running state. super(ModuleManager, self).stop() def add_child(self, child): """This method intelligently adds a child to this object in the MayaVi pipeline. """ if isinstance(child, Module): self.children.append(child) else: # Ask our source to deal with it. self.source.add_child(child) def remove_child(self, child): """Remove specified child from our children. """ self.children.remove(child) ###################################################################### # `TreeNodeObject` interface ###################################################################### def tno_can_add(self, node, add_object): """ Returns whether a given object is droppable on the node. """ try: if issubclass(add_object, Module): return True except TypeError: if isinstance(add_object, Module): return True return False def tno_drop_object(self, node, dropped_object): """ Returns a droppable version of a specified object. """ if isinstance(dropped_object, Module): return dropped_object ###################################################################### # Non-public interface ###################################################################### def _children_changed(self, old, new): self._handle_children(old, new) def _children_items_changed(self, list_event): self._handle_children(list_event.removed, list_event.added) def _handle_children(self, removed, added): # Stop all the old children. for obj in removed: obj.stop() # Setup and start the new ones. for obj in added: obj.set(module_manager=self, scene=self.scene, parent=self) if self.running: # It makes sense to start children only if we are running. # If not, the children will be started when we start. try: obj.start() except: exception() def _source_changed(self): self.output_info.copy_traits(self.source.output_info) self.update() def _setup_event_handlers(self): src = self.source src.on_trait_event(self.update, 'pipeline_changed') src.on_trait_event(self.update, 'data_changed') def _teardown_event_handlers(self): src = self.source src.on_trait_event(self.update, 'pipeline_changed', remove=True) src.on_trait_event(self.update, 'data_changed', remove=True) def _scene_changed(self, value): for obj in self.children: obj.scene = value for obj in (self.scalar_lut_manager, self.vector_lut_manager): obj.scene = value def _lut_data_mode_changed(self, value): self.update() def _setup_scalar_data(self): """Computes the scalar range and an appropriate name for the lookup table.""" input = self.source.outputs[0] ps = input.point_data.scalars cs = input.cell_data.scalars data_attr = DataAttributes(name='No scalars') point_data_attr = DataAttributes(name='No scalars') point_data_attr.compute_scalar(ps, 'point') cell_data_attr = DataAttributes(name='No scalars') cell_data_attr.compute_scalar(cs, 'cell') if self.lut_data_mode == 'auto': if len(point_data_attr.range) > 0: data_attr.copy_traits(point_data_attr) elif len(cell_data_attr.range) > 0: data_attr.copy_traits(cell_data_attr) elif self.lut_data_mode == 'point data': data_attr.copy_traits(point_data_attr) elif self.lut_data_mode == 'cell data': data_attr.copy_traits(cell_data_attr) data_attr.config_lut(self.scalar_lut_manager) def _setup_vector_data(self): input = self.source.outputs[0] pv = input.point_data.vectors cv = input.cell_data.vectors data_attr = DataAttributes(name='No vectors') point_data_attr = DataAttributes(name='No vectors') point_data_attr.compute_vector(pv, 'point') cell_data_attr = DataAttributes(name='No vectors') cell_data_attr.compute_vector(cv, 'cell') if self.lut_data_mode == 'auto': if len(point_data_attr.range) > 0: data_attr.copy_traits(point_data_attr) elif len(cell_data_attr.range) > 0: data_attr.copy_traits(cell_data_attr) elif self.lut_data_mode == 'point data': data_attr.copy_traits(point_data_attr) elif self.lut_data_mode == 'cell data': data_attr.copy_traits(cell_data_attr) data_attr.config_lut(self.vector_lut_manager) def _visible_changed(self,value): for c in self.children: c.visible = value self.scalar_lut_manager.visible = value self.vector_lut_manager.visible = value super(ModuleManager,self)._visible_changed(value) def _menu_helper_default(self): from mayavi.core.traits_menu import ModuleMenuHelper return ModuleMenuHelper(object=self) mayavi-4.1.0/mayavi/core/source.py0000644000175100001440000002204511674464502020067 0ustar ischnellusers00000000000000"""The base source object from which all MayaVi sources derive. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import List, Str from apptools.persistence.state_pickler import set_state from traitsui.menu import Action from tvtk.api import write_data from apptools.scripting.api import recordable # Local imports from mayavi.core.base import Base from mayavi.core.pipeline_base import PipelineBase from mayavi.core.module import Module from mayavi.core.module_manager import ModuleManager from mayavi.core.common import handle_children_state, \ exception, error from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.adder_node import ModuleFilterAdderNode ###################################################################### # Utility functions. ###################################################################### def is_filter(object): from mayavi.core.filter import Filter return isinstance(object, Filter) ###################################################################### # `Source` class. ###################################################################### class Source(PipelineBase): """ Base class for the sources objects in the pipeline. """ # The version of this class. Used for persistence. __version__ = 0 # The children of this source in the tree view. These objects all # get the output of this source. children = List(Base, record=True) # The icon icon = 'source.ico' # The human-readable type for this object type = Str(' data source') # Information about what this object can consume. input_info = PipelineInfo(datasets=['none']) # Information about what this object can produce. output_info = PipelineInfo(datasets=['any']) # The adder node dialog class _adder_node_class = ModuleFilterAdderNode ###################################################################### # `object` interface ###################################################################### def __set_pure_state__(self, state): # Do everything but our kids. set_state(self, state, ignore=['children']) # Setup children. handle_children_state(self.children, state.children) # Now setup the children. set_state(self, state, first=['children'], ignore=['*']) ###################################################################### # `Source` interface ###################################################################### def add_module(self, module): """ Adds a module smartly. If no ModuleManager instances are children, it first creates a new ModuleManager and then adds the module to it. If not it adds the module to the first available ModuleManager instance.""" mm = None for child in self.children: if isinstance(child, ModuleManager): mm = child if mm is None: mm = ModuleManager(source=self, scene=self.scene) if self.running: mm.start() self.children.append(mm) if self.recorder is not None: index = len(self.children) - 1 self.recorder.register(mm, parent=self, trait_name_on_parent='children[%d]'%index) mm.children.append(module) @recordable def save_output(self, fname): """Save our output (by default the first of our outputs) to the specified filename as a VTK file. Both old style and new style XML files are supported. """ if len(self.outputs) > 0: write_data(self.outputs[0], fname) else: error('Object has no outputs to save!') ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. """ # Do nothing if we are already running. if self.running: return # Start all our children. for obj in self.children: try: obj.start() except: exception() # Call parent method to set the running state. super(Source, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return # Stop all our children. for obj in self.children: obj.stop() # Call parent method to set the running state. super(Source, self).stop() def add_child(self, child): """This method intelligently adds a child to this object in the MayaVi pipeline. """ if is_filter(child): # It is a Filter, so append to children. self.children.append(child) elif isinstance(child, Source): # A non-filter source object. This should be added to the # scene. self.parent.add_child(child) elif isinstance(child, Module): # Modules should be added carefully via add_module. self.add_module(child) elif isinstance(child, ModuleManager): self.children.append(child) else: self.children.append(child) def remove_child(self, child): """Remove specified child from our children. """ self.children.remove(child) ###################################################################### # `TreeNodeObject` interface ###################################################################### def tno_can_add(self, node, add_object): """ Returns whether a given object is droppable on the node. """ from mayavi.core.filter import Filter try: if issubclass(add_object, Filter) or \ issubclass(add_object, ModuleManager): return True except TypeError: if isinstance(add_object, Filter) or \ isinstance(add_object, ModuleManager): return True return False def tno_drop_object(self, node, dropped_object): """ Returns a droppable version of a specified object. """ if is_filter(dropped_object) or \ isinstance(dropped_object, ModuleManager): return dropped_object ###################################################################### # Non-public interface ###################################################################### def _children_changed(self, old, new): self._handle_children(old, new) def _children_items_changed(self, list_event): self._handle_children(list_event.removed, list_event.added) def _handle_children(self, removed, added): # Stop all the removed children. for obj in removed: obj.stop() # Process the new objects. for obj in added: obj.set(scene=self.scene, parent=self) if isinstance(obj, ModuleManager): obj.source = self elif is_filter(obj): obj.inputs.append(self) if self.running: try: obj.start() except: exception() def _scene_changed(self, old, new): super(Source, self)._scene_changed(old, new) for obj in self.children: obj.scene = new def _visible_changed(self,value): for c in self.children: c.visible = value super(Source,self)._visible_changed(value) def _menu_helper_default(self): from mayavi.core.traits_menu import FilterMenuHelper return FilterMenuHelper(object=self) def _extra_menu_items(self): """Return a save output menu action.""" save_output = Action(name='Save output to file', action='object._save_output_action', enabled_when='len(object.outputs) > 0') return [save_output] def _save_output_action(self): """Pops up a dialog box for the action to ask for a file.""" # FIXME: in a refactor this should all go in a separate view # related object. from pyface.api import FileDialog, OK wildcard = 'All files (*.*)|*.*|'\ 'VTK XML files (*.xml)|*.xml|'\ 'Image Data (*.vti)|*.vti|'\ 'Poly Data (*.vtp)|*.vtp|'\ 'Rectilinear Grid (*.vtr)|*.vtr|'\ 'Structured Grid (*.vts)|*.vts|'\ 'Unstructured Grid (*.vtu)|*.vtu|'\ 'Old-style VTK files (*.vtk)|*.vtk' dialog = FileDialog(title='Save output to file', action='save as', wildcard=wildcard ) if dialog.open() == OK: self.save_output(dialog.path) mayavi-4.1.0/mayavi/core/mouse_pick_dispatcher.py0000644000175100001440000001626211674464502023137 0ustar ischnellusers00000000000000""" An object to register callbacks and dispatch event wiring mouse clicks on a scene to picking. """ # ETS imports from traits.api import HasTraits, Dict, Instance, \ Enum, Int, Callable, on_trait_change, List, Tuple from mayavi.core.scene import Scene from tvtk.api import tvtk VTK_VERSION = tvtk.Version().vtk_major_version \ + .1*tvtk.Version().vtk_minor_version ################################################################################ # class `MousePickDispatcher` ################################################################################ class MousePickDispatcher(HasTraits): """ An event dispatcher to send pick event on mouse clicks. This objects wires VTK observers so that picking callbacks can be bound to mouse click without movement. The object deals with adding and removing the VTK-level callbacks. """ # The scene events are wired to. scene = Instance(Scene) # The list of callbacks, with the picker type they should be using, # and the mouse button that triggers them. callbacks = List(Tuple( Callable, Enum('cell', 'point', 'world'), Enum('Left', 'Middle', 'Right'), ), help="The list of callbacks, with the picker type they " "should be using, and the mouse button that " "triggers them. The callback is passed " "as an argument the tvtk picker." ) #-------------------------------------------------------------------------- # Private traits #-------------------------------------------------------------------------- # Whether the mouse has moved after the button press _mouse_no_mvt = Int # The button that has been pressed _current_button = Enum('Left', 'Middle', 'Right') # The various picker that are used when the mouse is pressed _active_pickers = Dict # The VTK callback numbers corresponding to our callbacks _picker_callback_nbs = Dict(value_trait=Int) # The VTK callback numbers corresponding to mouse movement _mouse_mvt_callback_nb = Int # The VTK callback numbers corresponding to mouse press _mouse_press_callback_nbs = Dict # The VTK callback numbers corresponding to mouse release _mouse_release_callback_nbs = Dict #-------------------------------------------------------------------------- # Callbacks management #-------------------------------------------------------------------------- @on_trait_change('callbacks_items') def dispatch_callbacks_change(self, name, trait_list_event): for item in trait_list_event.added: self.callback_added(item) for item in trait_list_event.removed: self.callback_removed(item) def callback_added(self, item): """ Wire up the different VTK callbacks. """ callback, type, button = item picker = getattr(self.scene.scene.picker, '%spicker' % type) self._active_pickers[type] = picker # Register the pick callback if not type in self._picker_callback_nbs: self._picker_callback_nbs[type] = \ picker.add_observer("EndPickEvent", self.on_pick) # Register the callbacks on the scene interactor if VTK_VERSION>5: move_event = "RenderEvent" else: move_event = 'MouseMoveEvent' if not self._mouse_mvt_callback_nb: self._mouse_mvt_callback_nb = \ self.scene.scene.interactor.add_observer(move_event, self.on_mouse_move) if not button in self._mouse_press_callback_nbs: self._mouse_press_callback_nbs[button] = \ self.scene.scene.interactor.add_observer( '%sButtonPressEvent' % button, self.on_button_press) if VTK_VERSION>5: release_event = "EndInteractionEvent" else: release_event = '%sButtonReleaseEvent' % button if not button in self._mouse_release_callback_nbs: self._mouse_release_callback_nbs[button] = \ self.scene.scene.interactor.add_observer( release_event, self.on_button_release) def callback_removed(self, item): """ Clean up the unecessary VTK callbacks. """ callback, type, button = item # If the picker is no longer needed, clean up its observers. if not [t for c, t, b in self.callbacks if t == type]: picker = self._active_pickers[type] picker.remove_observer(self._picker_callback_nbs[type]) del self._active_pickers[type] # If there are no longer callbacks on the button, clean up # the corresponding observers. if not [b for c, t, b in self.callbacks if b == button]: self.scene.scene.interactor.remove_observer( self._mouse_press_callback_nbs[button]) self.scene.scene.interactor.remove_observer( self._mouse_release_callback_nbs[button]) if len(self.callbacks) == 0 and self._mouse_mvt_callback_nb: self.scene.scene.interactor.remove_observer( self._mouse_mvt_callback_nb) self._mouse_mvt_callback_nb = 0 def clear_callbacks(self): while self.callbacks: self.callbacks.pop() #-------------------------------------------------------------------------- # Mouse movement dispatch mechanism #-------------------------------------------------------------------------- def on_button_press(self, vtk_picker, event): self._current_button = event[:-len('ButtonPressEvent')] self._mouse_no_mvt = 2 def on_mouse_move(self, vtk_picker, event): if self._mouse_no_mvt: self._mouse_no_mvt -= 1 def on_button_release(self, vtk_picker, event): """ If the mouse has not moved, pick with our pickers. """ if self._mouse_no_mvt: x, y = vtk_picker.GetEventPosition() for picker in self._active_pickers.values(): picker.pick((x, y, 0), self.scene.scene.renderer) self._mouse_no_mvt = 0 def on_pick(self, vtk_picker, event): """ Dispatch the pick to the callback associated with the corresponding mouse button. """ picker = tvtk.to_tvtk(vtk_picker) for event_type, event_picker in self._active_pickers.iteritems(): if picker is event_picker: for callback, type, button in self.callbacks: if ( type == event_type and button == self._current_button): callback(picker) break #-------------------------------------------------------------------------- # Private methods #-------------------------------------------------------------------------- def __del__(self): self.clear_callbacks() mayavi-4.1.0/mayavi/core/adder_node.py0000644000175100001440000003032011674464502020646 0ustar ischnellusers00000000000000""" Custom nodes for a Tree Editor that provide views for adding various nodes to the tree. """ # Authors: Judah De Paula # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import (HasTraits, Str, Property, Any, Button, List, Instance, ToolbarButton) from traitsui.api import View, Item, Group, \ TextEditor, TreeEditor, TreeNode, ListEditor from pyface.api import ImageResource # Local imports. from mayavi.core.registry import registry ############################################################################### # AdderNode class ############################################################################### class AdderNode(HasTraits): """ Base class that will display a TreeNode to add items to the tree. """ # String to be shown in the TreeEditor. label = Str('Base AdderNode') # Default tooltip for this class. tooltip = Str('Add an item') # The parent object that should be manipulated for adding children. object = Any # Duck-typing is necessary since Mayavi assumes nodes always have scenes. scene = Property # Trait view to show in the Mayavi current object panel. view = View(Group(label='AdderNode')) def dialog_view(self): """ View shown by double-clicking on the node. Same as in Base(). """ view = self.trait_view() view.buttons = [ ] view.title = self.label view.icon = ImageResource('add.ico') view.resizable = True view.width = 350 view.height = 650 return view def _get_scene(self): """ Trait Property getter for 'scene'. """ object = self.object if isinstance(object, AdderNode): return None if object is not None: return object.scene else: return None ############################################################################### # SceneAdderNode class ############################################################################### class SceneAdderNode(AdderNode): """ Subclass for adding Scene nodes to a Mayavi Engine node. """ # String to be shown in the TreeEditor. label = Str('Add a new scene') # Button for the View. add_scene = Button('Add a new scene', image=ImageResource('add_scene.png')) # Trait view to show in the Mayavi current object panel. view = View(Group(Item('add_scene', show_label=False, style='custom'), label='Add a scene')) def _add_scene_fired(self): """ Trait handler for when the add_scene button is clicked. """ self.object.new_scene() ############################################################################### # DocumentedItem class ############################################################################### class DocumentedItem(HasTraits): """ Container to hold a name and a documentation for an action. """ # Name of the action name = Str # Button to trigger the action add = ToolbarButton('Add', orientation='horizontal', image=ImageResource('add.ico')) # Object the action will apply on object = Any # Two lines documentation for the action documentation = Str view = View('_', Item('add', style='custom', show_label=False), Item('documentation', style='readonly', editor=TextEditor(multi_line=True), resizable=True, show_label=False), ) def _add_fired(self): """ Trait handler for when the add_source button is clicked in one of the sub objects in the list. """ action = getattr(self.object.menu_helper, self.id) action() def documented_item_factory(name='', documentation='', id='', object=None): """ Factory for creating a DocumentedItem with the right button label. """ documentation = documentation.replace('\n', '') documentation = documentation.replace(' ', '') class MyDocumentedItem(DocumentedItem): add = ToolbarButton('%s' % name, orientation='horizontal', image=ImageResource('add.ico')) return MyDocumentedItem( name=name, documentation=documentation, id=id, object=object) ############################################################################### # ListAdderNode class ############################################################################### class ListAdderNode(AdderNode): """ A node for adding object, with a list of objects to add generated from the registry. """ # The list of items to display to the user. items_list = List(DocumentedItem) # A reference to the registry, to generate this list. items_list_source = List() # Selected item selected_item = Instance(DocumentedItem) # A reference to self, to allow to build the tree view. self = Instance(AdderNode) # The icon of the displayed objects icon_name = Str('add.ico') def _self_default(self): return self def default_traits_view(self): nodes = [TreeNode(node_for=[AdderNode], label='name', copy=False, delete=False, rename=False, children='items_list', ), TreeNode(node_for=[DocumentedItem], label='name', copy=False, delete=False, rename=False, icon_item=self.icon_name, ), ] tree_editor = TreeEditor(editable=False, hide_root=True, orientation='vertical', selected='object.selected_item', nodes=nodes, on_dclick='object._on_tree_dclick', ) view = View(Item('self', show_label=False, editor=tree_editor, resizable=True, springy=True, height=0.5), Item('selected_item', style='custom', show_label=False, height=0.5), resizable=True) return view def _object_changed(self, value): """ Trait handler for when the self.object trait changes. """ result = [] if value is not None: # Don't need 'x', but do need to generate the actions. x = value.menu_helper.actions for src in self.items_list_source: if not self._is_action_suitable(value, src): continue name = src.menu_name.replace('&','') result.append( documented_item_factory( name=name, documentation=src.help, id=src.id, object=value) ) self.items_list = result def _is_action_suitable(self, object, src): """ Check that the action described by src can be applied on the given object. """ if hasattr(object.menu_helper, 'check_%s' % src.id) \ and getattr(object.menu_helper, 'check_%s' % src.id)(): return True else: return False def _on_tree_dclick(self, object): """ Called when an user double clicks on an item in the tree view. """ object._add_fired() ############################################################################### # SourceAdderNode class ############################################################################### class SourceAdderNode(ListAdderNode): """ Tree node that presents a view to the user to add a scene source. """ # Button for adding a data file, with automatic format checking. open_file = ToolbarButton('Load data from file', orientation='horizontal', image=ImageResource('file.png')) # A reference to the registry, to generate this list. items_list_source = [source for source in registry.sources if len(source.extensions) == 0] # The string to display on the icon in the TreeEditor. label = 'Add Data Source' # The icon of the displayed objects icon_name = Str('source.ico') # Trait view to show in the Mayavi current object panel. def default_traits_view(self): return View(Group(Group(Item('open_file', style='custom'), show_labels=False, show_border=False), Item('items_list', style='readonly', editor=ListEditor(style='custom')), show_labels=False, label='Add a data source')) def _open_file_fired(self): """ Trait handler for when the open_file button is clicked. """ self.object.menu_helper.open_file_action() def _is_action_suitable(self, object, src): return True ############################################################################### # ModuleAdderNode class ############################################################################### class ModuleAdderNode(ListAdderNode): """ Tree node that presents a view to the user to add modules. """ # String to be shown in the TreeEditor. label = Str('Add a visualization module') # The icon of the displayed objects icon_name = Str('module.ico') # A reference to the registry, to generate this list. items_list_source = registry.modules def _object_changed(self, value): if value is not None: value.menu_helper._build_filter_actions() ListAdderNode._object_changed(self, value) ############################################################################### # FilterAdderNode class ############################################################################### class FilterAdderNode(ListAdderNode): """ Tree node that presents a view to the user to add filters. """ # String to be shown in the TreeEditor. label = Str('Add a processing filter') # The icon of the displayed objects icon_name = Str('filter.ico') # A reference to the registry, to generate this list. items_list_source = registry.filters ############################################################################### # ModuleFilterAdderNode class ############################################################################### class ModuleFilterAdderNode(AdderNode): """ Tree node that presents a view to the user to add filter and modules. """ # The string to display on the icon in the TreeEditor. label = 'Add module or filter' # An adder node for modules modules = Instance(ModuleAdderNode, ()) # An adder node for filters filters = Instance(FilterAdderNode, ()) def _object_changed(self): """ Propagate the object to the sub nodes. """ self.filters.object = self.object self.modules.object = self.object # Trait view to show in the Mayavi current object panel. view = View( Group(Item('modules', style='custom', springy=True, resizable=True, height=1., ), show_labels=False, label='Visualization modules'), Group(Item('filters', style='custom', springy=True, resizable=True, height=1., ), show_labels=False, label='Processing filters'), ) ### EOF ####################################################################### mayavi-4.1.0/mayavi/core/metadata.py0000644000175100001440000001055211674464502020347 0ustar ischnellusers00000000000000""" The definition of different kinds of metadata that is put into the mayavi registry """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import HasTraits, Str, Callable, Either, List, Instance from mayavi.core.pipeline_info import PipelineInfo ################################################################################ # Utility functions. ################################################################################ def import_symbol(symbol_path): """ Import the symbol defined by the specified symbol path. Copied from envisage's import manager. """ if ':' in symbol_path: module_name, symbol_name = symbol_path.split(':') module = import_module(module_name) symbol = eval(symbol_name, module.__dict__) else: components = symbol_path.split('.') module_name = '.'.join(components[:-1]) symbol_name = components[-1] module = __import__( module_name, globals(), locals(), [symbol_name] ) symbol = getattr(module, symbol_name) return symbol def import_module(module_name): """This imports the given module name. This code is copied from envisage's import manager! """ module = __import__(module_name) components = module_name.split('.') for component in components[1:]: module = getattr(module, component) return module ################################################################################ # `Metadata` class. ################################################################################ class Metadata(HasTraits): """ This class allows us to define metadata related to mayavi's sources, filters and modules. """ # Our ID. id = Str # The class that implements the module/filter/source. class_name = Str # The factory that implements the object overrides the class_name if # not the empty string. The callable will be given the filename to # open along with the engine (for sources). factory = Either(Str, Callable, allow_none=False) # Description of the object being described. desc = Str # Help string for the object. help = Str # The name of this object in a menu. menu_name = Str # The optional tooltip to display for this object. tooltip = Str # Information about what this object can consume. input_info = Instance(PipelineInfo) # Information about what this object can produce. output_info = Instance(PipelineInfo) ###################################################################### # Metadata interface. ###################################################################### def get_callable(self): """Return the callable that will create a new instance of the object implementing this metadata. """ factory = self.factory if factory is not None: if callable(factory): symbol = factory elif isinstance(factory, str) and len(factory) > 0: symbol = import_symbol(factory) else: symbol = import_symbol(self.class_name) else: symbol = import_symbol(self.class_name) return symbol ################################################################################ # `ModuleMetadata` class. ################################################################################ class ModuleMetadata(Metadata): pass ################################################################################ # `FilterMetadata` class. ################################################################################ class FilterMetadata(Metadata): pass ################################################################################ # `SourceMetadata` class. ################################################################################ class SourceMetadata(Metadata): # The file name extension that this reader/source handles. Empty if # it does not read a file. extensions = List(Str) # Wildcard for the file dialog. wildcard = Str # `Callable` to check if the reader can actually read the file # `Callable` must accept the filename to be read and it should # return `True` if its possible to read the file else 'False' can_read_test = Str mayavi-4.1.0/mayavi/core/lut/0000755000175100001440000000000011674464502017016 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/core/lut/gray.gif0000644000175100001440000000312611674464502020451 0ustar ischnellusers00000000000000GIF89aP& !!!$$$%%%(((+++---...111333444777:::<<<===>>>???@@@AAADDDEEEFFFGGGIIIJJJMMMQQQRRRTTTWWWZZZ[[[]]]___```aaacccdddgggjjjmmmpppssswwwzzz}}}!,P&(p@&TAÆ@QE .^1 ;xH!C1D&NHB,Yl2fФQ 8rɳ?4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫+w@APH*P'@\d{PԐ`G!TLHrB4Aam@p@]PB!$p! &@ cG1<um-1?D}DF@F+Ekm#Lo[kE6 0qp r|2xQxYl0l|[H1砇.;mayavi-4.1.0/mayavi/core/lut/gist_yarg.gif0000644000175100001440000000331411674464502021476 0ustar ischnellusers00000000000000GIF89aP& !!!"""%%%((()))+++,,,---///222333555666777888:::<<<===>>>???@@@AAABBBCCCDDDEEEFFFHHHIIIKKKNNNRRRTTTXXX[[[\\\^^^___aaacccdddgggkkknnnpppqqqssswwwzzz{{{~~~!,P&#9j(!BO:ra2cĀE +TD򤉒$H!2D>zqF 3dhE N+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 ۦBn^[ Hqo@,^zpAɻ.@ɾ"ܮTqÿRi0jLPE|$r `B "{ "ɔ4@T0Rך @12"m4 $ 6:z@%NC$GRh؃ MI#apoK/% #SB; k8|% ȶW%7{:R1 P sP*l;HT %^8@;@ >H_ `@ w{\p ;mayavi-4.1.0/mayavi/core/lut/reds.gif0000644000175100001440000000314411674464502020444 0ustar ischnellusers00000000000000GIF89aP& """%%%,,,///222333555666888999:::;;;???k qw}CCCFFFHHHIIIZZZ\\\___jjjkkkmmmsssttt{{{}}}    #"'#+%.&2'5)8+=-A0E3J6O9T;X?]BaEfHlLpPtTxX|\`dhlptx}ĬDZ˶λ!,P&5et#G-R!>{sN:sā#& /^p٢% +VP"% 'N0Y$ #F"$>zء#6jИ!c 4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 A ߾+\P/\|-|2J$@ph@$"ph@ @,+ /$2JP LbX'5 J`70Jk$l4m.d(}TB  ].T0D .P|0 $`o1M.c`'+0P@ "  a/$' P@8n騧ꬷ:;mayavi-4.1.0/mayavi/core/lut/cool.gif0000644000175100001440000000220711674464502020442 0ustar ischnellusers00000000000000GIF89aP&} !!!666777AAABBBiiinnnooosssvvvwwwzzz '!$+.714:>GADJMWPTZ]gc`jmspvz}w~ztpgmjdaW^ZTQGNKDA;>841+.(%! !,P&hgfdecba_`^]\Z[YXWUVTSPRQONKMLJIFHGEDACB@?><=;:7896524310-/.,+(*)'&#%$" !҅&UiSOF:jUWfպkW_;lYg[m \sMlg^y}'Zk f\q =L']wm_i&[o-,]u]^{ jRWFźTjVS6iUn֒A,eҲ1mΪq{%Q,-MlqR+I'~]yt˥gg>]{s۝WG.жF6; &nw1{! ߅5O3mIK9o™]'yv^ǣq`̭ GWb՘h%i vkͅXg\vǠsUg}*{9]x HqסuJ7]z-؜wfԕg߁i >H}TiXf\v`)dihlY&)p)'$@qG P~*(ul@Rj)*qe1 AѩiW0Ї|뫰 jW2@6Ї,im}`)jdmF;P@r p.k/;mayavi-4.1.0/mayavi/core/lut/purples.gif0000644000175100001440000000322111674464502021175 0ustar ischnellusers00000000000000GIF89aP& """###%%%///555666777888999:::>>>???@~BCCCEEEFFFIIILLLQQQXXX[[[\\\___bbbiiijjjlllmmmpppsss{{{|||}}}D FHJLNP!S%U)W-Y1[5]:`>bBdFfJhNkSmWo[q`sduhxmzq|v~z}ÖęŜƟɢ˥ͩϬЮӱմ׷ٻ۽!,P&S"5JT(O2adI*Dh@O8oܴaFM4g̔!3FL,XDI&K$ArH"CG;tqF4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 $@B#.B% ۊ&Њ0p+.Ҋp{̭ۅ# P)LTh D` rHRD@C# A+ P,`*K* 0K@+[$\xqJ#H["N;0l'^zc˷0 ݂01 LAbr_;y_80A7,/  "2lpG꾎i3G/;mayavi-4.1.0/mayavi/core/lut/bugn.gif0000644000175100001440000000311011674464502020433 0ustar ischnellusers00000000000000GIF89aP& ###&&&555666888:::>>>???GJOS!W#[$_&c'g)k+n-r0 u2 w5z7}9DDDHHHOOOUUU[[[pppsssxxx<?A!C$G'L*Q.V0[3`6d9iN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 @ܖA Gb.$T@ۦa <6 ɿ(@P*p@!R8D _lq d;p)LTlz,4o#NAʏ3E @2 S]u$S<,'|6(@l@2MLސG.Wn嘛;mayavi-4.1.0/mayavi/core/lut/pastel2.gif0000644000175100001440000000325411674464502021063 0ustar ischnellusers00000000000000GIF89aP& """$$$%%%---///333555666888:::???@@@AAABBBCCCFFFIIIKKKLLLPPPQQQXXX[[[\\\dddjjjkkkmmmsss{{{|||}}}׽պٿήͭϼζαԷҴбȶ!,P&|Y#N@I %z3ΥJ0eҴN>J @$ETE1¥ -YDBe+U BH?~M5h4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 ,p)b $B)~+5n/&m©,v@!Kx/,00 Ȓpp& ȢPEЁT+: H_ЌoҮ Ֆ Kd+&<\AA9w F\ 9~o/<_ (%$piC! @)Q8/o;mayavi-4.1.0/mayavi/core/lut/ylorbr.gif0000644000175100001440000000317211674464502021021 0ustar ischnellusers00000000000000GIF89aP& &&&***+++,,,---///111222555777:::>>>i%n's(x*},CCCEEEHHHSSSVVVXXXxxxyyy-.0135:8<>ACFHJMQTY\ _ c fjnqvy~!#%'+/37:>BEJMRZ`fmszہއ!,P&#%BtP!Bљ#'7nڰY& 3Z`b )R@y %J 9b!B:ra2bx*R8 4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫슛LR$A k}Pm pqo&| 0"P>| X`0P0vPA%@՚ 1 %$PG@P5gT F#Ec%8 uvq><Tk  Iw X2>>???1m4r8w;{CCCFFFIII\\\jjjmmmpppsssxxx{{{}}}>BEHLO R UX[^behkn!q%u)y,|/259N+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 ڻIXP/ \&0&%(@ t"B 0'YBۛTT @^Aw pH$DT@E$0 &W@͝pb2Љ EK$ u&`+#@ da'0<lЉQ. oрqk !"d0᠇.褗n騟;mayavi-4.1.0/mayavi/core/lut/spring.gif0000644000175100001440000000315711674464502021015 0ustar ischnellusers00000000000000GIF89aP& %%%+++///555666777888999:::<<<>>>???CCCEEEIII[[[\\\___aaabbbccciiilllsss !%+(.14;8>GADKNWQTZ^gadjmwptz}}zsvpgmjd`W]ZTQGMJDA7>:41'+.$! !,P&S9ryf 6jҘAs 0cĀK-ZdbJ)R@i %H$9bdH"B:rq1dxE*4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫-+ˈ$X/\0X!\|[oI"`0 ~0`r0)(@!H< @AT &/@4'r$ <(0p(>|L~r(0p%9;l4'| v$H00O P, As_; A#"+x_8:L0>>???H7K;O?SBWEZI^LbOfSjWl[n_qdsiunwsyx{}DDDHHHLLLOOOQQQUUUXXX[[[pppsssxxx|||} "(,27xa 4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 (B ۾m@ KDb. &h۶a PK0&&$$'ck`n4pE W; 2X : hH +AScBL5LLgko 0Hʗp%S;"պ7ge[@I zcBr>81pÅݷB2}IT* E|O.yX4nIo(pYO.o';mayavi-4.1.0/mayavi/core/lut/file.gif0000644000175100001440000000246011674464502020426 0ustar ischnellusers00000000000000GIF89aP$!!!777>>>DDDLLLSSSVVVPSXSW]WZ_XXXZZZ\\\^^^GOaGPeEQiFPjFQkGQlGRlOWfHRkHRmHSlHSmITmQYgWZ`TZeV\gT\iT\jV^l[^cY_k_bgX`oYan[bm]bi[cq[du\dq\esYe{[e~]h~eeegggadjcfkaelgjolllknsppptttyyy~~~GcGdHcHdKfJgKgKhMjNjMjNjNkWkXkQmPmYqQnSpTpTqVsVsWt[wYvZvZw_x\y\z]z_}bjblgo`x`ybzc{d}e~fxgbehijkmmhokoostqrstuvnqwtxzz||~ɂĀʁ˂̃̈́ψĈŎҎӞРҡգ֥٧۩ݳ!,P$ HdᆒÇPŋ3^PKV7laHI`!Fm˗0ɛ8 ~peh۷g@ V+9ਐBҫ?0ғMFVEAlP%kQرp~88ixVWq~ǚ[!V"KEy|)^|0`lySĈԁsyȆjQ6Y„=]`'qΒ3CV =h\8c:UH̘_,aJ֯j# edžKe9P!X m(+*I MNџqą' )pIHI1!IiB aB)t%8Q'}! DI@74~XcR4\P[VpfL)di)`@9&b0N9 'V pv>@,̝'ih9 R9 @I%EڦN4i96A8LFzHPͪj뭸j]@;mayavi-4.1.0/mayavi/core/lut/gist_earth.gif0000644000175100001440000000330111674464502021633 0ustar ischnellusers00000000000000GIF89aP& 5"""%%%+++---///333555666777888:::<<Z;a9e6m8i4q3u2x0|GFCNDJMHUJAR_LgMnOuQ{RSTUVWXYZ[\]^_cdba`£iŤpɧw̩Ьӯײڶ޻ƺ!,P&+ Q )T` 1d̠Q UL%'K0r,ZtL1cȔ1sM5kش7sԱQ#G E T(U4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 *"B[/ˮ)"HVA ɻb 4pD,ld-HnR'n{l ,̈́B1 ²-   T[ ͩhMs+0P -S rq36@ ˳o>J)SJ!@B**+_j;}15H Ӷ0pHZ. [:栧kJ |Ѣ \b -,F8`@2ݰ & 3on@;mayavi-4.1.0/mayavi/core/lut/prgn.gif0000644000175100001440000000313411674464502020454 0ustar ischnellusers00000000000000GIF89aP& ,,,222333666888999:::???HN T$ ['b+h.o2u5 {:DOKVQ ]Xd_kerl"yCCCDDDHHHLLLOOOQQQUUUXXX[[[mmmsss|||(?/D7I?OGTNYV^_ehmru{|s(x/}7@IQZcksyDŽ͌Ҕ؜ۣߩ᯼漵úȿħȭͳѹֿ!,P&A1D ?2H#G$q0aČ!S N+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 $Qm }+R P/LRPF)$ q$ W Pk`00r , T@,210˰#2% 2Eko ‘P1@PT[0g=nr0PgP"< }(j7p7n@M㠇.褗n騳;mayavi-4.1.0/mayavi/core/lut/rdgy.gif0000644000175100001440000000320311674464502020450 0ustar ischnellusers00000000000000GIF89aP& !!!###%%%((())),,,///222333666888999:::;;;<<<===>>>???o x!BBBCCCDDDFFFHHHIIIOOORRRUUUVVVZZZ[[[^^^___eeekkklllmmmppptttzzz{{{# $&')+.(2179;C?KCUG]LfQoXw_elryĪ˲ѻ!,P&I A !R$I,a(hҨYæ7 "$K2UiQCc3eȄ +Sa,V 4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 ؾ[m oxd'wB$`{b'p'|R@&b'0E 6%@P) ' H@A&b$@'T% du\ 8c#pxRv-8]Ğ\@3Eq Haoơ8O t@A Ѕ'On;&<~o;mayavi-4.1.0/mayavi/core/lut/gnbu.gif0000644000175100001440000000311111674464502020434 0ustar ischnellusers00000000000000GIF89aP& ###&&&555666888:::>>>???DDDHHHOOOUUU[[[pppsssxxxBFJNRUY]be i mptx{!%(,036:=@DHX\KOSaeinrw|ÃҾӽջֺظڷܵݵ߷ิặ㻻佾濁!,P&*Dh@|Ƀ玝:tȉ獛6_tEK,WT2EJ0G!2DHN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫K(A`/PFb  $D@6 LH["$;$21!?,#l @F\n RE3}p#JAE=K 2@2Qq u?@epp6)1A X^ n@;mayavi-4.1.0/mayavi/core/lut/spectral.gif0000644000175100001440000000327311674464502021327 0ustar ischnellusers00000000000000GIF89aP& 18"""%%%---///333555666777888999:::>>>???OZl{@@@AAABBBCCCEEEFFFIII\\\___bbbdddiiijjjlllmmmnnnsssvvvwww{{{}}}:`EExx6JyXy/Qy}s-Kh{݂܍̪!,P&\(aN={qM9tع' B 菟>|lyD &ND2J+Wd3jҠ!3F /^1R!A~$4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫%$ ֫kﵛxP@*˼<)RC0 .(2/.T2VkX#˰/4"; *p -@%W(J RP+;2+nrtӎ00|-0RR! $7 %.Ȧ\-.ʵb28*0t` L# $x8Ь 7ۙcxKJ  '##D2,K,24p@ H(- xܚ۪/;mayavi-4.1.0/mayavi/core/lut/purd.gif0000644000175100001440000000311111674464502020453 0ustar ischnellusers00000000000000GIF89aP& ###,,,222333999:::;;;j!o%t(y,~0CCCHHHLLLQQQXXXZZZ[[[___kkkmmmpppttt|||37:>ADFGJK M O QSUY^chmr!w#}%(,18=CJOU[ahmqv{Є͉ˍɒɖʚ˝͡ΥϨЬѰҳӷջֿ!,P&!"4HP @S9qI挙2dƈ 拗.\hI&K$ArH"CG;tqF 3dĀE +T@ 4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫  ۾"'ADI@oo$p@$0#4@ p @1LX!!o"&զloDAs@3$);r@H4btK,{p5\;$l)UrawQA$0߀G.Wngv@;mayavi-4.1.0/mayavi/core/lut/accent.gif0000644000175100001440000000320711674464502020744 0ustar ischnellusers00000000000000GIF89aP& """%%%&&&666777888999???AAABBBFFFGGGJJJzcSsdZ```bbbjeaiiijjjmmmnnnssstttvvvwww{{{}}};5#,\m~6꺜߷ϳԴڶ乣DŽŋ’ʡןÇȈΊӌٍޏƹ;įɱ!,P&ݼʕ0`D!5kؤiL3tع'>} G9_@yH"C9DI%Lc9Zܨa 1f"4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫.d`0T V oP&&,P@x {I (P& t/;\ A*0"PI+ b p!@sJ@,@$8%*ʹ0%D$@sx^x"\{v֞\oۢ1($ g%M}mTrrL0'Z I &@G>''"K8~8#4d2h'.;$($q.o';mayavi-4.1.0/mayavi/core/lut/rdylbu.gif0000644000175100001440000000321411674464502021006 0ustar ischnellusers00000000000000GIF89aP& ###&&&***+++,,,111222333555999:::;;;>>>CCCHHHZZZ___kkkmmmppptttxxxyyy& &&&'!'''-'4)<,C/K3R7Z:b>iAqEyHLPSW[^ciou{2:5B7J9R~ "d3hҨYæ7p2tI-RQN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 ؾ[T{ o'd`AX ՖI`P0zI4]IhR1b\Z,x_P0I'aPP-t ' P#cnվ /@ݱܾ\`' ' &al&`͝o4@$6l'͵̓9^J#̳'g~Z#,y @ p'c8n'7<;mayavi-4.1.0/mayavi/core/lut/binary.gif0000644000175100001440000000316011674464502020771 0ustar ischnellusers00000000000000GIF89aP& !!!$$$%%%'''(((+++---...111333444666777888:::;;;===>>>@@@AAADDDEEEFFFGGGIIIJJJMMMPPPRRRTTTWWWZZZ]]]```aaacccdddgggjjjlllmmmppprrrsssvvvzzz}}}!,P&g;sā 5jИ!L.[`Rʔ(O4a䈑"D G:pبAc /\HĈ:pЀB 4X@4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 ؾK羻$Q^Y ABF!np@xPE j AXHrB`y@ P@l@ QPH a32lIB H!$0<>ZW+/GP 6 715V;cT4 Bk wu@Hnz!A_nz\ n;mayavi-4.1.0/mayavi/core/lut/set3.gif0000644000175100001440000000314111674464502020362 0ustar ischnellusers00000000000000GIF89aP&  """$$$(((---///777===>>>???@@@AAADDDFFFGGGJJJLLLNNNOOOPPPXXX]]]```jjjmmmqqqssswww{{{}}}{vdccpkhzefgvދ͒璒ʳ۳ߙעΩًלԬҽ򹿌ʆجǤĺƱ˰мЗŧ!,P&FM7oqDHP!=xtق U 9D&Qdr ;xȁC0e4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫즻  lKE|HW;~l Ւ A\; 0( /l(Ojp~X D4@ l-L .rA%`bl ̰ <+,́) Jc D.(@P)UsrrbO""AC Q/čn#'4)hbJ(+40@;;a @⤗n騧ꬋ;mayavi-4.1.0/mayavi/core/lut/blue-red.gif0000644000175100001440000000320211674464502021201 0ustar ischnellusers00000000000000GIF89aP&"""###777888999;;;???EEEFFFZZZ___aaajjjkkklllmmmppprrrsssttt{{{}}})G[kz/&J]m{G[ky/J]m{!,P&LRJ/_sM5nCgN6l֘)Cf -Z`2CF +ThⅎN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫~ ؾ.Λmk;ɻu0@![xP8/D0I 7< 4GA`P#@|@~2(< H"$0:"ÊT+4FOJ  $ < I^vX=u" #Rʋb݋\ 6 | oI'0"o#@X^->I!p/o<;mayavi-4.1.0/mayavi/core/lut/oranges.gif0000644000175100001440000000336411674464502021151 0ustar ischnellusers00000000000000GIF89aP& """%%%+++---///111333555666777888:::<<?ACEGILPSVZ ] `cfjnqu x$|(,059>CHLQVZ_dhnsx~ĎǓʙΟѣӨլװٵܹ޾!,P&}U+VTBuI"AzE!M6kԤAsL2cĄK.[drJ*SDI&K$ArH"CG;4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫~ 0B'{' tK,0T[/()('$P( @!L2` +S >>???@@@AAABBBCCCDDDFFFGGGIIIJJJKKKOOOQQQRRRSSSTTTXXXZZZ]]]aaadddeeehhhiiijjjlllmmmnnnpppqqqrrrssstttvvvwwwyyy{{{}}}~~~!,P&%dbD/fаq#AAdI'PLbK-]C3hԬiM9tɣg>~D!D-j$I(YIӦN4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫V ؾ+kp.- "0@&?<` , !LPȵ d  hnI=d@ xƄXr(%(ttA"Q$p$AϠ1 PrE1 ,2h\I'H%`'0@QB'wb;>B@0+A 9tG:GˠTTX;W+,rdo%="@֦. ~.A#z6<pm.0޵.& DTv(L W0La@;mayavi-4.1.0/mayavi/core/lut/gist_ncar.gif0000644000175100001440000000325511674464502021463 0ustar ischnellusers00000000000000GIF89aP& >/%%%+++---///333555666777888:::<<N+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 .Bn^[JqtpP@*xھ[b ,48` ز "B- /pt rȕ- "%H!b- ATp "`KI ˩;в4 ۀ .sgr ɳ{t*,m+q[r\"81 Ko@$`- Rk}Ll7S[ {@ܢ\bK @zܯ)tb !8 2+B؃B&|po_~@;mayavi-4.1.0/mayavi/core/lut/hsv.gif0000644000175100001440000000304111674464502020303 0ustar ischnellusers00000000000000GIF89aP& %%%)))***///555666888:::CCCHHHIIIWWW\\\bbbhhhjjjkkkwww~~~!3"4FYk~GZm  /-BUgz@Sex  -/@SexBUgy!,P&E qE =|BH$F  5fxC:v(c6oqFM4WT%L@"e.]CF/Z`qт %4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 .{yPA\@Xz[8 {PA @{"ЈH0!x Lﻍԁ ءb/΍ @#!X F}3 7b%RuH>>???A)D+H.L0Q3 T5"X7#\:%`=&d?(hB*lD+oF-sI.wL0{N1P3AAABBBEEEFFF___bbbiiijjjlllmmmnnnooosssvvvwwwzzz{{{}}}S5U6W8Z9];_=b>e@gBiClEoFqHtJvKxM{N~PˀRςS҅UևVڊXތY[]^`acdfgijlnoqrtuwyz|}!,P& 8A2h RX0bȘA8r0i (RPb ,Zp 0bƐ)c 4jְi 8rЩcN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫+ﵗ!:$I&FHtD`B#l0˜ @C`0%-$t !>|rJ!,p!XPr@$ I$L 4F_#0B’ؼprmUo s] opRgs$׆@$?0#DS0wݝT`$G|'|$, 8Bgk  $"\R I}(pBz'qo`y~ n/on@;mayavi-4.1.0/mayavi/core/lut/flag.gif0000644000175100001440000000303711674464502020421 0ustar ischnellusers00000000000000GIF89aP& % + = %!68###+++---333<<L1၇*E$psHf$Q`%r2i$1f 4qbAF=PFGJ=P9a+F4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫֋.Rȼ;[ [o+8pӛH0d@0 ~x1!'* r/ B* =s13.E H'mn`,  `؉ sVl@tmx;mayavi-4.1.0/mayavi/core/lut/winter.gif0000644000175100001440000000312711674464502021020 0ustar ischnellusers00000000000000GIF89aP& """///111222666777???@@@AAACCCEEEFFFGGGQQQRRRSSSjjjmmmppprrrsssttt{{{}}}= .'!$*714:]MGADJWPTZfc`jmvspy}ƛÝʚ֓͘ӕЖّܐ!,P&YxbE 0fq:vHB|)2H$H(Y 'PTbE ,WD˗-b€C挙2hҰY8p̡N;x4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫{ l{֫.ګ+H /"TB+\\2\"@%' (<#ABBR L@Gs% y@& 0lLr@ ɵm!jpI67 }I~$qݭ"q.w%4rw砇.褗;mayavi-4.1.0/mayavi/core/lut/ylgnbu.gif0000644000175100001440000000320311674464502021003 0ustar ischnellusers00000000000000GIF89aP& ###&&&***+++,,,111222555666888:::>>>??? [ `"f%m'r)x+DDDHHHOOOUUU[[[pppsssxxxyyy. 0#2%5$9$>$B$F#J#N#R"V"["_y~!d!j n sb¿ižoǽuɼ{˻!$),/37:=CJPV\κкԹָٸܷ޶ᶻ!,P&#Az#>{sN:syM6kԤAsL$G!rJ*SDI&K$"$@~#6jИ!#.Z4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫[H`R-& ɶދ.P dq'P%^vB|&@ X'~Aa|'1@3\2*:{+,jq>Lth^{"RLu'I:%p5t@cM6j?}|3#s|2ۓ$Tbu?h m̀d`pa$"/oT;mayavi-4.1.0/mayavi/core/lut/gist_gray.gif0000644000175100001440000000330311674464502021474 0ustar ischnellusers00000000000000GIF89aP& !!!"""%%%(((+++,,,---///222333444555666777888:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGIIIKKKNNNQQQRRRTTTWWW[[[\\\^^^___aaacccdddgggkkknnnpppsssvvvzzz{{{~~~!,P&@*XAB85lȡcG@ !Rȑ$J0i$)U`K1dʘ97qԱG? 2tH"F4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 ۮBn^K PqpP@/@$zzHW$ ~2b" _828F!XH,g+4 b-Ft@h$)  $#ox2ۆ=6ÏT 4.# ǣ #K<$ " Q/`"HlKfqp4$` tHE-l+;2<Ľۀ N_6/p x{䛏 Ao?;mayavi-4.1.0/mayavi/core/lut/jet.gif0000644000175100001440000000212211674464502020264 0ustar ischnellusers00000000000000GIF89aP&r """%%%???AAAFFFjjjmmmsss{{{}}})4@LWco{8.$WMBkav+8DQ^jw_isJT@+6" 󖖖!,P&<=>?@ABCDEFGHIJKLMNOVWXYZ_`acb[^\]USTPQR;9:678+45/1320.-,*)('&%$#"! ҅&UiSOF:jUWfպkW_;lYg[m \sMlg^y}'Zk f\q =L']wm_i&[o-,]u]^{ jRWFźTjVS6iUn֒A,eҲ1mΪq{%Q,-MlqR+I'~]yt˥gg>]{s۝WG.жF6; &nw1{! ߅5O3mIK9o™]'yv^ǣq`̭ GWb՘h%i vkͅXg\vǠsUg}*{9]x HqסuJ7]z-؜wfԕg߁i >HrTiXf\v`)dihlY%ln֩&v%q@P'zj%PL g <g)ʁm\ )vdj 0kq Z,` ƲF+;mayavi-4.1.0/mayavi/core/lut/autumn.gif0000644000175100001440000000312111674464502021013 0ustar ischnellusers00000000000000GIF89aP& ###---333444666???@@@AAAIIIdddlllooopppsss !$'+.147:>ADGJMPTWZ]`cgjmpsvz}!,P&=|B%L@B -\C 5lC=|D"EAD&M<E*U\E.]|F2e̜AF6m4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫[+0bAD$V[ p1Anx\ ؁ш 4p,r Rk H3ˆ8R!>\! @ؾ~#N1!H[{,4RՐ-6;[-u֐0J }p0$1u-lWKG d @Grp$+xgw;mayavi-4.1.0/mayavi/core/lut/bone.gif0000644000175100001440000000311511674464502020430 0ustar ischnellusers00000000000000GIF89aP&  !%( , !!!"""""0%%4((8++<.-?666888;;;???00C33G66K99O;;S>>VFFFAAZCC^ZZZGGbIIfLLjOOnQRqTUtWYwZ]z]a|_eaaajjjlllmmmooorrrssswwwzzz{{{}}}bhelgpktmxq}tvy|¥ũȮʲζлӿ!,P&@` .`Ƞ!D@B .^Đ1F 7pa$J0i ,Zp 0bƠIf 6nC;x3!D4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 .SPP0 H$1BgD 8"nWC$Q`DA0AdPDH @ DR@, #  WK u Dr3QGM`2@` 1RMoT!-&]e@@(&ߔWng朧;mayavi-4.1.0/mayavi/core/lut/prism.gif0000644000175100001440000000307711674464502020646 0ustar ischnellusers00000000000000GIF89aP& %%%///444555666777888999:::>>>CCCEEEIII\\\___bbbiiilllooo # ': $ (BGY]bfx}ET. 7 = +%)17cs|WhkdZR60' ,' E@IOflwrİɚ텅!,P&Y#4%`8Aƒ5'T +HEL/$CظM LRP| "TnX FE#Krh!fD'?4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫춋-+; x$`A"N!rб@};.@HPTo B 4 . HN0 + ϓq 5-S? @u\[ͭw5KL-sU|p tmw}nu7GNt@;mayavi-4.1.0/mayavi/core/lut/brbg.gif0000644000175100001440000000310311674464502020416 0ustar ischnellusers00000000000000GIF89aP& ?3&&&555666777888:::>>>???X2_6g;m?D9J?tC{GNDTJYP^Ud[ia ogum{sDDDEEEHHHOOOUUU[[[xxxK O U Z`flr"x'~+y2ō<ɕF͝PѥZխcٶnܾw%+19CLU^hq·{ɽāɉ͒қפܬ徃ÌǕ̞Ч԰ٸ!,P&)TpA-\C 5nȡc>LR,YԬaM=~Cg8pހ -Z 9b!Ba!@|ЁC 4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 ՊAPGK Q@ \epM L @Ȼ&N+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 kjAlo# oB PJ*ﮰ䡁{ q 0 p1)˩upzlq 0 &'!J gtO,@w5֥)Iۑ Aje+<Ǧ|wEwIҩ (𶶂nʵ 2 G Ig"`A* 8{5#9|˖8m{2pےÎj𫽸}\@@". oo觯c;mayavi-4.1.0/mayavi/core/lut/greens.gif0000644000175100001440000000322311674464502020770 0ustar ischnellusers00000000000000GIF89aP& """%%%///555666777888:::???FJNR!V"Z$_&c'g)k+n-q/t2 w5z7}9CCCDDDEEEFFFHHHIIIOOOUUU[[[\\\jjjmmmsss{{{}}}<>@!C$F'H*J-M/O3R6T9WN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫[G "{p X`@  (`' 30waA_w Wd@*2@}!s;b0FHH&H JOGt#( @\RIRG0(l6I+s6ݢ8&tӽ 4a T18w(.zuW@|0ځhxRH{H'd7G/ԋ;mayavi-4.1.0/mayavi/core/lut/README.txt0000644000175100001440000000141311674464502020513 0ustar ischnellusers00000000000000This directory contains lookup tables imported from pylab automaticaly by "cm2lut" script. Do not modify manually These colormaps are borrowed from matplotlib. See matplotlib license for license agreement (http://matplotlib.sourceforge.net/license.html) 34 of the colormaps are based on color specifications and designs developed by Cynthia Brewer (http://colorbrewer.org). The ColorBrewer palettes have been included under the terms of an Apache-stype license (for details, see the file LICENSE_COLORBREWER in the base directory of mayavi source). 7 palettes are from the Yorick scientific visalisation package, an evolution of the GIST package, both by David H. Munro. They are released under a BSD-like license (see LICENSE_YORICK in the base directory of mayavi source). mayavi-4.1.0/mayavi/core/lut/pubu.gif0000644000175100001440000000221511674464502020460 0ustar ischnellusers00000000000000GIF89aP&| ###&&&555:::>>>9Z<`AeDjGoJuMzQLLLQQQXXX[[[pppxxx|||UX[]_acfhjlnp twz}$)-28>DJPW]bhov{Ѥ҅ӊԏՔ֙؞٣ڧ۫ܰݴ߸!,P&utsqonmlkjihedcba`_^]\[ZTSRQPONMLKJIHGA@?>=<;:9876543210/.-,+*)('&%$#"! ҅&UiSOF:jUWfպkW_;lYg[m \sMlg^y}'Zk f\q =L']wm_i&[o-,]u]^{ jRWFźTjVS6iUn֒A,eҲ1mΪq{%Q,-MlqR+I'~]yt˥gg>]{s۝WG.жF6; &nw1{! ߅5O3mIK9o™]'yv^ǣq`̭ GWb՘h%i vkͅXg\vǠsUg}*{9]x HqסuJ7]z-؜wfԕg߁i >H|TiXf\v`)dihl  [f\XɇA4|hV` i~1کj de@Q~0+lR)|4~TA|Qf%D(`G嶺,a;mayavi-4.1.0/mayavi/core/lut/puor.gif0000644000175100001440000000310611674464502020472 0ustar ischnellusers00000000000000GIF89aP& ###+++---///1117771Q6 X: `?hDoIwN!CCCEEEHHHLLLQQQSSSVVVXXX[[[ppp|||<C@GKNRUZ_d i o ty~ +4>HQ[epzS&X.]7cAhJnTs]yg~pxȄ͎ҙףܭ㿝£Ʃ˯еӹ׾!,P&O@b.^!c6nȡc>~ R,YlqN@"4HО;vЙ#0`xI&K$ArH"ChȀႅ 4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 $ܖtFb#ۮa@y!l@oLQ-P1L఺@WoR |D#o"%0B)@#2RFȿ G~nj."| QT+!𬵹_c<&k۶P4pp\G.Wn;mayavi-4.1.0/mayavi/core/lut/greys.gif0000644000175100001440000000322211674464502020635 0ustar ischnellusers00000000000000GIF89aP& !!!"""###%%%'''(((,,,///000555666777888999:::===>>>???BBBCCCDDDEEEFFFHHHIIIKKKOOOPPPRRRSSSUUUVVVZZZ[[[\\\]]]aaadddgggjjjmmmpppssstttxxxzzz{{{}}}~~~!,P&%Az"E"4(П>|cg8oܴYM1b€E˕*S@ydI$F 䇏N+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫잛 , {.`H`@ #L 8#P$t" y\.$R0_2PG4@p1$4Tk @c$'|@";KTkАT;@1dl9p$|urpȵ4AT:q ( k$V$!F0$@ V mm#?<` ~LH`0$` o#~P'G/;mayavi-4.1.0/mayavi/core/lut/set1.gif0000644000175100001440000000310211674464502020355 0ustar ischnellusers00000000000000GIF89aP& """$$$(((---///111777>>>???AAAFFFGGGJJJKKKNNNOOOPPP]]]```jjjmmmsssvy{{{{}}}!(+7](X1m)~*}wm9r)5F?V\@aNIeSu_jZzhIdYe]iln{YXLNQOIYEoCygi`aorGdĎ+͟,հ./"0',121N+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫즫 @lFk<-V;Hɵ`"@pÒ0Kbm Q&t$(r"I(4 \$l B_Kt6H"R\Tw<H2o`-! "$"#! 0HbGtm7G.a;mayavi-4.1.0/mayavi/core/lut/piyg.gif0000644000175100001440000000310211674464502020451 0ustar ischnellusers00000000000000GIF89aP& ***+++,,,111222666888:::???*h.n3s8y=~DDDHHHLLLOOOQQQUUUXXX[[[xxxyyy|||UZ _ ejouzAFK!Q#W']+d/i3o7v;|?FOW`hqy!,7CNZepzႶ㊻ނ!,P&QPE / !R$IDe .^G:u( D G7mجɂ劕*TG;tqF 3dĀAC 4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 ([T!F(\$r X%TKLq@@0#@(f11(@2#isb#H:Q-,B2C., J+"6 S]f_Ҍ@,]k"x06cP`<7G.g;mayavi-4.1.0/mayavi/core/lut/ylgn.gif0000644000175100001440000000306011674464502020455 0ustar ischnellusers00000000000000GIF89aP&***+++,,,111222666888:::???H*K+O,R.V/Y0]2`3d5g6j7l9 o: s;u<x=z?}@DDDHHHOOOUUU[[[sssxxxyyyA"B%E(G+J.L0O4R7T:W=Y?[D^JbOdTgZj_leojrpuux|z|~΁уӅՇ׉ڋ܍ޏ⓽!,P&Ӈ=yY& 3fʐ#& /^p٢% +VP"% #F"$>zء#6jИ!#&J!":pؠ! 4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫K R! վAHs/TJܫH@H"s!@(q`{(Q`" '@!"Ђ0rKD @"0+|o v "@D]L,І"Yw =e{pLP4A6q߀.nn@;mayavi-4.1.0/mayavi/core/lut/gist_heat.gif0000644000175100001440000000326711674464502021464 0ustar ischnellusers00000000000000GIF89aP&  !%*/48<"""%%%+++---///333555666888:::<<DKQV\bgmtz*8DO[grȎ͙Ӥٰ޼!,P&H! :x"0bȘA8r(QLR,Yl0aČ!S4iԬa8q̡SΝE9z'S4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 *+oֻ(.dGR ɻ^."*dp,(J"&=,` 0 /DЇû( 40HH 0Є,( J%`Z@), *l@h)0B,ao ,N#6\@8ҥ"* wkoʵ JZQ)I&7xW G7O(-`0j`,0CjD,TP<%R "3 m@;mayavi-4.1.0/mayavi/core/lut/gist_rainbow.gif0000644000175100001440000000350211674464502022174 0ustar ischnellusers00000000000000GIF89aP&  !!!%%%+++---///111222333555666777888:::;;;<<N+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫b ۺn[oދo L hRo`,n׾ Bt 4`1Tp/0C 4 %pMhkА14+1 4!,X0? 3@3 < 4x 6N@1 p ڻ< 0 h0L M r # 3 JB0v %f9@c>Dk34ɤR p 2ם0@4@L@ $8 ;,4`V> ! XŻX  , 0 /PL@ hB+_J ztX($P_@C< h]_Ļn-X# EDr@ l!9PH*Z];mayavi-4.1.0/mayavi/core/lut/set2.gif0000644000175100001440000000312611674464502020364 0ustar ischnellusers00000000000000GIF89aP& """$$$(((---///666777>>>???AAABBBFFFGGGJJJKKKNNNOOOPPP]]]```jjjkkkmmmsss{{{}}}=7:401:~{ʟx֚rޒ~dklfuNKUQ^hrCH@CL^Tofx{nԔ˕µʬϥԾ՟ۗǺ˻¸нȁƉĒ–Ǒ˙ʡɩȸǰЏ׍ߋȑ!,P&4b% 4jqjְᲥ?DhD R4GN7nN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫즛 @lFBpIWt"l Z@#A\ $p' /|&@q,\"֦@^B("_+';!PI( ̰g 9,A( F[D4@@Dɵ5_f|. t P6"&0R"00&tRp`I(`AVMw砇.9;mayavi-4.1.0/mayavi/core/lut/__init__.py0000644000175100001440000000012611674464502021126 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/core/lut/summer.gif0000644000175100001440000000316311674464502021020 0ustar ischnellusers00000000000000GIF89aP& """###%%%///444555666777888:::???CCCEEEFFFIII\\\jjjlllmmmooopppsss{{{}}}fff fffffff!f$f(f+f.f1f4f7f;f>fAfDfGfJfNfQfTfWfZf]f`fdfgfjfmfpfsfwfzf}fffffffffffffffffffffffffffffffffffffffff!,P&SX0bȘA8r@B#H(Y¤'PHB+Xh¥/`ˆC3hҨYæ7pșC;x4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫+o"`/}T@z, ?@ABCDEFGHIJKLMNOPQRSTUVWXYZcdefgijkor҅&UiSOF:jUWfպkW_;lYg[m \sMlg^y}'Zk f\q =L']wm_i&[o-,]u]^{ jRWFźTjVS6iUn֒A,eҲ1mΪq{%Q,-MlqR+I'~]yt˥gg>]{s۝WG.жF6; &nw1{! ߅5O3mIK9o™]'yv^ǣq`̭ GWb՘h%i vkͅXg\vǠsUg}*{9]x HqסuJ7]z-؜wfԕg߁i >HxTiXf\v`)dihl _AAtzw֩go1`A Booz^tx(oƁhxD@l@pnhw vҙFZ+)rGk9 x|A@fvKg ;mayavi-4.1.0/mayavi/core/lut/bupu.gif0000644000175100001440000000221111674464502020454 0ustar ischnellusers00000000000000GIF89aP&| ###&&&555:::>>>PNUSZX`\dajfo kt py uzLLLQQQXXX[[[pppxxx|||} %*/49>CGKOSW\`dhlquy}ŒčǏɐ˒͔ϕјә՛ם٠ڣۦݩެ߯!,P&utsponmlkjgfedcba`_^XWVUTSRQPONMLKJIHGFEDCB<;:9876543210/.-,+*)('&%$#"҅&UiSOF:jUWfպkW_;lYg[m \sMlg^y}'Zk f\q =L']wm_i&[o-,]u]^{ jRWFźTjVS6iUn֒A,eҲ1mΪq{%Q,-MlqR+I'~]yt˥gg>]{s۝WG.жF6; &nw1{! ߅5O3mIK9o™]'yv^ǣq`̭ GWb՘h%i vkͅXg\vǠsUg}*{9]x HqסuJ7]z-؜wfԕg߁i >H|TiXf\v`)dihl [)XɇA@aGl|hVG`>:کp څ~j~ Tpib: )| hKB{,`~ Yx m߆{-^k\ h1.{.;mayavi-4.1.0/mayavi/core/lut/pink.gif0000644000175100001440000000302111674464502020442 0ustar ischnellusers00000000000000GIF89aP& )7666999>>>???A&&J,,R22Y77`<4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫{-l ;oD p d4 o8 ,ѰAP | \`0 #'`>= :8[F<'}.7 APo m=vfl=v@;mayavi-4.1.0/mayavi/core/lut/orrd.gif0000644000175100001440000000314711674464502020460 0ustar ischnellusers00000000000000GIF89aP& +++,,,---///111222333777999:::;;;CCCEEEHHHSSSVVVXXXZZZ___kkkmmmttt  $)-2!8%=)B-H1M5R9W=\AbFfHjJoLsNvO{Q~RTVX[`dimquz~čƐɒ˕ΗКҝՠפب۬ݱߵ!,P&)MdAg8p޸if4hΘ)Cf0TH剓&L(I䈑"D 䇏N+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫KG`/XnDH"Gn vՂثA? & "4ET tE#s \h)+T tIڛ$% I@Ǚ0 @&\@%&X}5E wPZG,#n-.8!݌?   n`v9'U" pE喧ꬷny@;mayavi-4.1.0/mayavi/core/lut/dark2.gif0000644000175100001440000000316311674464502020513 0ustar ischnellusers00000000000000GIF89aP& $$$&&&***---333666777???z~<@@@BBBEEEGGGIIIKKKLLLPPPUUUlIyjOnh\tiUdddifbkkksssyyymhvzs's)oN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 \Hb  `&['!^, [p#`%P@nh )T&ւ [p(8`2 x<'_BA\ǦBBq'վ;~\[!4.)QX5DK$S'&\dž"lM)"@#bMǦh"@.8@tA ,}y$ ..n;mayavi-4.1.0/mayavi/core/lut/ylorrd.gif0000644000175100001440000000324211674464502021021 0ustar ischnellusers00000000000000GIF89aP& ***+++,,,---///111222333777999:::;;;CCCEEEHHHSSSVVVXXXZZZ___kkkmmmtttxxxyyy&&&&&&&&&&%!%#$" ! + 0!5#:$@&D'J(P*V,\.b/h1n3u5{78:<>?ACDFGIKMQUZ^bfjnsw{߃!,P&+Q:dA=z9c 1b€ -Z`b )R@y %J 9b!BБ 52bx*4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫R-)"A |Ј{`{o@ բ?@`bd{p*vp" ) H"\`!pGx *z!dp,20|R-(< *t*# G BQJ0Π0H Ibw2ȭJ|R'8)rBJ }s PG`સ 8Px'gpV;rXBA@Bqк [+"${| 1>>ox!2e 9n @xCCCHHHZZZ___kkkmmmppptttxxx# $&'(+.(2179;C?KCUG]LfQnXw_elryGNTZb"h&m+s/y3~7;?FOZcmwԉĪʲѻ؊ۓޜ!,P&3h8",Zx#4jظ#N+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 PAؾ+ `[/D{/l6|p+I80J < X T!Gr&x1 qH2!34 #X0A"@$,#VÑ\G0@ H1H2L\n%H-p2ɒ<$h 3C~T?oHE!gw砏;mayavi-4.1.0/mayavi/core/lut/pylab_luts.pkl0000644000175100001440000116002211674464502021706 0ustar ischnellusers00000000000000(dp1 S'data' p2 (dp3 S'Spectral' p4 (dp5 g2 S'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\n' p6 sS'type' p7 S'numeric' p8 sS'id' p9 I1 ssS'summer' p10 (dp11 g2 S'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\n' p12 sg7 g8 sg9 I2 ssS'RdYlGn' p13 (dp14 g2 S'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\nezdN+R8MjWIihyAAAA==\n' p15 sg7 g8 sg9 I3 ssS'PuBu' p16 (dp17 g2 S'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\nBs/xFe7xj/50g8V/AYShAAuHIAAA\n' p18 sg7 g8 sg9 I4 ssS'copper' p19 (dp20 g2 S'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\n' p21 sg7 g8 sg9 I5 ssS'spring' p22 (dp23 g2 S'H4sIAP6B70kC/3WZR3CVVRSAE8ANq4gRnxjxLVkxdlHRszcjo0gTEaU544xAEhIg9I1tF0IIAUIZ\nxnEcx2HooSf0Dum9vP7S6b357vnP+R3vuedbmHG+hLz8/72nrhkyZ0HB/JzCsXMW5s0bO7/gp/wf\nZ+XlzSoc/l3evDkLFyzKzyuYkz88N937ruEL5no2d0h22s+T02f/OjF36Jjs9M/T0rOH/ULfMze/\nMGfe8Nxhk4f8MC47LTs99T0vjMkeOjl9/IQJEz57ngL/k52WP/v3SWnBNOIGpP0P/v//vmYgORDq\nMlxX/BqoqjRIH0B+g+3bDNrPF8Ga1QbpM5ES+A7R/v0y8JA+CymHNxDt92+H588Mg8KPQHZAV6dh\nUPl8f0DlcYP0I5E/YdtWw6Dy+f+CVYj0o5C/YSYyqPx9/8CniPSjkZ30dVD5+3fBs6eGAeFfRHZD\nZ4dhQHk+e+D4MYP0LyF7YesWw4Dy/PbBqpUG6V9G9sO3yIDyfA/AJ4j0ryAV8DoyoDz/g/D0iaFf\n+FeRQ9DRbuhX3s9hOHbUIP1ryBHYUm7oV97fUViBSO997mMwA+lX3u9xGI9I7537Svo9/cr7r4In\njw19wq8tMlRBe5uhTzkfJ+DoEYP0xWsNJ6B8s6FPOT8nYcVyg/Trig0n4RukTzlfp+BjRPqSdYZT\n9B76lPN3Gh4/MvQKv77EcBraWg29yvk8A0cOG6QvXW84A5uQXuX8noVliPQbSg1nYTrSq5zvc/AR\nIn3ZBsM5Oqe9yvk/D48eGnqE31hmOA+tLYYe5X5cgMOHDNJv2mi4QF97lPtzEZYVGqTfjM/tInyN\n9Cj36xJ8iEjvnbtL9Dl7lPt3GR4+MHQL793by9DSbOhW7ucVOHTQIL0X964APsaybuX+XoWliPRe\n3rgK05Bu5X5fg3GI9F7evUbvuVu5/9Xw4L4hKfwXSDU0NxmSSnyohoMVBulrqhE6h0klftTA0iUG\n6b9EamDaVENSiS818AEifW0NQvckqcSfWrh/z5AQfiJSC02NhoQSn2rhACJ9XS0CeI1LE0r8qoPF\niPRfIXUwBUko8a0O3kekr69DKM4klPhXD/fuGuLCT0LqobHBEFfiYz0c2G+QvqEeoTgYV+JnAywu\nMEg/GWmAKfg1rsTXBngPkd773A30e+JK/G2Eu3cMMeG9595If0dMic+NsA+R3js3jYCPqSSmxO8m\nyEekx2M/tYneQ0yJ703wLiK9d2+b6D3HlPjfDHduG6LCe3Gnmc5RVMkPzbBvr0F6L2420zmNKvmj\nBfIXGaT34n4LTMJ7EFXySwu8g0jv5a0WumdRJf+0wu1bhojwXt5tpXscUfJTK+xBpPfqhlbAMFEc\nUfJXG+Qh0nt1TxvFoYiS39rgbUR6r25rozgXUfJfO9y6aQgL79Wd7RRHw0p+bIc9uw3Se3VzO8Xp\nsJI/OyAv1yC9V/d3wETMA2Elv3bAW4j0Xt/SQXkmrOTfTrh5wxAS3uu7OimPhZT83Am7EOm9vrET\nME0WhZT83QU5iPRe39tFeTik5PcueBOR3uvbuyjPh5T8H/K/z/b87/LvcdcHIfiePqft+e8K0t/p\nrh9C0EnPyfa76blupefsri9C/nuyPb9Xfs/u+iPsnxPb87nic+auT8Iwk86p7XPpXI+mc+6uX8LQ\nTvfE9nyvyumeueubsH9Pbc/3mu+5u/6J+HHC9hxXOM6466MIzKA4ZXuOa1kU59z1UwRaKU7afi/F\n1U0UZ931VcSP07bnuM5x3l1/Rf08YXvOK5xn3PVZFKZTnrL9IsproyjPueu3KDRTnrQ959UyyrPu\n+i7q52nbc17nPO+u/2J+nWB7riu4znDXhzGqz6XnuiZAdY67foxBI9VJtt9PdVUp1Vnu+jLm12m2\n57qO6zx3/Rn360Tbc13Jdaa7Po1TfSp9AdW1I6nOddevcainOtn2XFeXUJ3trm/jfp1ue67ruc53\n178Jv0+wPfcV3Ge46+ME1WfSc1+TSX2Ou35OQC31SbavoL6qmPosd32d8Ps023Nfx32eu/5O+n2i\n7bmv5D7TXZ8nqT6Rfgn1tSOoz3XX70mopj7Z9txXF1Gf7a7vk36fbnvu67nPd9f/3f6cwPY8VwjS\nnMHdH6R+nuYUtue5RpDmHO7+oRvKaU5ie56rZNGcxd1fdFN+vCw8z3V4zuPuP3r8OZHtea6URXMm\nd3+S+nmaU9m+kOZaWTTncvcvPVBGczLb81wtQHM2d3/TQ/nhvPA81+M5n7v/6fXnhLbnuWKA5ozu\n/ij18zSntD3PNQM053T3T71QQnNS2/NcNZPmrO7+qpfi42nhea7Lc153/9Xnz4ltz3PlTJozu/uz\n1M/TnNr2y2munUlzbnf/1gdFNCe3Pc/VM2jO7u7v+ig+VAnPc32e87v7v36635XC814hSHsGd3+Y\n8rSnsD3vNYK053D3j/0QoD2J7XmvEqA9i7u/7Kf7cUh43uvwnsfdfw7Q+a4QnvdKAdozufvTlKc9\nle1X0l4rQHsud/86ABm0J7M979UyaM/m7m8H6HzsFp73erznc/e/g/R+dwrPe8Ug7Rnd/XHK057S\n9rzXDNKe090/D0IG7Ultz3vVDNqzuvvrQXo+O4TnvS7ved3993X6+8qF571ykPbM7v485WlPbfvV\ntNcO0p7b3b9fhzTak9ve36vTnt3d37PPAW2Pb+/582eP/ReuwF/OhyAAAA==\n' p24 sg7 g8 sg9 I6 ssS'Accent' p25 (dp26 g2 S'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\n' p27 sg7 g8 sg9 I7 ssS'OrRd' p28 (dp29 g2 S'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\n' p30 sg7 g8 sg9 I8 ssS'autumn' p31 (dp32 g2 S'H4sIAP6B70kC/4WZR3CUdRjGdwNecrILYjtyythFQXN3x4wisStDQphxBkJ22aixcrHdKAEChDKO\n4ziO45DeC71Dei/b+25C72DeJ3jj8fkdcshvk+x+3/973+eZrM8qLC5dU1KWU7jWU5SzpnS19/MV\nHs+KsuzlnqLCtcXrvJ7SQm+22zn7quzilbPWneVy/JjvLPh5qXvOQpfzLYfTNfenu69Z6S0rKcp2\nz83PWrXI5XA5Z15z30LXnHznkry8vDfvzIAvLoe34Ndljmccd5nOddyT/74/nXs/KMn9f7+e+nng\nF/HzG6h/GJSL319B/ROgUvz9vdQ/CH4T7+936h8Ff4j3/yf1j4O/xOf7m/qnwD/i8++j/gFQJa5P\nNfUPgRpx/WqpfwTUietbT/1joEFc/0bq54MmcX+aqV8AWsT9a6X+SdAm7m879U+DDnH/O6nfuMHo\nFOdjP/WbNhr7xfk5QP3mTcYBcb4OUl++2Tgozt8h6reUG4fE+TxM/dYtxmFxfo9Qv22rcUSc76PU\nV2wzjorzf4z67RXGMfF8HKd+x3bjuHh+TlC/c4dxQjxfJ6mv3GmcFM/fKep3VRqnxPN5mvrdu4zT\n4vk9Q/2e3cYZ8XyfpX7vHuOseP67qH8bdIn5wH13FxDzo5v6d0C3mC/c93QDMX96qF8KesR84r63\nB4j51Uv9u6BXzDfu+3qBmH991C8DfWI+ct/fB8T87Kc+H/SL+cr9QD8Q83eA+vfAgJjP3A8OADG/\nB6l/HwyK+c790CAQ83+I+g/AkNgP3A8PAbE/hqn/EAyL/cL9yDAQ+2eE+o/AiNhP3I+OALG/Rqn/\nGIyK/cb92CgQ+2+M+k/AmNiP3I+PAbE/x6n/FIyL/cr9xDgQ+3eC+s/AhNjP3E9OALG/J6lfDibF\nfufeNwnE/vdR/yzwiXzAfQnwifzAfdU+wyfyBffnpg2fyB9+6p8DfpFPuHcDv8gv3FdXGX6Rb7g/\nf87wi/wToP55EBD5iHsPCIj8xH1NtREQ+Yr7C+eNgMhfQepfAEGRz7hfB4Iiv3FfW2MERb7j/uIF\nIyjyX4j6F0FI5EPuvSAk8iP3dbVGSORL7i9dNEIif4apfwmERT7lvhSERX7lvr7OCIt8y/3lS0ZY\n5N8I9S+DiMjH3H8BIiI/c99Qb0REvub+ymUjIvJ3lPpXQFTkc+6/BFGR37lvbDCiIt9zf/WKERX5\nP0b9IhAT/YD7r0BM9AfumxqNmOgX3F+7asRE/4hT/yqIi37CfRmIi/7CfXOTERf9hvvr14y46D8J\n6l8DCdGPuP8aJER/4r6l2UiIfsX9jetGQvSvJPWLQVL0M+6/AUnR37hvbTGSot9xf/OGkRT9L0X9\nEpAS/ZD7b0FK9Efu21qNlOiX3N+6aaRE/0xT/zpIi37K/XcgLfor9+1tRlr0W+5v3zLSov9mqH8D\nZEQ/5v57kBH9mfuOdiMj+jX3d24bGdG/p6ifZUr0c+5/AFOiv3Pf2WFMiX7P/b3/zz+Tywty/gWY\nm0ULhyAAAA==\n' p33 sg7 g8 sg9 I10 ssS'Set1' p34 (dp35 g2 S'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\n' p36 sg7 g8 sg9 I12 ssS'Set2' p37 (dp38 g2 S'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\nt7zN/e+GDfL/7/3rfmLFqP8CtcmTUIcgAAA=\n' p39 sg7 g8 sg9 I13 ssS'Set3' p40 (dp41 g2 S'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\n33+f169cEf89b9pfJkYM/z/CpZ0BhyAAAA==\n' p42 sg7 g8 sg9 I14 ssS'Pastel2' p43 (dp44 g2 S'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\n9Qp0ceb9/38lePjSQf8LudM+DocgAAA=\n' p45 sg7 g8 sg9 I43 ssS'spectral' p46 (dp47 g2 S'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\n' p48 sg7 g8 sg9 I15 ssS'binary' p49 (dp50 g2 S'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\n' p51 sg7 g8 sg9 I16 ssS'winter' p52 (dp53 g2 S'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\n' p54 sg7 g8 sg9 I17 ssS'jet' p55 (dp56 g2 S'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\nS2jw9jXoN7eOkhiHw17bkqlhzoH96XonX+rYK9DbHvyZYfUd7P+xieuziWsGnXPT0nSFDuyqhu0C\nx5wm0DXST1S4mMA+h7dAZW2pFHSzMxLnDXawn1QfMLyV8hL0dK5NWc3VF/D+fStcJa5+Drr0GQro\nA1UzA1p6GuH9VRD6Z7cc7AZJRQf+GN8AusOJ0eOHtJ+CHhDunDBqoQRe377xc1SXPwE9cXXHNfX1\n9aDfsDC00Qx6DLqELs8CMby+1Kq9DISPQB/LUes1S34I+uyejbutCh6AvqwhXXFF5X3Qy493zk3L\nqwP9/ab9ttfr74KubzHRp6a/BnR/taw9zzSq4efbbRnTbVkF94dP3WvQrwJ05fjgu98cKwfdege3\nnZdTBvpuh185Bo9KQPdc6Wl2qKcI9Psfj21NNb4O+pLsKxer94pY/pdsnpHr8eppV0Uey3+SzUP0\nNehks/yJbJ6iKgg74p2dwvIfZPMYKd2e98Sz/HfZPGeJgpCfZR7Jcs0v8yD/9hfvamxYHsmYJ7Hn\nKAOMeRTbAxjzLLZLGPMwti9jzNPYns+Yx7FdizHPY3sUYx7I9kHGPJHtgYx5JNsbGPNMttsy5qFs\nL2DMU9k+nTGPZbuQMc9l+xBjHvyV+2PMk79yf4x59FfujzHPZjtzHs525jyd7cx5PDTng53vrfc3\nINrpmocgAAA=\n' p57 sg7 g8 sg9 I18 ssS'gist_earth' p58 (dp59 g2 S'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\nLvw/rVhEq4cgAAA=\n' p60 sg7 g8 sg9 I19 ssS'Dark2' p61 (dp62 g2 S'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\n' p63 sg7 g8 sg9 I20 ssS'RdBu' p64 (dp65 g2 S'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\n5v4gaLXl/wBfaaFDhyAAAA==\n' p66 sg7 g8 sg9 I21 ssS'gist_yarg' p67 (dp68 g2 S'H4sIAP6B70kC/3XZaVTTVxrH8YRFFFFxrR4wKm5oa5xCrahBtFZBCWitaKUKsoglrcimZamC1QKC\nuzZVSp2KGDoVETSCBRHEiOgQRHbZk5BACBD3BW2ZHutzT3p+HV7wefGNxwPcPP97b2KN/IK2bw2O\n5PttC93M37r9q/BAn9BQn0hz79DNftuCwsJDt/uFm4dw/3qVeZD/XzXEyJXznQfXN351iLGtK9eN\nw3U1iXv7Gv/wyODN5iEmHkYBDq4cV+6frzG1dTX24Arc3d1X9P/59eabKyfcd98azkTO268HTv/P\nmpmx00yUepD6OQ+O20CZHqTOy10VZiHRg9T3FTw4aZmgB6lnGq8vGiXSg9QXBJ3WjBXqQeql9VqL\n8Xw9SL0kfonzZK4epF6REjjftLIXpL7R7OKmQdJekLo2pC9uiLgXpL6j6aPzwyN6QerGzklVoz17\nQeqHsqtejhP0gtStra0n8ni9IPXaBBsXG04vSH3k3cSgacoekPrP86qOz5T1gNRnpVnl8yU9IPXf\nhvor7BJ6QOrLIjPM5op6QOpVqiezFgh7QOre7o5rnPg9IPX6kME1gcY9IPXIyeWx0yu7QeqmB8ek\nvyvtBqkffeElny3uBqlP8Et/bB/RDVLPkOvHOXh2g9QdHBwWCQTdIPWbqbEBi3jdIPVVnqaHP+Z0\ng9ST7eU5LkodSH36qdHNQpkOpH5poJfxJxIdSH1RmGSGR4IOpC5v7l25XqQDqa93mbtjo1AHUtdc\njEnx4etA1ucf3pxlqgOp98db3l5e2QVS3//oM72btAukPm7j6dGrxV0gdckt7YK1EV0gdTs7O19P\nzy6QemFKVIKXoAukLjS7kenL6wIN138Apwukvrnp01cipRak/mhZyqRtMi1IfVd2u3OYRAtSH2w9\nK+jrBC1I/UTc9uPRIi1IfcrDgrwYoRaknv35AMUevhakHr3/p95nZlqQ+p1//TDkq8pOkPq6H1vt\ng6WdIPV2U1vPcHEnSD04ODg2IqITpP57wxXJN56dIPXEpVx5rKATpD4ma/njvbxOkLpP3eAJ+zid\nIPX01584H1B2gNQ/2JK89YisA6R+/Z7y2PeSDpC6m+DdvJMJHSD1BklY20+iDpD6luFXB6QKO0Dq\nj6NNZkn4HSDb58hPxjmad6Bv+5BPvl9/sFIDUj+Z3xxzVKoBqU+bNk0iFmtANh8PB5UlR2hANh9f\n5Tw65akBqZdt7h97RqAB2XyscHZK52lAw/l3jqMBqYeerU+6oFSDbD4Om3TxkkwNsvkYFVifK1GD\n1Meqs//IT1CD1M+u7JtSJFKDbD7mfeQqE6pB6temJIWU8tUgdTEvfdheCzXI5uNLq1NZle0gdX9/\nf5lU2g5Sf1ie0XVF3A6y+TjvqWVBRDtI3TzNce51z3aQ+g9D4zbeFLSDbD5Glu+5zWsHqWepxvwq\n57SD1B3dvSvuKVUgm49X0p/VyFQgm4+TH1g3SFQgddUBhyUtCSqQzccXsYFKkQpk89H39iGNUAWy\n84V8eE4XXwVSfy9YeuLOUBVIfenSperKSiVIffYQ3cA6qRKkfvVr+9mNYiVIfbky2qM1Qgmy85dQ\nFq3yVILUfXMtUjsESpC6fpJHqY6nBA2ff3qOEqQ+8Ll61BOlAqT+vQ9/wQuZAqQ+qWyHz2uJAmTn\nuw8L4zn7FCA73502yzT5UgGy893gVdUD3RQg9TU7TvRZzFaA1BVtbROHD1eA1BvFq4PG1bSB1Psu\nh7i9lLaB1OMn5oX9Lm4DqY9KMkrmRraB7Hz1dEWR6edtIDtfbTqmGeTYBlLPu9NoMXRCG8jmw600\nuxHcNpCtnzJZ2nslrSD7+13w0n6Q2ApSv6BTLHZ0bwXZ+tLu3bV0RCtIPci8v8WttgVk69/m3Mi1\nyS0g218Os/7Gy6sFZOeLsrKMAJsWkM23w3uNijKbQfZ802kHzRU1g2x/vvuLACd+M0jd1dws3flR\nE8jOD/kHX63MaQLZzzdjysefRTWB1MOeSA9vWtgEsp/PebHiC6MmkP1+ZjVODSlpBNl8DfDaFZnY\nCLLz4RZu/m73RpC6y3fRVokjGkHqu3+evupobQNIfeaBE1eSkxtAtn91Xq5N9WoAqe8tueZ2zqYB\nNFgf3cJL90HqLf3tqdGi+yDb/323R7WHfx9k5/d5gx32P6oHqavP/Bx7PKceZPcLoe/LU6LqQepp\nxQXjzy6sB6l3n1vne96oHqTup2jIu1xSB7L7GfXO5wWJdSD1yZwX60rc60A233hp+8tH1IFsfQwe\n/bS2thak/m3xzamtybUg9dSPFyR2eNWC1BcrlMV6m1qQ+stXxbyG3BrQ4J50YpGo5p9809fmxoeX\n8mtANh944wsqHlWDbH/24Pyg+znVINs/Os73VERVg2z/OLP6lHZhNUh94YZ1zx8aVYNsf+vXZ99X\nUgWy/VdU+HGjpCqQ7b9TJt42X1kFsvPbviPvjxxZBVLf4PiRr1VdJcjOl8W5JZN/rASpW23w7n/X\nuxJk/XXz5x9MrgSpX376YuyWq/dA6tvnmGSbfHkPZPdnJ0++tJh9D6R+N2jmstGPK0B2fruSIx6f\nWwFSn/HLqsap0RUgO1/VVdnznSpA6oMylmz90LgCNHh+79558C7I9hdz5usOjLkLGjx/r546VQ5S\n/yp45/gL08pBg+enpjBTDlLXnc9dV/GhHDR4/lkqrpWB1AN1D488XFYGUncvN1rbufu/IPVO2/eE\nR2vvgGz/eN9Nnep1B6QeEBAQd0lzG2T3+8LkYbKg26DB86Go+lkpSH1JoWaeelcpaDDf+54OKAWp\nP3/f3n/AoVugwXye8847t0C2/w/YIZ7+7xKQ+uudPt4O00tA9nw4k1XhcuEmaDAf962fexNknz8p\nusxFhTLQYL5dinKWgdTnOa4QGSXdANn7q3aZl1VdMfj3+VMMsvsbl2MJgo7roMH8mOe27TpIPTa/\n9eLG50Wgwfs/LCimCKTOHWvVtMusCKSeNMJaMGFCIcg+n8ht+cNp9jXQcP/p41QAUv+P3adh3668\nChq+f85454Ps/vz8rQ7ZtjzQcP1rYn4DqZ+xFWwwO3wFZM/v5f2vHWxzwb+vz8sg+/wscawqsFAK\nUv+JN3Vp0t1LIDufb8uem9F6ETRYH7/J9dkgu19Zs2J/b38WSD3ObCjf0jILpG6x4cYNn0WZoOHf\nJ21TBsjOl4LEXzWxv4Ls8+O0mIMzTv8CsvevXdixL69LQMPfT6YiDWTr33bDNw+4Z0DqK+ar2uxs\nToPs/kHi7HJZmwJSn2M+84nouhj8+/9/DGT7C7k0rnPvQdDw3z8xjQepd51NnfRsSAxouD+1tAz+\nJ9/0cF/+/wDqjVichyAAAA==\n' p69 sg7 g8 sg9 I22 ssS'BuGn' p70 (dp71 g2 S'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\n67rByJGEe+6c9n+x8bMYhyAAAA==\n' p72 sg7 g8 sg9 I24 ssS'prism' p73 (dp74 g2 S'H4sIAP6B70kC/4WZfTxU6xbHh1AJyUvJ20E55RRHKqWrHinlEjXS4RZJRSKlCUXSm8RxRTgUUSaV\nIknSSag4oqjk/SVlGCJh5CDdymU7Zu/Ze9aYP3yY78Yzz1rPWr/1e04I79rv5e7ho7PrAMNJx91r\nn+deRwbD0UfcgeG068D+Q54Mr12e4geFxp4S3797jB4UNqed2SK086zVwUnzzYUsaELmIoH/PLPb\n08fDSfygyBZh5+XmNHOhkWdE55tP2iJkaGlpaTY88sK+mNM8dwZb09Ro/7w4iMb3Nf4+B90t1s5I\nvPwYCeavQR7wG9Pk4YOaCX7/Pcj3x9tNLyxomeDvfwB53PT2o7dTOif4/z0Uzvim8GtlbTviv08c\ntO730PxOThPIl+iKpIrJlIO8abtrTNoMJsjHv8tf3OmnZ9CBhnQ3S+grlVCej37qIG1GZ6EqP6P3\nbvaNFF6ZkLy+oLwcRacvsEuIbqPwsopQz8hZyahA40FxoH83l+eJ66lozXgK7tuhJFcNNdkKkAeu\nMcxKiW8E+YZesWtKcmyQn0bywdts8LjuYIYp9Ax1cPnPzQ8W/OD+TKON7XczlxcHbH115dInLpf2\n6/U3tK7kcrd53z3WIDzuY/HIQOR1GF084vwiOgtcJ29cqDxZ/D7jtFcVyHnjRuUnBls1bzGbQc4b\nVyq/vfdgRMTvH0HOG3ecW5qKCYsrdYL5qVHt+4f64haQfyp6R2dEV4N807mfWs4oPpow/xOO1250\nOdqJgoQSnmyXyac8z5C0aDke2YKUW50MaHW1FP5f/dNdOjY1KHv3qmAXazaFF5vepR9RyUMfqp/3\nbzL8xOUmR6WVgmbCcW/N/SNWW7oM5F/s9ug8jK0Huai/siMzAo5rt7PMV2JcV3b7dc4lrO/81PuX\nVht0kPIb/3xLUm0QuwGve8WypXOTa/H9qbb8yiLGfSz/8yn5f6MicGmbUgq4znXY+XgG8jd5RZct\nFsN1Yhg7Pw0gj6EfMAw7yQJ5Gna+WkFeWPA1+fDeDpC/xc5fF4UrRfVVIu771PzsVndxseH+XyoP\ndHg42KjYAHLbgbUrFbn7Bue/W0mhRkJuF7qW9KZDSjaD8vya/6zIul/VinwtQiKjCPVtnOu/so+M\nbWhArQr0LD1CfRznpVj9L0aSIW81lL7i9XVOvs/gUfkkcN8epp3U9FZ8AXJv74jrz2Lgvl+E9Rc4\nrtvPTXdrrsLjKr7CNW8roT8N62ZInjnSzuXNWL3B+1tihXX6PCU8/xkp11/IEvqjsfeg1YtcPO5r\nsXpUQsn/4x3LDQ/IRYDr5K1LVM5h1JfO6oXrBG/dovKklC9WnjuaQM5b16i8TCXgsZ1pO8h56x7O\nJzc13fDi6iJqfla7b/lXKFd3UfmGwcj4f3N1HZXXan4N6k14PWH+r5t1r79iuAdx3tp5/iw6sIrM\nVa/NC+6c1Y6C3Bwb6Feof29A75LyJN0mJBdb9GyIz3ounVTfsTS8DOmhno2tfD6P0c3NrHCJILTt\nrm/OS8J+xLFq5M/L/QXua1tYrmplVCVc93TCMhx+gnWvxB7dq/2VcFzdbO/4FxN0bw5W33G9KvE/\nurrbVrzvRyUa/fmYoFdvx/9dIPkdj3stVv9xvWppFOOcTtDFRpVxn7ukmJTzkYb1h0fgOpdZ1vcV\n2L+EdSDWP6pB7jdNpCLD/x3Iq7H+As8H3Yclq1fPg+cDsUWj/QeeD1Q/XjD+ZTp1Poh6aCxVQNCj\nZL57/YPE+hnNIHe5ZePK3lwJcjWFSSaW0hkTno86jpyd+kic4wxTw3xjqP3krn9wz3JGM0oQ7lI7\np0TtRzKzv4kmylWhw31Cx+7w6WdTKqLuGcneR5edQjtiCP0wUctOwlYlB9atN9ujWeffgPyiafYv\n7spvQV7XZ+LgtxmOK+t4as+0QVwfHcH6La5XT2lsjEi7iPf9TV3aIf16uF6dU9i7hE6oe5ZYP8b1\n6l97omr63uH66Mv6P8M1Zz+i5H91mXxsh0wGuM71InOFNeuKQT4jPCHO1VpAnVBZkGVsCO/TQNU2\nF0kZWEd2KTO9hkTh+YC9q/1U6xCsjxpu6YS/6aLqI+bjmbRjBD1K5mN6hA3ypKxhC1s+ep3LMb2T\nP2H+Cw22WzSMzHmDfOMzcj4C7FmfM9mIHN9xvlMzqUMutg5tJOXHOFc7pageFFWAyPkl80nD9KVU\nOrhvmpmZJceU4bqnNpTi80KhDuQKpXliKW8E6KNEh9/m6ONxzSadT9G1wkOFtXjfP0U63yltSXGj\ncxW3P5Dqg1Xw+lXTVPH8N+VbXzhIfPZonWKC64zE6hzcJxlYnSwHeSxWZ+F9Ysnvya3iwPqo/1Vq\n33M23EenBn3WyquF9ZHq6uU7Mkqp+ige60vdYH56Yn2tDeQXsL7YCPLwytG+WjJh/g8ZV6vNeN+N\n9pbx6880Wn6ZWeb8kTp5jNTfx7lcrn/YCdV3yJukD7hxv/t6n0N8KUoj6QtNs5VXJSTCwX1z/CCe\nc2FmEcjzZGp6a+Thvt9mW5m/aBMc1/JiuyJnwtznS9Jnnm7De6cq4HG1Iuk7eSmmRGo2ro+sSfow\nK33tHUvCXDhlH6++tN38gd47TNAFJ/bx0acctA3TubB/KoXpZNg/fYDpbHiO+vB+VKcL0JFa+uJ3\nBPinc5uPGl0V4J8axj31jhHgn1pbT74dwsc/1cXmGtg/nXJodC6C/dM8bK6C/VOPztG5bGL/NEx5\n+dntBh2oMJTffEejOd9KVfSms9Bi0nw4zv08jO2UKspRPmm+5PqSjnazrRWT0UcD3vlUXtEx9Z40\n7J/qLBR2bZWBfRF6ScV3DwH+6Yr75aY/ZOG69jHiW/0Ggn8qRZrvQ5ZePtZD6Ptkf2Bh7Wq1CIJ/\nSvYXXvqy85cQ/NNkvv4EBzlhfRf20SKwvgr7p0OYzwL7p1qYTwPvk/bpx2pRAvxTE0NRmyAB/ql9\nv1monwD/1CstvMCDj3/aqUucK6j5WcAzt1C5Ps9cxKc/8MxdcP6buWhujRrp87V8/T0aTWPgYldK\nZAvKIfmD4/zvySrCZ21qkBjJXxznQecUkmpV89CvJH8yKHa/Qbw8HPdCE+3mz9NhX6Sd/cPLX4B/\nKmbcLnJGgH/qkD1kHk6I63OSvytkf6lbj+CfXiH5w8zhVeerCP4ph83rL69hshYfIcTdiK8/zUH5\nmK8E+6dCxaN1BfZPtTCfXUCdwHx62D9d2Wi6zEeAf0qPPufuLsA/ddpYedVRgH/qM0Wxfgsf/7SJ\nx1el5mcyj29L5c08vjCVsy8QfWc4/0UzZbRHfb4qB373OzRa4+qzT1hVrWgd6X5onLteHrzxuaEB\nmZPul7j3Tt5P49coFKNDpPupG0L6S67Lwv5pYF56Jk2Af0r/odUdIsA/vfPq+5NpAvzTJ6yBnAqC\nf0q+33MMuOjgTfBP40j3g8LzDIVnE3yRNtL9YtLzd0mPCP7pwkX87idH5k/70X2H/VNVbF9h/xRh\n96xwnTiA3dPC/ummZeUW2wX4py6cWQFWAvzTEzfts9cJ8E8v7LzKWcHHP03kuVem5qcPz701lTN5\n7sWp/ArPvTuc/+B8slPn/3IdJp+HIAAA\n' p75 sg7 g8 sg9 I23 ssS'PiYG' p76 (dp77 g2 S'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\n' p78 sg7 g8 sg9 I26 ssS'YlOrBr' p79 (dp80 g2 S'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\n' p81 sg7 g8 sg9 I27 ssS'YlGn' p82 (dp83 g2 S'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\nodxvo9X/AYMdFxeHIAAA\n' p84 sg7 g8 sg9 I28 ssS'pink' p85 (dp86 g2 S'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\n' p87 sg7 g8 sg9 I11 ssS'RdPu' p88 (dp89 g2 S'H4sIAP6B70kC/1WZeTzV2RvHSat2WqdSUmlPtKc5UqKYMiYlGqWotNjCtMhoo0UlLVoUbUqKtBAi\n2rRRWbNc293vde8xaRlM0+88Z/ycc88fvHhf957veT7P83yeI6TdWq+ALT7bJ6719vOYuCVgs7+n\nm5+f23bdVX4ea729tvr7Baz11/XV/u9Vul7u/1HfdrZa+x2114Q5+OqMttX+RUvbtn1o62vc/bf7\neOj6tndst26GrZatNnlNh9G2Oo7a5osXL170gyz6xVbLf83BpVrDtFpXA4KvP/5Vw/eQ4GD6nf6+\nuampqeU7Rhnp6envpWqUlZk5x2ED458bGxu/tWAUQNbl92q0c+fOlpEOjKtVKlVjE0YmZPmnqdHM\nmTPTmmYzLpNKpfgbRkqFQmF1WY2+ff0amDeS8bra2lrlF4zirl271v+wGj24f3/KpZ6MV1ZUVEgb\nMVpNlnyrGvn5+X3yb1K18ZLi4mLhXxgNIitjpRr2ccdGyPj7d+/eVWNMXxdupYb9bhmcx/jrV69e\nVagwOh4REeE6UY1uJSSMa0hh/NnTp09LlRjZkmXSX408PT3lT2MZJ+eVWSjHqCNZ7bTUyNjY+HrU\nIcYfpqamvpNilJOdnV0oUyGxSOS+yZ/xu8nJyW/EGAWRg732QYWuXL48HLkyTvaTkCvEaBpZf6Sr\n4Bxq9GwYh3N7WovRXw0NDTZXVMjAwOCiZDLjsTExMY+rMX2fn8JVcJ4u6YMYP3f27NkMAUbryKon\n+yI/DzzagfGTJ06cSK3AyJCsrN9VaPny5aVuuL6NHz1y5Mi9MkzjdGyBCvXt2/fU1DLGD4SFhSWV\nYhR1+vRpt0kqVFhQ4NDlKeN7du/enVCM0a9kmQ1QQRx6CW4xTo5l5/VCjLqR1UFbhYjM8++cZhx0\neeUDRrkvXrwokdfD68L3hTDuTVbMO0w/50ZBPcR7odNGxkk8Pc/nYWRO1o6Methvp/FLGV9LVtQb\nDLr9anu1HllZWT3/MYfx38k68QrTOA45Uo90dHT2FhozTs5r+bFcjDaThQPqQQcW13szDs99+DkG\n3RjnuNaj4ODgf3e0KNs46C7sKaZ5EmldD/t8tFjMONmP1d4cjC5ER0e7m9RDPm8f/k7Jx//PmMdU\n/44PBtRDfAL6PGSc/F3QhSyM9u/b1/7P70rQ85Dyc0peH9vPZ2JkZmZ2z6ZOCTp5EbOLcRLXwLOP\n6P7c9HKVqGfPnt4eq5W8frZGZdD86lWZoIR86D9uHuPk9z6n0jEi6/G1CCXoO7thpJLX15YTaZjm\nrXeAEnXp0sUzpTPjhw8d2nj8IX3+wTOdlRCH3kFKBa+/9cdSaf6+aYeUyNnZOX1uPuPkud2PpGB6\nbm+NlBC/tZ2SFbw+3Q4/wCj+xo3Rp8nnkjzqmneCcRIv14P3Mc2LVSoFWrp06f3IQAWvX5ewe7Q+\nhI4pUKDv//yz0mkF49u2bXPafxfTuteYooB87mBgruD17bg3GYMOhY/OK0D/iUIDxkk9dNh9ByM9\nPb3I0BAF6HRZvLaC1/+SP5No/bGw91CAHn54ieRtnMjSblcihtfhgYsUyMbG5saUXDmfHwt33sa0\nrggnKqDO2DfHM070sGD7LYzy3r61u62vAL00PQ6X8/kz748EWt9aAv+WI0tLy8v7vRkn8bYIuInR\n2LFjb1oI5NAnFtk6yPn8mrM1HqOyjx+ddJ/IQU+NvaYyTuI5y/cGpnlbFCeH/Igu6S/n82+693Va\nP1MuHJZD/Z0f3Sxr4yReU7bEYVqX1/vIQW8qN4GMz8/Jm65h+Fz9yY5yeJ/TxtmMk3hM9LyK4bme\nNM+Uo+qqqp9Vl2V8/o5bf4XWZ59nBnLYp/TufsbJeY/2uIwhLkOP6sihf0Vs2yDj83vk2kuY1r3l\nMhmcw4yfbRknnzvcLRaDroIM82Sg11qdiTJenxcNYqh+roxMkkH+RTn0lfH6O/PTBbo/k7IDMprP\n5royXl+R/c5jeJ+s8DUyqsdRP6S8fsL1ztL6bGdhLqPn2euzlNdHaI8oDLooaySfS+qIfrNMysc/\nRPcUrX/rr2Mp6PiTUCDl47uj4wmaH5+dX0lhn+/zCqR8/PzbHafnv6fHFSn0+6TUXCkfH69/j2L4\nfc8nQVKUlJh49NIjKX/+G5rDMejsQsAyKa03h5Ol/Pmu+XqI+pexY0ykNF8C4hgnelv56QCGc3xY\n2UVK473qPONEL8vUoTT/F0QIJaBz3YURjJN42yv2UX0VzsuUQH2Tm+5nnLx+kWQP7b9u305LwE/l\nDt7B+IgRI+bXhWB4LvVNHwn0wbiO3oyTvP25Kpj2tyDXRRKoL/sb1jI+YMCAGeVBGPpiF70REloP\ny50YJ/EwLdlB61vU8+9ims/PfmGcxH18wTaaPyO2l4qpHhMtGSfPPSo/EIOO7o5PFsNzaJ+Zzjj5\nMuy1P4bvFjWHxPC9Zvd4Ke9PB77wwxCXvBPuYsivrE2GUt6f6j/xof7PxfpnMfSvC479pLw/7Z7l\nReu3rLm/mPY71FXK+9NO6ZtpfQhM/EsEdcpljJaU96faKRsx+ECdNW9EtN7of5Hw/rQleQP1L8f7\nXhOB/x34XS7h/emX2+sw7NvgVbAIzvtvSZWE96c43p36g1tBTiKo3yXvCyW8P5VfW0P700wTUxG8\n34P0lxLenwovrab1L1fYVQR5cvJqpoTv74PnuFJ/sntstRDqebxxkoTvz/rTXTAKP3y4IuO6EE2Y\nMGHLj3MSvr/qTnbCyMPDY+ovPkJaJ0pDJXx/1B7nSPv3saoZQpqniX4Svr78PcIBo4EDB8q9tYU0\nT0JdJXx/wgZLMGr89MlS+3Vdm065+iEZYEf7S3RkZB3VybRpEr4/CPQWYnQ9Lu6rkUsdjVOP4RK+\nPhR1W4BRSEiI/QOjOnpO4u4Svj6/6TgPQ1xvLqivhZ8PZzaJ+fx/omWB0ZQpU9p/vF8L/mbJKTHj\nMO80m2PUo0cPV89dteBv9bZ8EPP5fefzTAw6S222qoX6WjI/k3GYF9TTMHqSk9M7vEct7OPc4Hgx\nn78XZWYYRZ8/v2lIaQ387Pr5JOPgt+tMMAoMCHieGFMDejV8GyLm8zO8cgJGS5YsGWqxoQbyX3xl\nM+PgV0vGYjRmzJjtH0xqqD52Oon5/Nv+3pjWr7jnn6qpPn6bzzj0tdcjMEpPS1sh21tN9THORMzn\n1/pnhhh5bdnSvWvfaqqPdoMZJ3nhmmWA0XBDw5wJcVVUH+WdxHz+OD4chFFxUZG//fQqqo+7jaI2\nDr7j7gCi/wMHRm99KaD6OFQt4vNj3q2+tP5VnlohaMvj/3My986K08OoAeNjDxWVVB8zU0X8fDY5\ntidGV69cmVexs5Lqo/cVxsEXnuuGkdPy5d++d6uk+pAfFfH+e+jJLqQ/du2aMOxiBdVHzg4R75/7\nHe2I0eOsLNd5kyqoPs6sE/H+t/sBHVo/9dZll1N9+DiIeP/afo8WRqNGjXpx4Ndyqg+bnxknfuGj\n6C81MjIy2pssLqPxGTtJxOf3oTuldK5dWBNURvfXb6SI72/mQZlqdCcpqWfPPmU0ftqDRBrzv/UV\nNfr65UvxnISPdP+qXhrPH6N/UE192WbLjzS+ZR1FGvNPtZca7dmzZ835slL6fM//EfL61ElYqkav\nXr4c/dqnlMY/+RPjZD/3A2ep4dzx351K6fNfkAn5+rHOcpgaOTo6PjCOKaH6OFgl5PvfgB4d1ZBf\nO5dNK6H5E1Ak1LgfKFOq4Pdz9+cVU/24vWb8/3P76NGjO993L6b5ZZfNOMmXCb6pKuTl5ZVf11JE\n9TUjRcjrv8r8gorW7d4nimj+jbgl5OejiM57VailudnZYmwR1V+vy0K+PlkWbVChuXPnGnrnFNL8\n/CdKqHF/E7NYhcgYLr3gVEj1KTsi1Lg/2DRFhfLz8m6/xQVUH0V7hRrz6/SfVKhPnz7+LaEFVB/Z\n2xknfqWLjpaK9sWxBgVUHwneQj6/0vPF9Sg2NlZrxYMPVB9RHkLeP24+96YeSSSSF2F2H6g+9roI\n+fpnsC65Hp7rSIrwPdWH969CjfudyVH1yH/r1t/EO95TfbhYCzXuF74H1UN9GthH7z3Vh/Ucxokf\nNXu1hs7j2fsd3lF9mJkxTnyN6KRNPQoPD7cThuRTfQwdw/hpslZPrIf+VmaRlEf10XWokPeXNuP7\n1CNyzB4XBW+pPr72EWrcv31rUiJTU9NPLV3fUn3U6Qo17n+eVpO5PCsreMWsN1Qf+VpCjfuHY8+V\noIuuqRteU32kf63j59/rwy4qkbu7e8zZs6/QpdjYCDNhnYa/euKlRB06dJjx481LqLPzoQ/z/n4t\nmZvJvt+7/5sL8W7Kv1PH6/ff9r2UaMGCBZ6vTXLRnDlzbkdH1fH57RtXo0BSiUTbZO0L5Ovr67Yx\nuE7Dv1iTeZvs89ypU8/hc/rO8KjT8M/y3QrIL7OW3GeovKzsVQe7Or7+vjzkoID68GZ181Pow8GF\npowTXzh7vJECbfT0dH8x/inki+mlgXV8fb2d1yhHurq638eteoICAwMlXlp1Gv7U+5kcJdy8eer4\n8Rz4fs5cWqsxH/U6JUd2trYTvz3NhvNYrJtfy+un/V0POfpYWnokrf4x0tfX1wEfweXXH79Nk4N+\nHu/7NQtZW1unXjtfq+H/PneUIxdn54YlKY+g3mzauqdWY/44XSqD+mU4aFAG1Omhcz0ZJ3NB/vQb\nMlRZWekg+TMNiYTCwh72jBM/PrdsmwzyYl+yKBXq4YHKabW8P7y3Y6EMrVq16kHQwhRkZ2dnfnNI\nrYa/H/yTDPJbYp14H3xWwx/tazXmy0yFFPKmv77+PagPV62UNW2czFW6qzKkaLiR0cKqP5KRXCZz\n0i+o4fW9Sytcil6+fPnZIS0JDRkypHvtwxoN/3xppRTOf7l8zy3QWw74IH5+mzdBit7m5aX9aReP\nQvfvDwgKreH7R5HouwTZ29sP7tcvDurEmEVbanh/Zh2aL0HvCwqCb1Vfhn4n6L+0hr8fSzOOkaAb\nN28+2e8Qg4YPHx4pnlWjMR+98pZAXMLuis+gZcuWLbhnWKMxn2+0IPz+fbvqbZGItOPmkM41vL/q\n1a23BPls3WpEQoOCd+36/UJVNX+/svd2LfFLLc3OwqBAJBGLn8+4Vc3PjxvDEqmfGtarl0/rvM14\n9+7d8wx2MP7fvMo48UWTUqwY/2/eY5z0g8hfejP+37zEeMqDB19ElaI2/jA1tfTsSsbPnjnjFHSD\nccjHXYur+f6aoe/PODyvmwXjrq6uBgmIceLHV1iZMk764m7LroyT/jdpzIhqfn4WlZUI2zjka/d+\njHfq1Mna9zLjpJ+VN3RiXCGXx3f2Ypz06ztFTVW8P+0WO5NxuA95qGSc6MJ7egfGIZ+jBYxHRkYW\n5L+va+Mwj4e8q+LvB6eui2Yc5ln3HMadnJzOfF/POJmPBdb3GJ89e3bLSTPGId/HXavi+6/r+B+1\nbZz0n4M9oxjX1tbOefqacbJWNR5gnNQbI5fTjJN5ZWrpDsZzX7wI/eTGONSDjM2M34yPlx+cwHhO\ndnbNRVfGoS8bNtW0ceIvUvbYM+7j7Z308Bnj0M/WWTLu4OCgZx/BONSLRVMYnzp1aoDUhXG4L5k4\nqor3jx+DjRmH+wa9AYwTXxwwXlLdxqHffenC+KOMjNkuuxiHelLWItCYrw/1YZzMy8cyVYyTfpH7\nMKGqjVtZWXlcqhbw/58aYmpTBf3h/O8/e8N99KCGFwJ+/vi6WSyg/59xNvcCn5WXcUvAz+fvru8R\noMjjx48vn7UF+ltwWKSA9zc36oYK0BESiKUzNoP+TX7bJuD7y+4hmXSuCvt12iY43zoDVwHvP1Y4\nOVeC/9+zeMpG8KsnFPMEfH0xPfGtAureLltTT/AHViljBLw/6pp/sgL6zDYbkw3QT7/t7sn4vn37\nRJ1NK8AfbrWauB78S/wvXyr5/pI57105+HMvy/HrIL+dB1ZU8v7jdPDmcvAPnmisB+inmzibcfh/\nR1qXcvBX7uaj3SFfsu7EVfLzr/XnuDK0mjTQmaPWQr/yDgpnnOh52KT5ZeBrnaeNWAPxNrTxq+T7\nS5Nn7Ue0jAwwZsPdwK8W6jtV8v6j4GrwR+hf9ibDVkP92lc9p5K/30ioHvSR+lRnc1eI87QEo0q+\n/u37Ka0U8ojEbyXEUxbYhXH4f4XjstLW+DhDvM5a4gp+Pp8a0VjSev5OEA/bHsWMJyYm9ngTUdJ6\nvsvgvL+XpVfw/lTaYWJJ6/kthfNMuhZbwd8PZ1u8KW49Hwc4r9W+oRX8/cvZnRuKW5/fns7JczZX\n8PeXfikdilufzw6e91lnhwrefyz663JR6/5t4HkCiqZX8P7UaLxFUev+5sN+jWOHMB4dHf3POkFh\n6+dbwH7KNukwHhgQUHxpR2Hr+8+Azzs0XV7O308nVvb//9+bwH2duc67ct5/hPV/UNDKh8Hr1fn3\ny3l/utrBoQBpsRV77hzj/msm/g9W+jIchyAAAA==\n' p90 sg7 g8 sg9 I30 ssS'Greens' p91 (dp92 g2 S'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\n' p93 sg7 g8 sg9 I31 ssS'PRGn' p94 (dp95 g2 S'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\n' p96 sg7 g8 sg9 I32 ssS'gist_heat' p97 (dp98 g2 S'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\nz/4z74EYtf8BjnrHFYcgAAA=\n' p99 sg7 g8 sg9 I33 ssS'YlGnBu' p100 (dp101 g2 S'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\nAA==\n' p102 sg7 g8 sg9 I34 ssS'BuPu' p103 (dp104 g2 S'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\n' p105 sg7 g8 sg9 I57 ssS'RdYlBu' p106 (dp107 g2 S'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\nm8T69f++1yrPzUXu7aj0P9JG22eHIAAA\n' p108 sg7 g8 sg9 I35 ssS'Paired' p109 (dp110 g2 S'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\nzVklhyAAAA==\n' p111 sg7 g8 sg9 I36 ssS'flag' p112 (dp113 g2 S'H4sIAP6B70kC/4WZeVBUVxbGadxSjDIoRJvGBRxMLKdENMJAJLb7RiAiGpWAioBDEBE64IASdDJB\nIIpgNYOKCEIZxxXFDQkMg1IsoiP7JsgOii10KwUMiaPzfP3uaTzv3Xn3D35V7+tbdN/z7rnfPeeQ\nvldAyJ7AMCuvvQofqz0h/sHfeioUnmEG2xU+XnsDvgtWhHgFGwRJtJ8yCPDWqkH6jnqRmyQ7ozYE\njZrtKHHSkziOPsx9xjs4LNDHIGj0Jv1ddo56jhLmM2NmO47aJHFwdnZe944Z7B9HveCdMRv1zPW4\noZHrCQ7yXCPPLJl7Iy21DCistwCx/uPX6Suzs54BheergeT5HwpXtYWEPgfieUMbcoJj41uBWE+8\nt91onUsFEOutVbHByikngVjHK6J4I51XXf+cqq/6Kfa+StNK1Rdaj74ydlIlVW/d5nciY2I66MPW\nruNtzR5S41MTvqRlt8dTqp54/Y/uKYndVL1gZlbJ4Yg+0fjnuBrU5J56AMS6rWHy8mvJTUCsrw7b\narjEthuI9e9UnftnT+8Dkuc70uOk6uEeIJ6nXc92INaNwl9FOGysBvLiz673DaBY/DMaPHc5ub2g\n6of+XDvsrWin6ude/F02bFJD1c1N34xJM7kN+rTcefNcPi6kxsfrfkZgmNsTqu5e1W456+Muqn54\nZcDxCf99KRr/oubuoTplAVD4/WgAYt0g2aiu6FYnUPj9eQkkz1MO1n/le0AFxPMUE5w6Dio7gFg/\navu3XqvNdUCsl6zJdAmdlgcUi3/vnUtnihpVVP3eihnzm/s7qPqcnz+NUU2pp78ff7XYYROfD7rZ\nMem5+ul51Pi0jJumHwW/i69PHTzVexnWha+v8Z3llgDrSo9/RtVhm26zHCDWvzwVuqs0sRaI9dq8\n4lSnzzqAWB87/32eUwHJ80fuFlFtb14C8byz7H7qAmK9W2PibsHsD0KsT2X3WyFQLP5mCf3Vco9e\nqt5n4eu7OaSLqh/enj30VNZI1bcMrvhCllgE+s/nKnoMjW9Q47Pf6YgyAfIaX++SutxZAHmRr084\n0jTT7Nce0fiHVR0ctDPPAGK9jM0LVUCsn2yL8K3ObAPy9iGbN3qAkDc+98tzY84nQjyvvbjZRcGc\nb4S8/HD5fKkxcz4SYn3FsRkdkbKHQLH4+wY5jM9u7qPqdu+up5YPdlN1Q5njrvJpzVTdfbev7N3p\nR6BLVHPyTKadpMZHohy2TAdfw9cvLvroYDb4Ir5ecVWpSgNfRY+/x9vicYkzo4FYd7XcFNMVXw7E\nutto2/nfWLcCsf7TvntnlkufA8nzVVNuDlS9UwPxvOlsPn0OxPrgguSpo5j/R4j1ZDbflgOxvuSi\na1v8+Gig2Puxr2m4dm3o//GHfgF+nvF0fzhHU/jbq/UVVP1srmbOpEk6n/r7zDL/7WceUeNXnxsR\nd2h6M1VPKF93a/YQ3R/mLqs1n9gi7g9L2HPhIRDrG52f9Bcw+48Q6zvZc6MbiPWk342uusHsf0Ly\nfNEPgfneTL4gxPOMjbpcw5l8Q8g7lxbdfPwpk68IsW6e234lS5YBFIt/QvYywwKeT9UN79VZaU8m\n0v2h76XNfp2u1XR/KB210tlI51NPO1yJ23+iiBqfFP1e82NmjVT9L/2S76+F0P1hqk9szwk47+jx\nX7jda4tkciEQ678q10RWMOcvIdbnsj63C8jLP1Y5p+0Zn0pInoey56kKiOet7517ZGBBBxDrzux5\nWwvE+n9W342fZZoDFIt/ZeKRlQ4H6P4w3VKvwVVJ94ed3j31tV/XUXUj/XXvpkzV+dSH+aaOLsfz\nqfE5OmD1JztpPVXfGunb2dFP94e9cfZXLzeK+0M1973U6PsRjvfR/i5Cnr/k1iVDcH008jpuXevQ\n+kqGnjs1MvcFQjyv4UePttfMfYOQl3dmnesxSWoA8vb/DzKL6IQCoFj8d6D3k+cPBN/vEfs7QWh/\n6EYy2l/zKxNuLjG+TY3PG+l7X1tD1e9ExKjtFHR/2Mf65hei8Tcw1eYlQqwrubymFMxvjE/j8qJC\nMD8y+Z/Lq0kovy6unh+5cXofEM+bvPO81N+2G4j1Jy02W8adaQJi3WToi7f+SQ+AYvE/g84nrAcL\nnm+6cVLwfNSN+OoPz9dvy0+/7jVMp8bne7auUUnV96UtufsvDd0fZhg/srxQL+4PlZwvUSJ/Qijj\nfI1M0N9o5DacL7IR9EdM/ud8lQfyV/sz9+f+O1UNxPM2yNVfdWU9A/J8SVJx0XBqCxDrH/l7Nrqc\nLQPy4n/IP/iTMYOLgSLvRybyt1iPEfTHunFf0F+PvGCUfODPf1v9vq7xmBq/PdFqSWwz3R/uZesm\nz6j6+QuGblPkatH3Yyl3r1mK7jeEr0y19yJCrFtw9yoLwfuVRr6Wu5etRfezuKl2Udvse4B43q5L\nV2T7XNqAWA8PXOZuVlUJxPoOT3fTjbILQLH4t6H7LdYvCt6PdaNd8H49Ij+g+3kpW9cuocbH9rGH\nMqmR7g+Xb/38zu0auj/c/bBwZso/xf3hAa6ucQDVNwivcnWRq4L1EeaeM1NbVyHE+iquLrMK1WeO\nDh/Kq/vlBRDP844acFFXtgOxHjqnunRCbQ2QV9eQDhT1S+8CxeKvsv6wvoX1AsH6mG7YCtbXRsb/\nw/pcPZv/79Pvd2x+p/vDsWzdvJOqz+sLV1k6iNePw7m6ZjiqbxJacXVRK8H6qEZextVVywTrqxr5\nuCxtXZaQPH9Q+2BgPfP9CHn1Q+/FMb7M7yPEuqbTx16voR7Iqw9IUvK3TboPFIv/bFTf5i2ZYH18\nRP9BsL6uG7moPq/d/3dF9jfdH5amXFhdUEn3h6+NFt66/Iu4P9T60NtArF/j+iLXBPsjTB7n+ipx\ngv0VjbyY68sUo/7MmFuT5pYy+YkQz3u6NCq/jclvhFj3Sx36x2smPxLy7r3seV4CFIu/N+pvYd1B\nsD+G358mqn4d9ee08b8gEl+6P4xl+6ZtVP32Z6rwBfbi9eO3Htq+JiHWrbm+qLVgf5S5x3N91fWC\n/VWNPJrry0aj/uyy4994L2f8CSHPl84oH3Jj/A0h1ju3xkzuYvwRIdYt2Xg+BorFPw31t7EeJtgf\nH1E/EOyvj6gPCvbnaUMjD95p9T+sjMcRhyAAAA==\n' p114 sg7 g8 sg9 I37 ssS'Oranges' p115 (dp116 g2 S'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\nJ/u6GerqodT5Hl70P+LBPB+HIAAA\n' p117 sg7 g8 sg9 I38 ssS'hsv' p118 (dp119 g2 S'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\nAAA=\n' p120 sg7 g8 sg9 I39 ssS'BrBG' p121 (dp122 g2 S'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\nQiY5hyAAAA==\n' p123 sg7 g8 sg9 I40 ssS'Purples' p124 (dp125 g2 S'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\n' p126 sg7 g8 sg9 I41 ssS'cool' p127 (dp128 g2 S'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\n' p129 sg7 g8 sg9 I42 ssS'Reds' p130 (dp131 g2 S'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\n93/vKIexhyAAAA==\n' p132 sg7 g8 sg9 I29 ssS'gray' p133 (dp134 g2 S'H4sIAP6B70kC/4WXR3DVZRRHE8BNVvbelqwYu6ioezMyisSuDASccUYgCYkaKxvbjt7bOI7jOI5D\n77130nt5L6/X0HuR7+JvcecsOAvO4gRIXv7/77t3+pDKKXWTq+pHVE6tmTRict0XtZ+Pr6kZX182\nrmZS5dQp02pr6ipry6pLb31V2ZSJt2r1kPKSnypKJ/wypnro8PLSN0tKy4f9/P/XTKytr5pUVj2s\nYshnI8tLyktvfs0dw8uHVpSOGj169Bs3bmJ/lJfUTvhtbMmTJbfh5Ot3GlWw79Nh9QeNX2H/92fA\n6vcac2D/7y+A1R81lsD+/18Bq99t/A777+8PWP1+40/Yf/9/weoPG3/D/uf7B1Z/3PgX9j//Slj9\nLmMV7D+f1bD6PcYa2H9+a2H1+4x1sP9818PqDxgbYP/5b4TVHzI2wf73sxlWf8TYAvvf31ZY/TFj\nG+x/v9th9SeMHbD//e+E1WfOCOyE/fOxC1afNTOwC/bPz25YffaswG7YP197YPU5swN7YP/87YXV\n584J7IX987kPVp83N7AP9s/vflh9/rzAftg/3wdg9QXzAwdg//wfhNUXLggchP37cQhWX7QwcAj2\n789hWH3xosBh2L9fR2D1JYsDR2D//h2F1ZcuCRyF/ft5DFZftjRwDPbv73FYffmywHHYv98nYPUV\nywMnYP/+N8DqbxkNsD8fGmD1xgYD9udHI6z+ttEI+/OlEVZvajRgf/40wepjjCbYn09NsHpzkwH7\n86sZVn/HaIb9+dYMq7c0G7A//1pg9bFGC+zPxxZYvbXFgP352QqrVxitsD9fW2H1tlYD9udvG6z+\nrtEG+/O5DVZvbzNgf363w+rvGe2wP9/bYfWOdgP2538HrP6+0QH7+6EDVu/sMGB/f3TC6h8YnbC/\nXzph9a5OA/b3Txes/qHRBfv7qQtW7+4yYH9/dcPqHxndsL/fumH1nm4D9vdfD6z+sdED+/uxB1bv\n7TFgf3/2wuqfGL2wv197YfW+XgP2928frP6p0Qf7+7kPVu/vM2B/f/fD6uOMftjf7/2weqTfgP39\nH4HVnzIisJ8PIrB6lRGB/fwQgdVXrQxEYD9fRGD1UycDEdjPH1FY/WkjCvv5JAqrVxtR2M8vUVh9\n9apAFPbzTRRWP30qEIX9/DMAqz9jDMB+PhqA1WuMAdjPTwOw+prVgQHYz1cDsPqZ04EB2M9fMVj9\nWSMG+/ksBqtPM2Kwn99isPraNYEY7Oe7GKx+9kwgBvv5Lw6rP2fEYT8fxmH1WiMO+/kxDquvWxuI\nw36+jMPq584G4rCfPxOw+vNGAvbzaQJWrzMSsJ9fE7D6+nWBBOzn2wSsfv5cIAH7+TcJq79gJGE/\nHydh9S+NJOzn5ySsvmF9IAn7+ToJq184H0jCfv5OweovGinYz+cpWP0rIwX7+T0Fq2/cEEjBfr5P\nweoXLwRSsJ//07D6SCMN+/0gDat/baRhvz+kYfVNGwNp2O8XaVj90sVAGvb7RwZWf8nIwH4/ycDq\n9UYG9vtLBlbfvCmQgf1+k4HVL18KZGC//2Rh9ZeNLOz3oyys/o2Rhf3+lIXVt2wOZGG/X2Vh9SuX\nA1nY7185WP0VIwf7/SwHq39r5GC/v+Vg9a1bAjnY73c5WP3qlUAO9vtfHlYfZeRhvx/mYfXvjDzs\n98c8rL5tayAP+/0yD6tfuxrIw37/LMDqrxoF2O+nBVj9e6MA+/21AKtv3xYowH6/LcDq168FCrDf\nf4uw+mtGEfb7cRFW/8Eown5/LsLqO7YHirDfr4uw+o3rgSLs9+9BWP0Wg7Dfzwdh9R+NQdjv74Ow\n+s4dgUHY7/eDsPrtXDthxH+YNkXChyAAAA==\n' p135 sg7 g8 sg9 I44 ssS'Pastel1' p136 (dp137 g2 S'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\n' p138 sg7 g8 sg9 I45 ssS'gist_stern' p139 (dp140 g2 S'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\nH6G4OEmHIAAA\n' p141 sg7 g8 sg9 I46 ssS'GnBu' p142 (dp143 g2 S'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\ng7BOXWBmRnDBlqn/A7NPwNKHIAAA\n' p144 sg7 g8 sg9 I47 ssS'gist_ncar' p145 (dp146 g2 S'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\n' p147 sg7 g8 sg9 I48 ssS'Greys' p148 (dp149 g2 S'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\n' p150 sg7 g8 sg9 I49 ssS'RdGy' p151 (dp152 g2 S'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\nu7vGiO2xq13/D5WGciSHIAAA\n' p153 sg7 g8 sg9 I9 ssS'hot' p154 (dp155 g2 S'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\nK6l/AD/0x0SHIAAA\n' p156 sg7 g8 sg9 I25 ssS'YlOrRd' p157 (dp158 g2 S'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\n' p159 sg7 g8 sg9 I51 ssS'PuOr' p160 (dp161 g2 S'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\nfrYEN3yfXTXq/wBWmf+NhyAAAA==\n' p162 sg7 g8 sg9 I52 ssS'PuRd' p163 (dp164 g2 S'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\nYOPM/wEkxE2ghyAAAA==\n' p165 sg7 g8 sg9 I53 ssS'gist_gray' p166 (dp167 g2 S'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\n' p168 sg7 g8 sg9 I54 ssS'Blues' p169 (dp170 g2 S'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\n' p171 sg7 g8 sg9 I55 ssS'PuBuGn' p172 (dp173 g2 S'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\nmx45gusy3NCQ8WDPCf8FF7eTr4cgAAA=\n' p174 sg7 g8 sg9 I56 ssS'gist_rainbow' p175 (dp176 g2 S'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\nm1P7Ojp4c7yvvcr/AUyMTtCHIAAA\n' p177 sg7 g8 sg9 I50 ssS'bone' p178 (dp179 g2 S'H4sIAP+B70kC/4WZeVyO2fvH2yRJBjEhZTCYEI0tqrkYTZZkyU6GpEQl5QmNnVJUFFlKUhQxZUuT\npdKufd/3Z6me9pCtLL9zjtN9v37nn+/7j/m8Xr27que+z7nOdcwJOcu9zvb7XHQsHZysdOyd7QS7\nLZycLFyUtzlZWTrs3S9wcrYUKDvK/vgu5b07f1hHORMZt3WyO9zNHOUnm8iaysiaKJyh37NT4LLP\nStlRYZ2ctZ6JjIks+p5+k03k18karFixYtl3BPmPiYxgx9m1MmNl/gddoEGwA7cgX8vRKLXfzjL5\n6r0f/r8/Qv0R6k9zfjjBDSRzIs+poQTbvK7etd5MvRet96L1lzh/0ZcAw+T1NXE+1ZELOuJ4jfn5\nl+nPv0x//g3O+18jwNM9Wh+vopSsyQ0x1Ahhfn8g/f2B9Pff5nzwTQKsK5TPD0Lp4HO9O/Z7GPP3\nhdC/L4T+feGcX0O4DV80BMVmKHuybRYbiO8zf38Y/fvD6N8fyfmyUswdyF7VuADn6QFzAl6mPWQ+\nXzj9fOH08z3m/GbCfbjhujFyE8r+t08eGLPuCfP5I+jnj6CfP4rztTWYSNj7LHMkTtOFUq2atKfM\n83lIn89D+nyiOf/4EQG0DmguwulbtyIjUC+GeX6P6fN7TJ/fM847EZ6Aks+Gz44oy45GO5nff848\n3yj6fKPo833B+VmEp9B5z+fhTJQ/Pm8s8/yj6fOPps8/jvPvCdFQlpxhhXP7i1OpVd7xzPv5j76f\n/+j7ecX5mB9AQo2cBs41EYKYnOMJzPt7Rt/fM/r+EjnvQngO9z7qFx5CeaB3klfks0Tm/b6g7/cF\nfb9JnNcnvATfIc4e81H6L6va7v0miXn/sfT9x9L3n8z5r18wsfDPlAd/4Hzpf362vXYKsz7i6PqI\no+uD96/iCWDxV1N3HMoa6UJlU8tUZv28ouvnFV0/vF9ASICw6T4lgFJG733t1MA0Zn0l0PWVQNfX\na87/QkgE/9iIR2NRjncPj1Ipec2sv0S6/hLp+kvnvBwhCbyXZnjKojQu3eLRNiiDWZ9JdH0m0fXJ\ne7EIkwSnShuscdr8OnhrtnEms36T6fpNpuuX9ynJBHC2lPsT5zlBkm7E8SxmfafQ9Z1C32c258NC\nMSmwp0tzDM7IZGdFr2fZzPpPpes/la7/HM67EVLh76P6n1xR5g/9rcr2TQ6zP1Lp/kil+yOX87us\n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\n' p180 sg7 g8 sg9 I58 sssg7 S'dict' p181 sg9 I0 s.mayavi-4.1.0/mayavi/core/lut/rdpu.gif0000644000175100001440000000311311674464502020455 0ustar ischnellusers00000000000000GIF89aP& ###,,,222333999:::;;;LjQlVmZn`pdqirnssuxv}wCCCHHHLLLQQQXXXZZZ[[[___kkkmmmpppttt|||xyyz{{|}}!',16;@FJPUZ_ekqv|ÿ!,P&"4HP @ܱS9qq挙2dƈ 拗.\hɂI&K$ArH"CG;tqF 3dĀ8lА 4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫  ؾ  [/@r{/6|pP@$xPЀ!k#U0A$%@ I ! Gˑ$&rHDH$#-@$0$4#-PB$ @c;|k+}+BpK DV0@.Wng;mayavi-4.1.0/mayavi/core/lut/pastel1.gif0000644000175100001440000000322611674464502021061 0ustar ischnellusers00000000000000GIF89aP& """%%%---///111333555666888:::>>>???@@@AAACCCFFFIIIKKKLLLQQQXXX[[[\\\dddjjjmmmsss{{{|||}}}纼ةۨ߬ڽؿݿնְԻٿܾշ޹ڼص!,P&0_X2J)ac>}Č!SƌDrq&$A QI'!:d@1BH7pЩQ#G ]d)M4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 ,P$Zk n 滭[t` Ph ƻ @)p ))P @p P0]- r(TE ) ` TkpP (mO_ r P)20Bڰv @b6Ԇ[r(;j?@'n'[k9uX0p)-0l* Aܐ IsG/Wo};mayavi-4.1.0/mayavi/core/lut/paired.gif0000644000175100001440000000316311674464502020754 0ustar ischnellusers00000000000000GIF89aP& """---333777999;;;???@@@EEEFFFIIILLLQQQXXXZZZ[[[___dddjjjkkkmmmsssttt{{{|||}}} ,%./k5B0Z<@AQRwJbctt;2@3L>[BwQ^LoY+&5`nĂDϙSHVDSț}eٰcabkerr${nBzRyOm_p6.rJ^ASfxӍޞ䚌ץ˼Ьΐڊ݂֙𘮏ĺ̊Ǯҝ!,P&J1Y)3@WT򃇎=|A$<9pب4n0Be&F "(wAd/^hN> %$24P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 (A$۾.k&.B;n@(lp@rpA((p#HFp%(st }BCCCDDDHHHOOOUUUZZZ[[[___kkkmmmssstttxxxyyy& &&&'"'('.'5)=-D0L4S7[:c>jArEzIEHKNP'S1V:XCZM\V_`aicqdyeMPTW\_dinfgghikoswsy~{փ܈!,P&aĐ1F 7pбG?"e *V`2L|9cFL0_tE 'N0Y$ #F"$4P!C%RQ#G E4R%K0eҴS'O@5T)SPN+m巘no|*kᯈ..m䵔^n绠nZwŻVn^sInɗ]iuZqᵖ\y5^mѵ[uv pܡFx!Gl̡GtF|aG jǍrzMn׍v1`6;h@"-bAXVj8e} ~H~#8$Θ#߄(8 - I_uנ7'< x!A·`2djɨ_0Vi杈*%}: erJWLv某J9'ez.ڤoTiIj&> &hTkfv+k覫 %ؾKT;[#@ /Ti;$|2do[JH @08P{l ġ@/0 lQpJ* '"bJvr 2Pss rJt ȋ+w cxS0%)G ^x% ٠۞  ẵ ?A t GG/Wo};mayavi-4.1.0/mayavi/core/filter.py0000644000175100001440000001341011674464502020050 0ustar ischnellusers00000000000000"""The base filter class from which all MayaVi filters derive. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import List, Str # Local imports from mayavi.core.source import Source from mayavi.core.pipeline_base import PipelineBase from mayavi.core.pipeline_info import (PipelineInfo, get_tvtk_dataset_name) ###################################################################### # `Filter` class. ###################################################################### class Filter(Source): """ Base class for all the Mayavi filters. """ # The version of this class. Used for persistence. __version__ = 0 # The inputs for this filter. inputs = List(PipelineBase, record=False) # The icon icon = Str('filter.ico') # The human-readable type for this object type = Str(' filter') # Information about what this object can consume. input_info = PipelineInfo(datasets=['any']) ###################################################################### # `object` interface. ###################################################################### def __init__(self, **traits): super(Filter, self).__init__(**traits) # Let the filter setup its pipeline. self.setup_pipeline() def __get_pure_state__(self): d = super(Filter, self).__get_pure_state__() # Inputs are setup dynamically, don't pickle them. d.pop('inputs', None) return d ###################################################################### # `Filter` interface. ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* its tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. """ pass def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ raise NotImplementedError def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Invoke render to update any changes. self.render() # Propagate the data_changed event. self.data_changed = True ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. Note that when start is invoked, all the other information for the pipeline should be already set. """ # Do nothing if we are already running. if self.running: return # Setup event handlers. self._setup_event_handlers() # Update the pipeline. self.update_pipeline() # Call parent method to start the children and set the state. super(Filter, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. This is where you remove your actors from the scene. """ if not self.running: return # Teardown event handlers. self._teardown_event_handlers() # Call parent method to stop the children and set the state. super(Filter, self).stop() ###################################################################### # Non-public interface ###################################################################### def _set_outputs(self, new_outputs): """Set `self.outputs` to the given list of `new_outputs`. You should always use this method to set `self.outputs`. """ old_outputs = self.outputs self.outputs = new_outputs if len(new_outputs) > 0: self.output_info.datasets = \ [get_tvtk_dataset_name(new_outputs[0])] if old_outputs == self.outputs: # Even if the outputs don't change we want to propagate a # data_changed event since the data could have changed. self.data_changed = True def _inputs_changed(self, old, new): if self.running: self.update_pipeline() self._setup_input_events(old, new) def _inputs_items_changed(self, list_event): if self.running: self.update_pipeline() self._setup_input_events(list_event.removed, list_event.added) def _setup_event_handlers(self): self._setup_input_events([], self.inputs) def _teardown_event_handlers(self): self._setup_input_events(self.inputs, []) def _setup_input_events(self, removed, added): for input in removed: input.on_trait_event(self.update_pipeline, 'pipeline_changed', remove=True) input.on_trait_event(self.update_data, 'data_changed', remove=True) for input in added: input.on_trait_event(self.update_pipeline, 'pipeline_changed') input.on_trait_event(self.update_data, 'data_changed') mayavi-4.1.0/mayavi/core/module.py0000644000175100001440000002035011674464502020051 0ustar ischnellusers00000000000000"""The base class for all MayaVi modules. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import List, Instance, Str # Local imports from mayavi.core.pipeline_base import PipelineBase from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.common import exception ###################################################################### # `Module` class. ###################################################################### class Module(PipelineBase): """ Base class for the Mayavi modules. """ # The version of this class. Used for persistence. __version__ = 0 # The ModuleManager associated with this module. A Module is # always a child of a ModuleManager. When the module is added to # the mayavi pipeline (as a child of the module manager), the # module manager automatically sets this trait. module_manager = Instance('mayavi.core.module_manager.ModuleManager', record=False) # The (optional) components used by this module. NOTE: This is # not pickled. It is the developers responsibility to setup the # components when the component traits are set in the handler. components = List(record=False) # The icon icon = Str('module.ico') # The human-readable type for this object type = Str(' module') # Information about what this object can consume. input_info = PipelineInfo(datasets=['any']) # Information about what this object can produce. output_info = PipelineInfo(datasets=['none']) ###################################################################### # `object` interface. ###################################################################### def __init__(self, **traits): super(Module, self).__init__(**traits) # Let the module setup its pipeline. self.setup_pipeline() def __get_pure_state__(self): d = super(Module, self).__get_pure_state__() for x in ('module_manager', 'components'): d.pop(x, None) return d ###################################################################### # `Module` interface. ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ pass def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ raise NotImplementedError def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # By default, just invoke render and set data_changed. self.data_changed = True self.render() ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. Note that when start is invoked, all the other information for the pipeline should be already set. """ if self.running: return # Setup event handlers. self._setup_event_handlers() # Setup the pipeline. self.update_pipeline() # Start the components. try: for component in self.components: component.start() except: exception() # Call parent method to set the running state. super(Module, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return # Teardown event handlers. self._teardown_event_handlers() # Stop the components. for component in self.components: component.stop() # Call parent method to set the running state. super(Module, self).stop() def add_child(self, child): """This method intelligently adds a child to this object in the MayaVi pipeline. """ # Pass on the buck to our module_manager. self.module_manager.add_child(child) ###################################################################### # `TreeNodeObject` interface ###################################################################### def tno_has_children(self, node): """ Returns whether or not the object has children. """ return False def tno_allows_children(self, node): """ Returns whether chidren of this object are allowed or not. """ return False def tno_get_children(self, node): """ Gets the object's children. """ return None ###################################################################### # Non-public interface ###################################################################### def _change_components(self, old, new): """This method sets up the `components` trait and is typically called from a handler for a particular component. For example lets say you are using a `Actor` component and have a `actor` trait on the module. The `_actor_changed` method should setup the pipeline. Typically inside this handler, you also want to change the module's `components` trait. This method merely does that by removing the older component and adding the new one and then updating the pipeline just in case. """ comp = self.components if old is not None: comp.remove(old) comp.append(new) if old is not None: self.update_pipeline() def _setup_event_handlers(self): mm = self.module_manager src = mm.source mm.on_trait_change(self.update_pipeline, 'source') src.on_trait_event(self.update_pipeline, 'pipeline_changed') src.on_trait_event(self.update_data, 'data_changed') def _teardown_event_handlers(self): mm = self.module_manager src = mm.source mm.on_trait_change(self.update_pipeline, 'source', remove=True) src.on_trait_event(self.update_pipeline, 'pipeline_changed', remove=True) src.on_trait_event(self.update_data, 'data_changed', remove=True) def _scene_changed(self, old_scene, new_scene): for component in self.components: component.scene = new_scene super(Module, self)._scene_changed(old_scene, new_scene) def _components_changed(self, old, new): self._handle_components(old, new) def _components_items_changed(self, list_event): self._handle_components(list_event.removed, list_event.added) def _handle_components(self, removed, added): for component in removed: if self.running: component.stop() scene = self.scene for component in added: if scene is not None: component.scene = scene if self.running: component.start() def _visible_changed(self,value): for c in self.components: c.visible = value super(Module,self)._visible_changed(value) def _menu_helper_default(self): from mayavi.core.traits_menu import ModuleMenuHelper return ModuleMenuHelper(object=self.module_manager) mayavi-4.1.0/mayavi/core/__init__.py0000644000175100001440000000013211674464502020317 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/core/base.py0000644000175100001440000004001011674464502017471 0ustar ischnellusers00000000000000"""The base object from which all MayaVi pipeline objects derive. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. import cPickle from copy import deepcopy import os import logging import imp # Enthought library imports. from traits.api import (Instance, Property, Bool, Str, Python, HasTraits, WeakRef, on_trait_change) from traitsui.api import TreeNodeObject from tvtk.pyface.tvtk_scene import TVTKScene from apptools.persistence import state_pickler from pyface.resource.api import resource_path from pyface.image_resource import ImageResource from traitsui.menu import Menu, Action, Separator from traitsui.api import View from apptools.scripting.api import Recorder # Local imports. from mayavi.preferences.api import preference_manager from mayavi.core.common import get_engine # Setup a logger for this module. logger = logging.getLogger(__name__) # Subdirectory that the Base class will check for possible external views. UI_DIR_NAME = ['ui'] #------------------------------------------------------------------------------- # The core tree node menu actions: #------------------------------------------------------------------------------- NewAction = 'NewAction' CopyAction = Action(name = 'Copy', action = 'editor._menu_copy_node', enabled_when = 'editor._is_copyable(object)' ) CutAction = Action(name = 'Cut', action = 'editor._menu_cut_node', enabled_when = 'editor._is_cutable(object)' ) PasteAction = Action(name = 'Paste', action = 'editor._menu_paste_node', enabled_when = 'editor._is_pasteable(object)' ) DeleteAction = Action(name = 'Delete', action = 'editor._menu_delete_node', enabled_when = 'editor._is_deletable(object)' ) RenameAction = Action(name = 'Rename', action = 'editor._menu_rename_node', enabled_when = 'editor._is_renameable(object)' ) standard_menu_actions = [Separator(), CutAction, CopyAction, PasteAction, Separator(), RenameAction, DeleteAction, Separator(), ] ###################################################################### # `Base` class. ###################################################################### class Base(TreeNodeObject): # The version of this class. Used for persistence. __version__ = 0 ######################################## # Traits # The scene (RenderWindow) associated with this component. scene = Instance(TVTKScene, record=False) # Is this object running as part of the mayavi pipeline. running = Property(Bool, record=False) # The object's name. name = Str('') # The default icon. icon = 'module.ico' # The human readable type for this object type = Str('', record=False) # Is this object visible or not. visible = Bool(True, desc='if the object is visible') # Extend the children list with an AdderNode when a TreeEditor needs it. children_ui_list = Property(depends_on=['children'], record=False) # The parent of this object, i.e. self is an element of the parents # children. If there is no notion of a parent/child relationship # this trait is None. parent = WeakRef(record=False) # A helper for the right click menus, context sensitivity etc. menu_helper = Instance(HasTraits, record=False) # Our recorder. recorder = Instance(Recorder, record=False) ################################################## # Private traits _is_running = Bool(False) # This is used to save the state of the object when it is not # running. When the object "starts", the state is loaded. This # is done because a stopped object will not have a meaningful VTK # pipeline setup, so setting its state will lead to all kinds of # errors. _saved_state = Str('') # Hide and show actions _HideShowAction = Instance(Action, kw={'name': 'Hide/Show', 'action': 'object._hideshow'}, ) # The menu shown on right-click for this. _menu = Instance(Menu, transient=True) # Path to the icon for this object. _icon_path = Str() # Adder node: a dialog to add children to this object _adder_node_class = None # Name of the file that may host the hand-crafted view _view_filename = Str(transient=True) # Hand crafted view. _module_view = Instance(View, transient=True) # Work around problem with HasPrivateTraits. __ = Python ################################################## ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): """Method used by the state_pickler. """ d = self.__dict__.copy() for attr in ('scene', '_is_running', '__sync_trait__', '__traits_listener__', '_icon_path', '_menu', '_HideShowAction', 'menu_helper', 'parent', 'parent_', '_module_view', '_view_filename', 'mlab_source'): d.pop(attr, None) return d def __getstate__(self): """Allows standard pickle to work via the state_pickler. """ return state_pickler.dumps(self) def __setstate__(self, str_state): """Allows standard pickle to work via the state_pickler. """ self.__init__() # Get the state from the string and update it. state = state_pickler.loads_state(str_state) state_pickler.update_state(state) # Save the state and load it if we are running. self._saved_state = cPickle.dumps(state) if self.running: self._load_saved_state() def __deepcopy__(self, memo): """Method used by copy.deepcopy(). This also uses the state_pickler to work correctly. """ # Create a new instance. new = self.__class__() # If we have a saved state, use it for the new instance. If # not, get our state and save that. saved_state = self._saved_state if len(saved_state) == 0: state = state_pickler.get_state(self) saved_state = cPickle.dumps(state) new._saved_state = saved_state # In the unlikely case that a new instance is running, load # the saved state. if new.running: new._load_saved_state() return new ###################################################################### # `Base` interface ###################################################################### def start(self): """Invoked when this object is added to the mayavi pipeline. """ self.running = True self._load_saved_state() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ self.running = False def add_child(self, child): """This method intelligently adds a child to this object in the MayaVi pipeline. """ raise NotImplementedError def remove_child(self, child): """Remove specified child from our children. """ raise NotImplementedError() def remove(self): """Remove ourselves from the mayavi pipeline. """ if self.parent is not None: e = get_engine(self) self.parent.remove_child(self) if e.current_object is self: e.current_object = self.parent def render(self): """Invokes render on the scene, this in turn invokes Render on the VTK pipeline. """ s = self.scene if s is not None: s.render() def dialog_view(self): """ Returns a view with an icon and a title. """ view = self.trait_view() icon = self._icon_path + os.sep + 'images' + os.sep \ + self.icon view.icon = ImageResource(icon) view.title = "Edit%s: %s" % (self.type, self.name) view.buttons = ['OK', 'Cancel'] return view def trait_view(self, name = None, view_element = None ): """ Gets or sets a ViewElement associated with an object's class. Overridden here to search for a separate file in the same directory for the view to use for this object. The view should be declared in the file named _view. If a file with this name is not found, the trait_view method on the base class will be called. """ # If a name is specified, then call the HasTraits trait_view method # which will return (or assign) the *view_element* associated with # *name*. if name: return super(Base, self).trait_view(name, view_element) view = self._load_view_cached(name, view_element) # Uncomment this when developping views. #view = self._load_view_non_cached(name, view_element) return view ###################################################################### # `TreeNodeObject` interface ###################################################################### def tno_get_label(self, node): """Gets the label to display for a specified object. """ if self.name == '': self.name = self.__class__.__name__ return self.name def tno_get_view(self, node): """Gets the View to use when editing an object. """ view = self.trait_view() view.kind = 'subpanel' return view def tno_confirm_delete(self, node): """Confirms that a specified object can be deleted or not. """ if preference_manager.root.confirm_delete: return None else: return True def tno_get_menu ( self, node ): """ Returns the contextual pop-up menu. """ if self._menu is None: return super(Base, self).tno_get_menu(node) return self._menu def tno_get_icon(self, node, is_expanded): return self.icon def tno_get_icon_path(self, node): return self._icon_path def tno_delete_child(self, node, index): if len(self.children_ui_list) > len(self.children): del self.children[index - 1] else: del self.children[index] def tno_append_child(self, node, child): """ Appends a child to the object's children. """ self.children.append(child) def tno_insert_child(self, node, index, child): """ Inserts a child into the object's children. """ if len(self.children_ui_list) > len(self.children): idx = index -1 else: idx = index self.children[idx:idx] = [child] ###################################################################### # Non-public interface ###################################################################### def _get_running(self): return self._is_running def _set_running(self, new): if self._is_running == new: return else: old = self._is_running self._is_running = new self.trait_property_changed('running', old, new) def _get_children_ui_list(self): """ Getter for Traits Property children_ui_list. For the base class, do not add anything to the children list. """ if ((not preference_manager.root.show_helper_nodes or len(self.children) > 0) or self._adder_node_class is None or (not self.type == ' scene' and 'none' in self.output_info.datasets) # We can't use isinstance, as we would have circular # imports ): return self.children else: return [self._adder_node_class(object=self),] @on_trait_change('children[]') def _trigger_children_ui_list(self, old, new): """ Trigger a children_ui_list change when scenes changed. """ self.trait_property_changed('children_ui_list', old, new) def _visible_changed(self , value): # A hack to set the name when the tree view is not active. # `self.name` is set only when tno_get_label is called and this # is never called when the tree view is not shown leading to an # empty name. if len(self.name) == 0: self.tno_get_label(None) if value: #self._HideShowAction.name = "Hide" self.name = self.name.replace(' [Hidden]', '') else: #self._HideShowAction.name = "Show" n = self.name if ' [Hidden]' not in n: self.name = "%s [Hidden]" % n def _load_view_cached(self, name, view_element): """ Use a cached view for the object, for faster refresh. """ if self._module_view is not None: view = self._module_view else: logger.debug("No view found for [%s] in [%s]. " "Using the base class trait_view instead.", self, self._view_filename) view = super(Base, self).trait_view(name, view_element) return view def _load_view_non_cached(self, name, view_element): """ Loads the view by execing a file. Useful when tweaking views. """ result = {} view_filename = self._view_filename try: execfile(view_filename, {}, result) view = result['view'] except IOError: logger.debug("No view found for [%s] in [%s]. " "Using the base class trait_view instead.", self, view_filename) view = super(Base, self).trait_view(name, view_element) return view def _hideshow(self): if self.visible: self.visible = False else: self.visible = True def _load_saved_state(self): """Load the saved state (if any) of this object. """ saved_state = self._saved_state if len(saved_state) > 0: state = cPickle.loads(saved_state) if hasattr(self, '__set_pure_state__'): self.__set_pure_state__(state) else: state_pickler.set_state(self, state) self._saved_state = '' def __view_filename_default(self): """ The name of the file that will host the view. """ module = self.__module__.split('.') class_filename = module[-1] + '.py' module_dir_name = module[1:-1] base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) view_filename = reduce(os.path.join, [base_dir] + module_dir_name \ + UI_DIR_NAME + [class_filename]) return view_filename def __module_view_default(self): """ Try to load a view for this object. """ view_filename = self._view_filename try: result = imp.load_module('view', file(view_filename), view_filename, ('.py', 'U', 1)) view = result.view except: view = None return view def __menu_default(self): extras = [] if self.menu_helper is not None: extras = self.menu_helper.actions + self._extra_menu_items() menu_actions = [Separator()] + extras + \ [Separator(), self._HideShowAction, Separator()] + \ deepcopy(standard_menu_actions) return Menu( *menu_actions) def __icon_path_default(self): return resource_path() def _extra_menu_items(self): """Override this to generate any new menu actions you want on the right click menu.""" return [] mayavi-4.1.0/mayavi/core/trait_defs.py0000644000175100001440000002172211674464502020714 0ustar ischnellusers00000000000000#--------------------------------------------------------------------------- # # DEnum: is a 'dynamic enum' trait whose values are obtained from # another trait on the object. # # Caveat: # The problem with this trait is that the listeners (for changes to # the valid values) are added only when the attribute is read or # set. Thus if the acceptable list of values are changed before the # listeners are activated then the value will be set correctly only # when it is accessed and not when the values are set. # # Written by: David C. Morrill and Prabhu Ramachandran # # (c) Copyright 2006-2008 by Enthought, Inc. # #--------------------------------------------------------------------------- from traits.api import Property, TraitFactory, TraitError, TraitType, Int from traitsui.api import EnumEditor from traits.traits import trait_cast #--------------------------------------------------------------------------- # Utility functions: #--------------------------------------------------------------------------- def super_getattr(object, name, *args): """Works the same way as getattr, except that name can be of the form 'a.b.c' (as many levels as you like). For example: >>> class A: ... pass ... >>> a = A() >>> a.b = A() >>> a.b.c = 1 >>> super_getattr(a, 'b.c') 1 >>> super_getattr(a.b, 'c') 1 """ if '.' in name: attrs = name.split('.') last = attrs.pop() obj = object for attr in attrs: obj = getattr(obj, attr) return getattr(obj, last, *args) else: return getattr(object, name, *args) def super_setattr(object, name, value): """Works the same way as setattr, except that name can be of the form 'a.b.c' (as many levels as you like). For example: >>> class A: ... pass ... >>> a = A() >>> a.b = A() >>> super_setattr(a, 'b.c', 1) >>> a.b.c 1 """ if '.' in name: attrs = name.split('.') last = attrs.pop() obj = object for attr in attrs: obj = getattr(obj, attr) setattr(obj, last, value) else: setattr(object, name, value) #-------------------------------------------------------------------------------- # Helper class for DEnum trait. #-------------------------------------------------------------------------------- class DEnumHelper(object): """Defines a bunch of staticmethods that collect all the helper functions needed for the DEnum trait. """ ###################################################################### # Get/Set functions for the property. def get_value ( object, name ): return super_getattr(object, DEnumHelper._init_listeners(object, name)) get_value = staticmethod(get_value) def set_value ( object, name, value ): _name = DEnumHelper._init_listeners( object, name ) trait = object.trait( name ) values = super_getattr(object, trait.values_name) if value not in values: raise TraitError, (object, name, "one of %s"%values, value ) old = super_getattr(object, _name) super_setattr( object, _name, value ) object.trait_property_changed(name, old, value) set_value = staticmethod(set_value) ###################################################################### # Makes a default EnumEditor for the trait: def make_editor ( trait = None ): return EnumEditor( name=trait.values_name ) make_editor = staticmethod(make_editor) ###################################################################### # Ensures that the listeners are initialized. def _init_listeners ( object, name ): _name = '_' + name if not hasattr( object, _name ): trait = object.trait( name ) DEnumHelper._add_listeners( object, name, trait.values_name) default = trait.default or '' values = super_getattr( object, trait.values_name ) if values: if default is None or default not in values: default = values[0] super_setattr( object, _name, default ) return _name _init_listeners = staticmethod(_init_listeners) def _add_listeners ( object, name, values_name ): def check_values(object, values_name, old, new): cur_choice = super_getattr(object, name) if cur_choice not in new: if new: super_setattr(object, name, new[0]) else: super_setattr(object, name, '') def check_values_items(object, values_name, list_event): cur_choice = super_getattr(object, name) values = super_getattr(object, values_name[:-6]) if cur_choice not in values: if values: super_setattr(object, name, values[0]) else: super_setattr(object, name, '') object.on_trait_change( check_values, values_name ) object.on_trait_change( check_values_items, values_name + '_items' ) _add_listeners = staticmethod(_add_listeners) #------------------------------------------------------------------------------- # Defines the DEnum property: #------------------------------------------------------------------------------- DEnum = Property(DEnumHelper.get_value, DEnumHelper.set_value, values_name = 'values', editor = (DEnumHelper.make_editor, {'trait': None}) ) DEnum = TraitFactory(DEnum) ########################################################################## # `ShadowProperty` trait type. ########################################################################## class ShadowProperty(TraitType): # Not really necessary but specifies the attribute up front. trait_type = None # Call the notifiers smartly only when the value has really changed. # If this is set to False, the notification will always occur. smart_notify = True def __init__(self, trait_type, smart_notify=True, **metadata): """Defines a shadow property trait that is best explained by example:: class Thing(HasTraits): x = ShadowProperty(Float, smart_notify=False) def _x_changed(self, value): print value In this example, the actual value of the property (`x`) will be stored in `_x` and `_x_changed` will be called regardless whether the value actually changed or not. If `smart_notify` is set to `True` then the handler is called only if the value has actually changed. Note that the validation uses the validation of the specified `trait_type` parameter. """ self.trait_type = trait_cast(trait_type) self.smart_notify = smart_notify super(ShadowProperty, self).__init__(**metadata) def validate(self, object, name, value): """Validates that a specified value is valid for this trait. """ trt = self.trait_type if trt is not None and hasattr(trt, 'validate'): value = trt.validate(object, name, value) return value def get(self, object, name): """Get the value of the trait.""" shadow = self._get_shadow(name) d = object.__dict__ if shadow in d: return d[shadow] else: return None def set(self, object, name, value): """Set the value of the trait.""" old = self.get(object, name) shadow = self._get_shadow(name) object.__dict__[shadow] = value # Fire a trait property changed. fire = True if self.smart_notify: if old is value: fire = False if fire and self._check_notification(object): object.trait_property_changed(name, old, value) def _get_shadow(self, name): """Get the shadow attribute name to use.""" return '_' + name def _check_notification(self, object): """Checks to see if notifications are allowed or not i.e. has the trait been set via: object.set(name=value, trait_change_notify=False) """ if hasattr(object, '_get_trait_change_notify'): return object._get_trait_change_notify() else: # Traits won't tell us so we find out by adding a dynamic # trait, changing it and then seeing if the callback was # called, sigh! attr = '_testing_Notification_handlers_tmp_dont_touch' def callback(value): callback.value = value callback.value = -1 object.add_trait(attr, Int) object.on_trait_change(callback, attr) setattr(object, attr, 1) status = False if callback.value == 1: status = True object.on_trait_change(callback, attr, remove=True) object.remove_trait(attr) return status mayavi-4.1.0/mayavi/core/file_data_source.py0000644000175100001440000001550511674464502022062 0ustar ischnellusers00000000000000"""The base file related data source object from which all MayaVi data sources derive. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. import re from os.path import split, join, isfile from glob import glob # Enthought library imports. from traits.api import List, Str, Instance, Int, Range from traitsui.api import Group, Item, FileEditor from apptools.persistence.state_pickler import set_state from apptools.persistence.file_path import FilePath # Local imports from mayavi.core.source import Source from mayavi.core.common import handle_children_state ###################################################################### # Utility functions. ###################################################################### def get_file_list(file_name): """ Given a file name, this function treats the file as a part of a series of files based on the index of the file and tries to determine the list of files in the series. The file name of a file in a time series must be of the form 'some_name[0-9]*.ext'. That is the integers at the end of the file determine what part of the time series the file belongs to. The files are then sorted as per this index.""" # The matching is done only for the basename of the file. f_dir, f_base = split(file_name) # Find the head and tail of the file pattern. head = re.sub("[0-9]+[^0-9]*$", "", f_base) tail = re.sub("^.*[0-9]+", "", f_base) pattern = head+"[0-9]*"+tail # Glob the files for the pattern. _files = glob(join(f_dir, pattern)) # A simple function to get the index from the file. def _get_index(f, head=head, tail=tail): base = split(f)[1] result = base.replace(head, '') return float(result.replace(tail, '')) # Before sorting make sure the files in the globbed series are # really part of a timeseries. This can happen in cases like so: # 5_2_1.vtk and 5_2_1s.vtk will be globbed but 5_2_1s.vtk is # obviously not a valid time series file. files = [] for x in _files: try: _get_index(x) except ValueError: pass else: files.append(x) # Sort the globbed files based on the index value. def file_sort(x, y): x1 = _get_index(x) y1 = _get_index(y) if x1 > y1: return 1 elif y1 > x1: return -1 else: return 0 files.sort(file_sort) return files ###################################################################### # `FileDataSource` class. ###################################################################### class FileDataSource(Source): # The version of this class. Used for persistence. __version__ = 0 # The list of file names for the timeseries. file_list = List(Str, desc='a list of files belonging to a time series') # The current time step (starts with 0). This trait is a dummy # and is dynamically changed when the `file_list` trait changes. # This is done so the timestep bounds are linked to the number of # the files in the file list. timestep = Range(value=0, low='_min_timestep', high='_max_timestep', enter_set=True, auto_set=False, desc='the current time step') base_file_name=Str('', desc="the base name of the file", enter_set=True, auto_set=False, editor=FileEditor()) # A timestep view group that may be included by subclasses. time_step_group = Group(Item(name='file_path', style='readonly'), Item(name='timestep', defined_when='len(object.file_list) > 1') ) ################################################## # Private traits. ################################################## # The current file name. This is not meant to be touched by the # user. file_path = Instance(FilePath, (), desc='the current file name') _min_timestep = Int(0) _max_timestep = Int(0) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(FileDataSource, self).__get_pure_state__() # These are obtained dynamically, so don't pickle them. for x in ['file_list', 'timestep']: d.pop(x, None) return d def __set_pure_state__(self, state): # Use the saved path to initialize the file_list and timestep. fname = state.file_path.abs_pth if not isfile(fname): msg = 'Could not find file at %s\n'%fname msg += 'Please move the file there and try again.' raise IOError, msg self.initialize(fname) # Now set the remaining state without touching the children. set_state(self, state, ignore=['children', 'file_path']) # Setup the children. handle_children_state(self.children, state.children) # Setup the children's state. set_state(self, state, first=['children'], ignore=['*']) ###################################################################### # `FileDataSource` interface ###################################################################### def initialize(self, base_file_name): """Given a single filename which may or may not be part of a time series, this initializes the list of files. This method need not be called to initialize the data. """ self.base_file_name = base_file_name ###################################################################### # Non-public interface ###################################################################### def _file_list_changed(self, value): # Change the range of the timestep suitably to reflect new list. n_files = len(self.file_list) timestep = min(self.timestep, n_files) self._max_timestep = max(n_files -1, 0) if self.timestep == timestep: self._timestep_changed(timestep) else: self.timestep = timestep def _file_list_items_changed(self, list_event): self._file_list_changed(self.file_list) def _timestep_changed(self, value): file_list = self.file_list if len(file_list) > 0: self.file_path = FilePath(file_list[value]) else: self.file_path = FilePath('') def _base_file_name_changed(self,value): self.file_list = get_file_list(value) if len(self.file_list) == 0: self.file_list = [value] try: self.timestep = self.file_list.index(value) except ValueError: self.timestep = 0 mayavi-4.1.0/mayavi/core/lut_manager.py0000644000175100001440000003747311674464502021100 0ustar ischnellusers00000000000000"""This manages the lookup table used to map values to colors. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. import os.path import sys import subprocess # Enthought library imports. from traits.api import Instance, Range, Bool, Array, \ Str, Property, Enum, Button from traitsui.api import FileEditor, auto_close_message from apptools.persistence import state_pickler from tvtk.api import tvtk # Local imports. from mayavi.core.base import Base from mayavi.core.common import error from mayavi.core import lut lut_image_dir = os.path.dirname(lut.__file__) pylab_luts = state_pickler.load_state(os.path.join(lut_image_dir, 'pylab_luts.pkl')) ################################################################# # Utility functions. ################################################################# def set_lut(vtk_lut, lut_lst): """Setup the tvtk.LookupTable (`vtk_lut`) using the passed list of lut values.""" n_col = len(lut_lst) vtk_lut.number_of_colors = n_col vtk_lut.build() for i in range(0, n_col): lt = lut_lst[i] vtk_lut.set_table_value(i, lt[0], lt[1], lt[2], lt[3]) return vtk_lut def check_lut_first_line(line, file_name=''): """Check the line to see if this is a valid LUT file.""" first = line.split() if first[0] != "LOOKUP_TABLE": errmsg = "Error: The input data file \"%s\"\n"%(file_name) errmsg = errmsg+ "is not a proper lookup table file."\ " No LOOKUP_TABLE tag in first line. Try again." raise IOError, errmsg try: n_color = first[2] except: raise IOError, "Error: No size for LookupTable specified." else: return n_color def parse_lut_file(file_name): """Parse the file specified by its name `file_name` for a LUT and return the list of parsed values.""" input = open(file_name, "r") line = input.readline() n_color = check_lut_first_line(line, file_name) lut = [] for line in input.readlines(): entr = line.split() if len(entr) != 4: errmsg="Error: insufficient or too much data in line "\ "-- \"%s\""%(entr) raise IOError, errmsg tmp = [] for color in entr: try: tmp.append(float(color)) except: raise IOError, \ "Unknown entry '%s'in lookup table input."%color lut.append(tmp) return lut def lut_mode_list(): """ Function to generate the list of acceptable lut_mode values. """ lut_mode_list = ( ['blue-red', 'black-white', 'file', ] + pylab_luts.keys() ) lut_mode_list.sort() return lut_mode_list ###################################################################### # `LUTManager` class. ###################################################################### class LUTManager(Base): # The version of this class. Used for persistence. __version__ = 0 # The lookup table. lut = Instance(tvtk.LookupTable, (), record=False) # The scalar bar. scalar_bar = Instance(tvtk.ScalarBarActor, (), record=True) # The scalar_bar_widget scalar_bar_widget = Instance(tvtk.ScalarBarWidget, ()) # The representation associated with the scalar_bar_widget. This # only exists in VTK versions about around 5.2. scalar_bar_representation = Instance(tvtk.Object, allow_none=True, record=True) # The title text property of the axes. title_text_property = Property(record=True) # The label text property of the axes. label_text_property = Property(record=True) # The current mode of the LUT. lut_mode = Enum('blue-red', lut_mode_list(), desc='the type of the lookup table') # File name of the LUT file to use. file_name = Str('', editor=FileEditor, desc='the filename containing the LUT') # Reverse the colors of the LUT. reverse_lut = Bool(False, desc='if the lut is to be reversed') # Turn on/off the visibility of the scalar bar. show_scalar_bar = Bool(False, desc='if scalar bar is shown or not') # This is an alias for show_scalar_bar. show_legend = Property(Bool, desc='if legend is shown or not') # The number of labels to use for the scalar bar. number_of_labels = Range(0, 64, 8, enter_set=True, auto_set=False, desc='the number of labels to display') # Number of colors for the LUT. number_of_colors = Range(2, 2147483647, 256, enter_set=True, auto_set=False, desc='the number of colors for the LUT') # Enable shadowing of the labels and text. shadow = Bool(False, desc='if the labels and text have shadows') # Use the default data name or the user specified one. use_default_name = Bool(True, desc='if the default data name is to be used') # The default data name -- set by the module manager. default_data_name = Str('data', enter_set=True, auto_set=False, desc='the default data name') # The optionally user specified name of the data. data_name = Str('', enter_set=True, auto_set=False, desc='the title of the legend') # Use the default range or user specified one. use_default_range = Bool(True, desc='if the default data range is to be used') # The default data range -- this is computed and set by the # module manager. default_data_range = Array(shape=(2,), value=[0.0, 1.0], dtype=float, enter_set=True, auto_set=False, desc='the default range of the data mapped') # The optionally user defined range of the data. data_range = Array(shape=(2,), value=[0.0, 1.0], dtype=float, enter_set=True, auto_set=False, desc='the range of the data mapped') # Create a new LUT. create_lut = Button('Launch LUT editor', desc='if we launch a Lookup table editor in' ' a separate process') ######################################## ## Private traits. # The original range of the data. _orig_data_range = Array(shape=(2,), value=[0.0, 1.0], dtype=float) _title_text_property = Instance(tvtk.TextProperty) _label_text_property = Instance(tvtk.TextProperty) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): super(LUTManager, self).__init__(**traits) # Initialize the scalar bar. sc_bar = self.scalar_bar sc_bar.set(lookup_table=self.lut, title=self.data_name, number_of_labels=self.number_of_labels, orientation='horizontal', width=0.8, height=0.17) pc = sc_bar.position_coordinate pc.set(coordinate_system='normalized_viewport', value=(0.1, 0.01, 0.0)) self._shadow_changed(self.shadow) # Initialize the lut. self._lut_mode_changed(self.lut_mode) # Set the private traits. ttp = self._title_text_property = sc_bar.title_text_property ltp = self._label_text_property = sc_bar.label_text_property # Call render when the text properties are changed. ttp.on_trait_change(self.render) ltp.on_trait_change(self.render) # Initialize the scalar_bar_widget self.scalar_bar_widget.set(scalar_bar_actor=self.scalar_bar, key_press_activation=False) self._number_of_colors_changed(self.number_of_colors) ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. """ # Do nothing if we are already running. if self.running: return # Show the legend if necessary. self._show_scalar_bar_changed(self.show_scalar_bar) # Call parent method to set the running state. super(LUTManager, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return # Hide the scalar bar. sbw = self.scalar_bar_widget if sbw.interactor is not None: sbw.off() # Call parent method to set the running state. super(LUTManager, self).stop() ###################################################################### # Non-public interface ###################################################################### def _lut_mode_changed(self, value): if value == 'file': if self.file_name: self.load_lut_from_file(self.file_name) #self.lut.force_build() return reverse = self.reverse_lut if value in pylab_luts: lut = pylab_luts[value] if reverse: lut = lut[::-1, :] n_total = len(lut) n_color = self.number_of_colors if not n_color >= n_total: lut = lut[::round(n_total/float(n_color))] self.load_lut_from_list(lut.tolist()) #self.lut.force_build() return elif value == 'blue-red': if reverse: hue_range = 0.0, 0.6667 saturation_range = 1.0, 1.0 value_range = 1.0, 1.0 else: hue_range = 0.6667, 0.0 saturation_range = 1.0, 1.0 value_range = 1.0, 1.0 elif value == 'black-white': if reverse: hue_range = 0.0, 0.0 saturation_range = 0.0, 0.0 value_range = 1.0, 0.0 else: hue_range = 0.0, 0.0 saturation_range = 0.0, 0.0 value_range = 0.0, 1.0 lut = self.lut lut.set(hue_range=hue_range, saturation_range=saturation_range, value_range=value_range, number_of_table_values=self.number_of_colors, ramp='sqrt') lut.modified() lut.force_build() self.render() def _scene_changed(self, value): sbw = self.scalar_bar_widget if value is None: return if sbw.interactor is not None: sbw.off() value.add_widgets(sbw, enabled=False) if self.show_scalar_bar: sbw.on() self._foreground_changed_for_scene(None, value.foreground) def _foreground_changed_for_scene(self, old, new): # Change the default color for the text. self.title_text_property.color = new self.label_text_property.color = new self.render() def _number_of_colors_changed(self, value): if self.lut_mode == 'file': return elif self.lut_mode in pylab_luts: # We can't interpolate these LUTs, as they are defined from a # table. We hack around this limitation reverse = self.reverse_lut lut = pylab_luts[self.lut_mode] if reverse: lut = lut[::-1, :] n_total = len(lut) if value > n_total: return lut = lut[::round(n_total/float(value))] self.load_lut_from_list(lut.tolist()) else: lut = self.lut lut.number_of_table_values = value lut.modified() lut.build() self.render() # necessary to flush. sc_bar = self.scalar_bar sc_bar.maximum_number_of_colors = value sc_bar.modified() self.render() def _number_of_labels_changed(self, value): sc_bar = self.scalar_bar sc_bar.number_of_labels = value sc_bar.modified() self.render() def _file_name_changed(self, value): if self.lut_mode == 'file': self.load_lut_from_file(value) else: # This will automagically load the LUT from the file. self.lut_mode = 'file' def _reverse_lut_changed(self, value): # This will do the needful. self._lut_mode_changed(self.lut_mode) def _show_scalar_bar_changed(self, value): if self.scene is not None: self.scalar_bar_widget.enabled = value self.render() def _get_show_legend(self): return self.show_scalar_bar def _set_show_legend(self, value): old = self.show_scalar_bar if value != old: self.show_scalar_bar = value self.trait_property_changed('show_legend', old, value) def _shadow_changed(self, value): sc_bar = self.scalar_bar sc_bar.title_text_property.shadow = self.shadow sc_bar.label_text_property.shadow = self.shadow self.render() def _use_default_name_changed(self, value): self._default_data_name_changed(self.default_data_name) def _data_name_changed(self, value): sc_bar = self.scalar_bar sc_bar.title = value sc_bar.modified() self.render() def _default_data_name_changed(self, value): if self.use_default_name: self.data_name = value def _use_default_range_changed(self, value): self._default_data_range_changed(self.default_data_range) def _data_range_changed(self, value): try: self.lut.set_range(value[0], value[1]) except TypeError: self.lut.set_range((value[0], value[1])) except AttributeError: self.lut.range = value self.scalar_bar.modified() self.render() def _default_data_range_changed(self, value): if self.use_default_range: self.data_range = value def _visible_changed(self, value): state = self.show_scalar_bar and value self._show_scalar_bar_changed(state) super(LUTManager, self)._visible_changed(value) def load_lut_from_file(self, file_name): lut_list = [] if len(file_name) > 0: try: f = open(file_name, 'r') except IOError: msg = "Cannot open Lookup Table file: %s\n"%file_name error(msg) else: f.close() try: lut_list = parse_lut_file(file_name) except IOError, err_msg: msg = "Sorry could not parse LUT file: %s\n"%file_name msg += err_msg error(msg) else: if self.reverse_lut: lut_list.reverse() self.lut = set_lut(self.lut, lut_list) self.render() def load_lut_from_list(self, list): self.lut = set_lut(self.lut, list) self.render() def _get_title_text_property(self): return self._title_text_property def _get_label_text_property(self): return self._label_text_property def _create_lut_fired(self): from tvtk import util script = os.path.join(os.path.dirname(util.__file__), 'wx_gradient_editor.py') subprocess.Popen([sys.executable, script]) auto_close_message('Launching LUT editor in separate process ...') def _scalar_bar_representation_default(self): w = self.scalar_bar_widget if hasattr(w, 'representation'): r = w.representation r.on_trait_change(self.render) return r else: return None mayavi-4.1.0/mayavi/core/engine.py0000644000175100001440000005122111674464502020032 0ustar ischnellusers00000000000000"""The Mayavi engine. This class manages the Mayavi objects at the highest level. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. # VTK is used to just shut off the warnings temporarily. try: import vtk except ImportError, m: m.args = ('%s\n%s\nDo you have vtk and its Python bindings installed properly?' % (m.args[0], '_'*80),) raise # Enthought library imports. from traits.api import (HasStrictTraits, List, Str, Property, Instance, Event, HasTraits, Callable, Dict, Bool, on_trait_change, WeakRef) from traitsui.api import View, Item from apptools.persistence import state_pickler from apptools.scripting.api import Recorder, recordable # Local imports. from mayavi.core.base import Base from mayavi.core.scene import Scene from mayavi.core.common import error, process_ui_events from mayavi.core.registry import registry from mayavi.core.adder_node import AdderNode, SceneAdderNode from mayavi.preferences.api import preference_manager from mayavi.core.ui.mayavi_scene import viewer_factory ###################################################################### # Utility functions. ###################################################################### def _id_generator(): """Returns a sequence of numbers for the title of the scene window.""" n = 1 while True: yield(n) n += 1 scene_id_generator = _id_generator() def get_args(function): """ Simple inspect-like function to inspect the arguments a function takes. """ return function.func_code.co_varnames[:function.func_code.co_argcount] ###################################################################### # `Engine` class ###################################################################### class Engine(HasStrictTraits): """ The Mayavi engine base class. """ # The version of this class. Used for persistence. __version__ = 0 # The scenes associated with this project. scenes = List(Scene, record=True) # The list to provide to a TreeEditor. Always add on a AdderNode. # TODO: It makes more sense to put the modification of the list # in some other UI module, and not here. children_ui_list = Property(record=False) # Our name. name = Str('Mayavi Engine') # Current scene. current_scene = Property(Instance(Scene), record=False) # Current object. current_object = Property(record=False) # Current selection -- the currently selected object on the tree. current_selection = Property(record=False) # Has the Engine started? Use this event to do something after # the engine has been started. started = Event(record=False) # An optional callable that will generate a usable new viewer # containing a `tvtk.pyface.TVTKScene` instance. Ideally # the viewer should have an interface like # `tvtk.pyface.TVTKWindow` -- basically it must # implement the `closing` and `activated` events, however, this is # not necessary. The created viewer is used by the `new_scene` # method to create a new Viewer. This is a mechanism to use a # user specified scene with the Engine and have the ability to # load saved visualizations using the new scene. Handy for things # like off-screen rendering. scene_factory = Callable(viewer_factory) # Are we running? running = Bool(False, record=False) # The recorder for script recording. recorder = Instance(Recorder, record=False) ######################################## # Private traits. _current_scene = WeakRef(Scene, allow_none=True) _current_object = WeakRef(HasTraits, allow_none=True) _current_selection = WeakRef(HasTraits, allow_none=True) _viewer_ref = Dict # View related traits. current_selection_view = View(Item(name='_current_selection', enabled_when='_current_selection is not None', style='custom', springy=True, show_label=False,), resizable=True, scrollable=True ) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): super(Engine, self).__init__(**traits) # FIXME: This is tied to preferences. It really should not be # we need to use bind_preferences here. cbk = lambda : self.trait_property_changed('children_ui_list', [], self.children_ui_list) preference_manager.root.on_trait_change(cbk, 'show_helper_nodes') def __get_pure_state__(self): d = self.__dict__.copy() for x in ['_current_scene', '_current_object', '__sync_trait__', '_viewer_ref', '__traits_listener__']: d.pop(x, None) return d def __set_pure_state__(self, state): # Current number of scenes. n_scene = len(self.scenes) # Number of scenes in saved state. n_saved_scene = len(state.scenes) # Remove extra ones. for i in range(n_scene - n_saved_scene): self.close_scene(self.scenes[-1]) # Add new ones. for i in range(n_saved_scene - n_scene): self.new_scene() # Set the state. state_pickler.set_state(self, state) def __getstate__(self): return state_pickler.dumps(self) def __setstate__(self, str_state): self.__init__() state = state_pickler.loads_state(str_state) state_pickler.update_state(state) self.__set_pure_state__(state) ###################################################################### # `Engine` interface ###################################################################### def start(self): """This is called by the plugin when the plugin actually starts.""" registry.register_engine(self) # Notify any listeners that the engine is started. self.started = self self.running = True def stop(self): registry.unregister_engine(self) self.running = False @recordable def add_source(self, src, scene=None): """Adds a source to the pipeline. Uses the current scene unless a scene is given in the scene keyword argument.""" passed_scene = scene if scene is not None: tvtk_scene = scene.scene for sc in self.scenes: if sc.scene == tvtk_scene: scene = sc break else: error('This scene is not managed by mayavi') return else: scene = self.current_scene # Create a new scene if none is available. if scene is None: self.new_scene() scene = self.current_scene scene.add_child(src) self.current_object = src @recordable def add_filter(self, fil, obj=None): """Adds a filter to the pipeline at an appropriate point. Adds it to the selected object, or to an object passed as the kwarg `obj`. """ passed_obj = obj if obj is None: obj = self.current_object if not isinstance(obj, Base): msg = 'No valid current object, '\ 'please select an active object.' error(msg) return if (obj is not None) and (not isinstance(obj, Scene)): if obj.running: obj.add_child(fil) self.current_object = fil else: msg = 'Current object is not active, '\ 'please select an active object.' error(msg) else: if obj is None: error('Please create a VTK scene and open some data first.') else: error('No data: cannot use a Filter/Module/ModuleManager.') @recordable def add_module(self, mod, obj=None): """Adds a module to the pipeline at an appropriate point. Adds it to the selected object, or to an object passed through the kwarg `obj`. """ self.add_filter(mod, obj=obj) @recordable def save_visualization(self, file_or_fname): """Given a file or a file name, this saves the current visualization to the file. """ # Save the state of VTK's global warning display. o = vtk.vtkObject w = o.GetGlobalWarningDisplay() o.SetGlobalWarningDisplay(0) # Turn it off. try: state_pickler.dump(self, file_or_fname) finally: # Reset the warning state. o.SetGlobalWarningDisplay(w) @recordable def load_visualization(self, file_or_fname): """Given a file/file name this loads the visualization.""" # Save the state of VTK's global warning display. o = vtk.vtkObject w = o.GetGlobalWarningDisplay() o.SetGlobalWarningDisplay(0) # Turn it off. try: # Get the state from the file. state = state_pickler.load_state(file_or_fname) state_pickler.update_state(state) # Add the new scenes. for scene_state in state.scenes: self.new_scene() scene = self.scenes[-1] # Disable rendering initially. if scene.scene is not None: scene.scene.disable_render = True # Update the state. state_pickler.update_state(scene_state) scene.__set_pure_state__(scene_state) # Setting the state will automatically reset the # disable_render. scene.render() finally: # Reset the warning state. o.SetGlobalWarningDisplay(w) @recordable def open(self, filename, scene=None): """Open a file given a filename if possible in either the current scene or the passed `scene`. """ passed_scene = scene reader = registry.get_file_reader(filename) if reader is None: msg = 'No suitable reader found for the file %s'%filename error(msg) else: src = None if scene is None: scene = self.current_scene if scene is None: scene = self.new_scene() try: sc = scene.scene if sc is not None: sc.busy = True callable = reader.get_callable() if reader.factory is None: src = callable() src.initialize(filename) else: # Factory functions are passed the filename and a # reference to the engine. src = callable(filename, self) if src is not None: self.add_source(src, passed_scene) finally: if sc is not None: sc.busy = False if src is not None: return src def record(self, msg): """This is merely a convenience method to record messages to the script recorder. """ r = self.recorder if r is not None: r.record(msg) ###################################################################### # Scene creation/deletion related methods. ###################################################################### def add_scene(self, scene, name=None): """Add given `scene` (a `pyface.tvtk.scene.Scene` instance) to the mayavi engine so that mayavi can manage the scene. This is used when the user creates a scene. Note that for the `EnvisageEngine` this is automatically taken care of when you create a new scene using the TVTK scene plugin. Parameters: ----------- scene - `pyface.tvtk.scene.Scene` The scene that needs to be managed from mayavi. name - `str` The name assigned to the scene. It tries to determine the name of the scene from the passed scene instance. If this is not possible it defaults to 'Mayavi Scene'. """ if name is None: if hasattr(scene, 'name'): name = scene.name else: name = 'Mayavi Scene %d'%scene_id_generator.next() s = Scene(scene=scene, name=name, parent=self) s.start() # We don't want the startup setup to be recorded. recorder = self.recorder self.scenes.append(s) self.current_scene = s if recorder is not None: recorder.register(s) @recordable def remove_scene(self, scene, **kwargs): """Remove a given `scene` (a `pyface.tvtk.scene.Scene` instance) from the mayavi engine if it is already being managed by mayavi. Note that for the `EnvisageEngine` this is automatically taken care of when you close a scene started using the TVTK scene plugin. Parameters: ----------- scene - `pyface.tvtk.scene.Scene` The scene that needs to be removed from mayavi. """ s = None for index, x in enumerate(self.scenes): if x.scene is scene: s = x break if s is not None: s.stop() self.scenes.remove(s) # Don't record it shutting down. To do this we must # unregister it here so we don't record unnecessary calls. recorder = self.recorder if recorder is not None: recorder.unregister(s) # Remove the reference to the viewer if any. if scene in self._viewer_ref: del self._viewer_ref[scene] # Clear the current scene if it has been removed. if scene is self._current_scene: self._current_scene = None @recordable def new_scene(self, viewer=None, name=None, **kwargs): """Create or manage a new VTK scene window. If no `viewer` argument is provided, the method creates a new viewer using `self.scene_factory`. If `self.scene_factory` is `None` then it creates an `ivtk` viewer. This code requires that the `viewer` has a `scene` attribute/trait that is a `pyface.tvtk.scene.Scene`. It also works best if the viewer supports `closing` and `activated` events. The method returns the created viewer. Parameters: ----------- viewer - The viewer object, if None, one is created for you. name - The name attribute of the viewer ``**kwargs`` - The extra keyword arguments are passed along to the scene factory. """ if viewer is None: factory_kwargs = {} factory_kwargs_names = get_args(self.scene_factory) for arg, value in kwargs.iteritems(): if arg in factory_kwargs_names: factory_kwargs[arg] = value viewer = self.scene_factory(**factory_kwargs) process_ui_events() if name is not None: viewer.name = name # Hang on to a reference to this viewer, if not done this will cause a # crash with Qt4. This because the viewer will be closed and gc'd if # there isn't a reference to it. When the viewer is gc'd the scene is # also closed and the engine will have a dead scene causing a crash. self._viewer_ref[viewer.scene] = viewer self.add_scene(viewer.scene) if hasattr(viewer, 'on_trait_change'): viewer.on_trait_change(self._on_scene_closed, 'closing') viewer.on_trait_change(self._on_scene_activated, 'activated') if hasattr(viewer, 'title'): self.current_scene.sync_trait('name', viewer, 'title') return viewer @recordable def close_scene(self, scene): """Given a scene created from new_scene, this method closes it and removes the scene from the list of scenes we manage. Parameters: ----------- scene - `pyface.tvtk.scene.Scene` or an object that holds a reference to a `pyface.tvtk.scene.Scene` in a `scene` attribute. """ viewer = self.get_viewer(scene) self.remove_scene(scene.scene) if hasattr(scene, 'close'): scene.close() elif scene.scene is not None: scene.scene.close() if viewer is not None and hasattr(viewer, 'close'): viewer.close() def get_viewer(self, scene): """Return the viewer associated with a given scene. Parameters: ----------- scene - An `mayavi.core.scene.Scene` instance. """ return self._viewer_ref.get(scene.scene) def dialog_view(self): """ Default dialog view for Engine objects. """ return None ###################################################################### # Non-public interface ###################################################################### def _on_select(self, object): """Called by the EngineTree when an object on the view is selected. This basically sets the current object and current scene.""" self.current_selection = object self._current_object = object try: scene = object.scene for s in self.scenes: if s.scene == scene: self._current_scene = s break except AttributeError: pass def _get_current_scene(self): n_scene = len(self.scenes) if n_scene == 0: return None elif n_scene == 1: return self.scenes[0] elif self._current_scene is not None: return self._current_scene elif n_scene > 1: return self.scenes[-1] else: return None def _set_current_scene(self, scene): old = self._current_scene self._current_scene = scene self.trait_property_changed('current_scene', old, scene) def _get_current_object(self): if self._current_object is not None: return self._current_object elif self.current_scene is not None: return self.current_scene else: return None def _set_current_object(self, object): old = self._current_object self._current_object = object self.trait_property_changed('current_object', old, object) def _get_current_selection(self): return self._current_selection def _set_current_selection(self, object): old = self._current_selection if not isinstance(object, (Base, AdderNode)): object = None self._current_selection = object self.trait_property_changed('current_selection', old, object) def _on_scene_closed(self, obj, name, old, new): self.remove_scene(obj.scene) def _on_scene_activated(self, obj, name, old, new): for scene in self.scenes: if scene.scene is obj.scene: self.current_scene = scene break def _get_children_ui_list(self): """ Trait getter for children_ui_list Property. """ if preference_manager.root.show_helper_nodes \ and len(self.scenes) == 0: return [SceneAdderNode(object=self)] else: return self.scenes @on_trait_change('scenes[]') def _trigger_children_ui_list(self, old, new): """ Trigger a children_ui_list change when scenes changed. """ self.trait_property_changed('children_ui_list', old, new) def _recorder_changed(self, old, new): if new is not None: new.record('# Recorded script from Mayavi2') new.record('from numpy import array') new.record('try:') new.record(' engine = mayavi.engine') new.record('except NameError:') new.record(' from mayavi.api import Engine') new.record(' engine = Engine()') new.record(' engine.start()') new.record('if len(engine.scenes) == 0:') new.record(' engine.new_scene()') new.record('# ------------------------------------------- ') elif old is not None: old.record('# ------------------------------------------- ') old.record('from mayavi.tools.show import show') old.record('show()') mayavi-4.1.0/mayavi/tests/0000755000175100001440000000000011674464502016424 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/tests/test_builtin_image.py0000644000175100001440000000750311674464502022652 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import numpy import unittest from numpy import array # Enthought library imports. from mayavi.core.null_engine import NullEngine from mayavi.sources.builtin_image import BuiltinImage from mayavi.modules.surface import Surface from mayavi.modules.outline import Outline class TestBuiltinImageSource(unittest.TestCase): def setUp(self): e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() s=e.new_scene() image_data = BuiltinImage() e.add_source(image_data) outline = Outline() e.add_module(outline) surface = Surface() e.add_module(surface) image_data.data_source.radius = array([ 80., 80., 80.]) image_data.data_source.center = array([ 150., 150., 0.]) image_data.data_source.whole_extent = array([ 10, 245, 10, 245, 0, 0]) self.e=e self.scene = e.current_scene return def tearDown(self): self.e.stop() return def test_data_source(self): s = self.scene src = s.children[0] self.assertEqual(src.source,'ellipsoid') self.assertEqual(numpy.allclose(src.data_source.center,(150., 150., 0.)),True) self.assertEqual(numpy.allclose(src.data_source.radius,(80., 80., 80.)),True) self.assertEqual(numpy.allclose(src.data_source.whole_extent,(10, 245, 10, 245, 0, 0)),True) def check(self): s = self.scene src = s.children[0] ot = src.children[0].children[0] ot.render() # Check with the default properties of gaussian image to verify # that the source has actually changed self.assertEqual(src.source,'gaussian') self.assertEqual(numpy.allclose(src.data_source.center,(0., 0., 0.)),True) self.assertEqual(src.data_source.maximum,2.0) self.assertEqual(src.data_source.standard_deviation,15) # Check the scalar ranges sc = src.outputs[0].point_data.scalars self.assertEqual(numpy.allclose(sc.range, (0, 2.0), atol=1.01e-03), True) def test_change(self): s = self.scene src = s.children[0] ot = src.children[0].children[0] src.source = 'gaussian' # Check with the default properties of gaussian image to verify # that the source has actually changed self.assertEqual(src.source,'gaussian') self.assertEqual(numpy.allclose(src.data_source.center,(0., 0., 0.)),True) self.assertEqual(src.data_source.maximum,1.0) self.assertEqual(src.data_source.standard_deviation,100) #Check the scalar ranges self.assertEqual(numpy.allclose(src.outputs[0].point_data.scalars.range,(0.00149, 1.0),atol=1.01e-03),True) src.data_source.maximum = 2.0 src.data_source.standard_deviation = 15 self.check() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene src = scene.children[0] src.source = 'gaussian' src.data_source.maximum = 2.0 src.data_source.standard_deviation = 15 # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_no_ui_toolkit.py0000644000175100001440000000376511674464502022726 0ustar ischnellusers00000000000000""" Tests to try and ensure that important mayavi imports work with no UI. """ # Author: Prabhu Ramachandran # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. import sys import unittest from traits.etsconfig.api import ETSConfig class TestNoUIToolkit(unittest.TestCase): """Test if any important mayavi imports work with no UI whatsoever.""" def setUp(self): self.orig_tk = ETSConfig.toolkit ETSConfig._toolkit = 'null' # Import something from Pyface to force any potential imports # from a UI toolkit. Why did I pick Pyface? Well, adder_node # imports ImageResource and this seems to trigger some UI # toolkit import and this makes life difficult as far as the # testing goes. Forcing the issue here should let us test # safely since the Pyface imports will be done. from pyface.api import GUI # Remove any references to wx and Qt saved = {} for mod in ['wx', 'PyQt4', 'PySide']: saved[mod] = sys.modules.pop(mod, None) self.saved = saved def tearDown(self): ETSConfig._toolkit = self.orig_tk # Add back any any references to wx and Qt for mod in ['wx', 'PyQt4', 'PySide']: m = self.saved[mod] if m is not None: sys.modules[mod] = m def test_no_ui(self): """Test if mayavi imports work without any UI (wx or PyQt4).""" # These imports should work without any UI. from mayavi import mlab from mayavi.api import Engine from mayavi.sources.api import VTKDataSource from mayavi.filters.api import Optional from mayavi.modules.api import Outline from mayavi.preferences.api import preference_manager # Should not have triggered an import wx or PyQt4. self.assertEqual(sys.modules.has_key('wx'), False) self.assertEqual(sys.modules.has_key('PyQt4'), False) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_registry.py0000644000175100001440000001343711674464502021715 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Standard library imports. import unittest #Local Imports from common import get_example_data # Enthought library imports from mayavi.core.null_engine import NullEngine from mayavi.core.registry import registry from mayavi.sources.plot3d_reader import PLOT3DReader from mayavi.core.metadata import SourceMetadata from mayavi.core.pipeline_info import PipelineInfo class DummyReader(PLOT3DReader): def print_info(self): """This is a Dummy Reader for Testing Purposes Its extended from PLOT3D Reader""" pass # Callable which always returns false def check_read(cls, filename): """ Callable which alwasy returns False """ return False check_read = classmethod(check_read) class TestRegistry(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() #Uncomment to see visualization for debugging etc. #e = Engine() e.start() e.new_scene() self.e=e self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def test_unique_reader(self): "Check if a file with a unique reader in Mayavi2 can be read" e = self.e reader = e.open(get_example_data('prism.neu')) self.assertNotEqual(reader, None) def test_unsupported_file(self): "Test if the mechanism to handle unsupported files works fine" reader = registry.get_file_reader('junk.abc') self.assertEqual(reader, None) def test_multipe_readers(self): "Test if reader conflict is resolved" # Testing two files with same extensions but have to be read # by different readers reader = registry.get_file_reader(get_example_data('tiny.xyz')) callable = reader.get_callable() self.assertEqual(callable.__name__, 'PLOT3DReader') reader = registry.get_file_reader(get_example_data('thio3xx.xyz')) callable = reader.get_callable() self.assertEqual(callable.__name__, 'PolyDataReader') def test_multiple_valid_readers(self): """Test if the fixture works fine if there are multiple readers capable of reading the file properly""" # Inserting a dummy reader into the registry also capable of # reading files with extension 'xyz' open_dummy = SourceMetadata( id = "DummyFile", class_name = "mayavi.tests.test_registry.DummyReader", menu_name = "&PLOT3D file", tooltip = "Open a PLOT3D data data", desc = "Open a PLOT3D data data", help = "Open a PLOT3D data data", extensions = ['xyz'], wildcard = 'PLOT3D files (*.xyz)|*.xyz', can_read_test = 'mayavi.tests.test_registry:DummyReader.check_read', output_info = PipelineInfo(datasets=['structured_grid'], attribute_types=['any'], attributes=['any']) ) registry.sources.append(open_dummy) reader = registry.get_file_reader(get_example_data('tiny.xyz')) callable = reader.get_callable() self.assertEqual(callable.__name__, 'PLOT3DReader') # Removing existing readers for .xyz extensions to check if the Dummy # reader now reads it. remove = [] for index, src in enumerate(registry.sources[:]): if 'xyz' in src.extensions and src.id != 'DummyFile': remove.append((index, src)) registry.sources.remove(src) reader = registry.get_file_reader(get_example_data('tiny.xyz')) callable = reader.get_callable() self.assertEqual(callable.__name__, 'DummyReader') for index, src in remove: registry.sources.insert(index, src) registry.sources.remove(open_dummy) def test_no_valid_reader(self): """Test that if there is no reader which can read the file with assurity, the registry returns the last one of the readers which dont have a can_read_test and claim to read the file with the given extension""" open_dummy = SourceMetadata( id = "DummyFile", class_name = "mayavi.tests.test_registry.DummyReader", menu_name = "&PLOT3D file", tooltip = "Open a PLOT3D data data", desc = "Open a PLOT3D data data", help = "Open a PLOT3D data data", extensions = ['xyz'], wildcard = 'PLOT3D files (*.xyz)|*.xyz', can_read_test = 'mayavi.tests.test_registry:DummyReader.check_read', output_info = PipelineInfo(datasets=['structured_grid'], attribute_types=['any'], attributes=['any']) ) registry.sources.append(open_dummy) # Remove the poly data reader. for index, src in enumerate(registry.sources[:]): if src.id == 'PolyDataFile': poly = src registry.sources.remove(src) break reader = registry.get_file_reader(get_example_data('tiny.xyz')) callable = reader.get_callable() self.assertEqual(callable.__name__, 'PLOT3DReader') # Add back the poly data reader. registry.sources.insert(index, poly) registry.sources.remove(open_dummy) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_text3d.py0000644000175100001440000000556511674464502021263 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Standard library imports. import unittest from StringIO import StringIO from os.path import abspath import numpy as np # Local imports. from mayavi.core.null_engine import NullEngine from tvtk.api import tvtk # Enthought library imports from mayavi.sources.array_source import ArraySource from mayavi.modules.text3d import Text3D class TestText3D(unittest.TestCase): def make_data(self): """Trivial data """ return np.zeros((10,10,10)) def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() s=e.new_scene() self.e=e self.s=s ############################################################ # Create a new scene and set up the visualization. d = ArraySource() sc = self.make_data() d.scalar_data = sc e.add_source(d) self.t = Text3D() e.add_module(self.t) self.scene = e.current_scene return def save_and_reload(self): # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. self.e.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. self.e.close_scene(self.scene) # Load visualization self.e.load_visualization(f) self.scene = self.e.current_scene def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def test_text3d(self): "Test if the text3d has been properly instanciated" self.assert_(isinstance(self.t, Text3D)) def test_follower(self): """ Test if changing 'orient_to_camera' switches between plain actor and follower """ self.t.orient_to_camera = True self.assert_(isinstance(self.t.actor.actor, tvtk.Follower)) self.t.orient_to_camera = False self.assertFalse(isinstance(self.t.actor.actor, tvtk.Follower)) def test_persistence(self): # First test persistence without follower self.save_and_reload() self.t.orient_to_camera = False self.assertFalse(isinstance(self.t.actor.actor, tvtk.Follower)) self.assertFalse(self.t.orient_to_camera) self.t.orient_to_camera = True self.assert_(isinstance(self.t.actor.actor, tvtk.Follower)) self.save_and_reload() self.assertTrue(self.t.orient_to_camera) self.assert_(isinstance(self.t.actor.actor, tvtk.Follower)) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_builtin_surface.py0000644000175100001440000001000011674464502023202 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import numpy import unittest # Enthought library imports. from mayavi.core.null_engine import NullEngine from mayavi.sources.builtin_surface import BuiltinSurface from mayavi.modules.surface import Surface from mayavi.modules.outline import Outline class TestBuiltinSurfaceSource(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() s=e.new_scene() poly_data = BuiltinSurface() e.add_source(poly_data) outline = Outline() e.add_module(outline) surface = Surface() e.add_module(surface) poly_data.data_source.shaft_radius = 0.05 poly_data.data_source.shaft_resolution = 7 poly_data.data_source.tip_radius = 0.1 self.e=e self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def test_poly_data_source(self): """Do the basic testing""" s = self.scene src = s.children[0] #Check the properties of the default source self.assertEqual(src.source,'arrow') self.assertEqual(src.data_source.shaft_radius,0.05) self.assertEqual(src.data_source.shaft_resolution,7) self.assertEqual(src.data_source.tip_radius,0.1) def check(self): """Do the actual testing.""" s = self.scene src = s.children[0] ot = src.children[0].children[0] ot.render() # Flush the pipeline. # Check the outline bounds self.assertEqual(numpy.allclose(ot.outline_filter.output.bounds, (-0.5, 0.5, -0.5, 0.5, -0.475, 0.475), atol=1.01e-03), True) self.assertEqual(numpy.allclose(src.data_source.angle, 26.565, atol=1.01e-03),True) self.assertEqual(numpy.allclose(src.data_source.direction,(1., 0., 0.)),True) self.assertEqual(src.data_source.radius,0.5) self.assertEqual(src.data_source.height,1.0) self.assertEqual(numpy.allclose(src.data_source.center,(0., 0., 0.)),True) self.assertEqual(src.data_source.resolution, 10) #Modify Properties and check again src.data_source.height = 1.5 src.data_source.angle = 30 self.assertEqual(numpy.allclose(src.data_source.radius,0.866,atol=1.01e-03),True) def test_change(self): """Test if it works fine on changing the source""" s = self.scene src = s.children[0] ot = src.children[0].children[0] src.source = 'cone' src.data_source.resolution = 10 # Check with the default properties of cone to verify that the # source has actually changed self.assertEqual(src.source,'cone') self.check() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene src = scene.children[0] src.source = 'cone' src.data_source.resolution = 10 # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/datasets.py0000644000175100001440000001254211674464502020612 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. import numpy from numpy import linspace, cos, sin, pi, empty, sqrt,array,arange,random # Enthought library imports from tvtk.api import tvtk def generate_annulus(r=None, theta=None, z=None): """ Generate points for structured grid for a cylindrical annular volume. This method is useful for generating a unstructured cylindrical mesh for VTK (and perhaps other tools). Parameters ---------- r : array : The radial values of the grid points. It defaults to linspace(1.0, 2.0, 11). theta : array : The angular values of the x axis for the grid points. It defaults to linspace(0,2*pi,11). z: array : The values along the z axis of the grid points. It defaults to linspace(0,0,1.0, 11). Return ------ points : array Nx3 array of points that make up the volume of the annulus. They are organized in planes starting with the first value of z and with the inside "ring" of the plane as the first set of points. The default point array will be 1331x3. """ # Default values for the annular grid. if r is None: r = linspace(1.0,2.0, 11) if theta is None: theta = linspace(0,2*pi,11) if z is None: z = linspace(0.0,1.0, 11) # Find the x values and y values for each plane. x_plane = (cos(theta)*r[:,None]).ravel() y_plane = (sin(theta)*r[:,None]).ravel() # Allocate an array for all the points. We'll have len(x_plane) # points on each plane, and we have a plane for each z value, so # we need len(x_plane)*len(z) points. points = empty([len(x_plane)*len(z),3]) # Loop through the points for each plane and fill them with the # correct x,y,z values. start = 0 for z_plane in z: end = start+len(x_plane) # slice out a plane of the output points and fill it # with the x,y, and z values for this plane. The x,y # values are the same for every plane. The z value # is set to the current z plane_points = points[start:end] plane_points[:,0] = x_plane plane_points[:,1] = y_plane plane_points[:,2] = z_plane start = end return points def single_type_ug(): """Simple example showing how to create an unstructured grid consisting of cells of a single type. """ points = array([[0,0,0], [1,0,0], [0,1,0], [0,0,1], # tets [1,0,0], [2,0,0], [1,1,0], [1,0,1], [2,0,0], [3,0,0], [2,1,0], [2,0,1], ], 'f') tets = array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]) tet_type = tvtk.Tetra().cell_type ug = tvtk.UnstructuredGrid(points=points) ug.set_cells(tet_type, tets) return ug def mixed_type_ug(): """A slightly more complex example of how to generate an unstructured grid with different cell types. Returns a created unstructured grid. """ points = array([[0,0,0], [1,0,0], [0,1,0], [0,0,1], # tetra [2,0,0], [3,0,0], [3,1,0], [2,1,0], [2,0,1], [3,0,1], [3,1,1], [2,1,1], # Hex ], 'f') # shift the points so we can show both. points[:,1] += 2.0 # The cells cells = array([4, 0, 1, 2, 3, # tetra 8, 4, 5, 6, 7, 8, 9, 10, 11 # hex ]) # The offsets for the cells, i.e. the indices where the cells # start. offset = array([0, 5]) tetra_type = tvtk.Tetra().cell_type # VTK_TETRA == 10 hex_type = tvtk.Hexahedron().cell_type # VTK_HEXAHEDRON == 12 cell_types = array([tetra_type, hex_type]) # Create the array of cells unambiguously. cell_array = tvtk.CellArray() cell_array.set_cells(2, cells) # Now create the UG. ug = tvtk.UnstructuredGrid(points=points) # Now just set the cell types and reuse the ug locations and cells. ug.set_cells(cell_types, offset, cell_array) return ug def generateStructuredGrid(): """Generates Structured Grid""" dims = (32, 32, 12) sgrid = tvtk.StructuredGrid(dimensions=(dims[1], dims[0], dims[2])) r = linspace(1, 10, dims[0]) theta = linspace(0, 2*numpy.pi, dims[1]) z = linspace(0, 5, dims[2]) pts = generate_annulus(r, theta, z) sgrid.points = pts s = sqrt(pts[:,0]**2 + pts[:,1]**2 + pts[:,2]**2) sgrid.point_data.scalars = numpy.ravel(s.copy()) sgrid.point_data.scalars.name = 'scalars' return sgrid def generateUnstructuredGrid_single(): """Generates Untructured Grid""" ug = single_type_ug() temperature = arange(0, 120, 10, 'd') velocity = random.randn(12, 3) ug.point_data.scalars = temperature ug.point_data.scalars.name = 'temperature' # Some vectors. ug.point_data.vectors = velocity ug.point_data.vectors.name = 'velocity' return ug def generateUnstructuredGrid_mixed(): """Generates Untructured Grid""" ug = mixed_type_ug() temperature = arange(0, 120, 10, 'd') velocity = random.randn(12, 3) ug.point_data.scalars = temperature ug.point_data.scalars.name = 'temperature' # Some vectors. ug.point_data.vectors = velocity ug.point_data.vectors.name = 'velocity' return ug mayavi-4.1.0/mayavi/tests/test_mlab_integration.py0000644000175100001440000003127211674464502023360 0ustar ischnellusers00000000000000""" Integration tests of mlab with the null engine. This also tests some numerics with VTK. """ import unittest import numpy as np from mayavi import mlab from mayavi.core.null_engine import NullEngine from tvtk.api import tvtk from mayavi.tools.engine_manager import engine_manager from mayavi.core.registry import registry ################################################################################ # class `TestMlabNullEngine` ################################################################################ class TestMlabNullEngine(unittest.TestCase): """ Stub mlab to isolate as well as possible from creation of a new figure. """ def setUp(self): mlab.options.backend = 'test' e = NullEngine() e.start() mlab.set_engine(e) self.e = e def tearDown(self): # Check that the NullEngine is still the mlab engine if not mlab.get_engine() is self.e: raise AssertionError, \ "The NullEngine has been overridden" engine_manager.current_engine = None # Unregistering the engine, to avoid side-effects between tests self.e.stop() registry.unregister_engine(self.e) ################################################################################ # class `TestMlabNullEngineMisc` ################################################################################ class TestMlabNullEngineMisc(TestMlabNullEngine): """ Misc tests for mlab with the null engine """ def test_contour_filter(self): a = np.zeros((3, 3, 3)) a[1, 1, 1] = 1 src = mlab.pipeline.scalar_field(a) filter = mlab.pipeline.contour(src) x, y, z = filter.outputs[0].points.to_array().T # Check that the contour filter indeed did its work: np.testing.assert_almost_equal(x, [ 2. , 2. , 1.5, 2.5, 2. , 2. ]) np.testing.assert_almost_equal(y, [ 2. , 1.5, 2. , 2. , 2.5, 2. ]) np.testing.assert_almost_equal(z, [ 1.5, 2. , 2. , 2. , 2. , 2.5]) # Check that the filter was not added to a live scene: if filter.scene is not None: raise AssertionError, "The NullEngine seems not to work" def test_user_defined_filter(self): x, y, z = np.random.random((3, 100)) src = mlab.pipeline.scalar_scatter(x, y, z) density = mlab.pipeline.user_defined(src, filter='GaussianSplatter') self.assertEqual(len(density.outputs), 1) self.assert_(isinstance(density.outputs[0], tvtk.ImageData)) def test_mlab_source(self): """ Check that the different objects created by mlab have an 'mlab_source' attribute. """ # Test for functions taking 3D scalar data pipelines = ( (mlab.pipeline.scalar_scatter, ), (mlab.pipeline.scalar_field, ), (mlab.pipeline.scalar_field, mlab.pipeline.image_plane_widget), (mlab.contour3d, ), (mlab.points3d, ), ) data = np.random.random((3, 3, 3)) for pipeline in pipelines: obj = pipeline[0](data) for factory in pipeline[1:]: obj = factory(obj) self.assertTrue(hasattr(obj, 'mlab_source')) # Test for functions taking x, y, z 2d arrays. x, y, z = np.random.random((3, 3, 3)) pipelines = ( (mlab.mesh, ), (mlab.surf, ), (mlab.quiver3d, ), (mlab.pipeline.vector_scatter, ), (mlab.pipeline.vector_scatter, mlab.pipeline.extract_vector_components), (mlab.pipeline.vector_scatter, mlab.pipeline.extract_vector_norm), (mlab.pipeline.array2d_source, ), ) for pipeline in pipelines: obj = pipeline[0](x, y, z) for factory in pipeline[1:]: obj = factory(obj) self.assertTrue(hasattr(obj, 'mlab_source')) def test_figure(self): """ Various tests for mlab.figure(). """ # Test when specifying figure instances f1 = mlab.figure() e = mlab.get_engine() self.assert_(e.current_scene is f1) f2 = mlab.figure() self.assert_(e.current_scene is f2) mlab.figure(f1) self.assert_(e.current_scene is f1) # Test when specifying figure numbers f1 = mlab.figure(3) self.assert_(e.current_scene is f1) f2 = mlab.figure(4) self.assert_(e.current_scene is f2) mlab.figure(3) self.assert_(e.current_scene is f1) # Test when specifying figure names f1 = mlab.figure('Test 1') self.assert_(e.current_scene is f1) f2 = mlab.figure('Test 2') self.assert_(e.current_scene is f2) mlab.figure('Test 1') self.assert_(e.current_scene is f1) def test_close(self): """ Various tests for mlab.close(). """ f = mlab.figure() self.assert_(f.running) mlab.close(f) self.assertFalse(f.running) f = mlab.figure(314) self.assert_(f.running) mlab.close(314) self.assertFalse(f.running) f = mlab.figure('test_figure') self.assert_(f.running) mlab.close('test_figure') self.assertFalse(f.running) f = mlab.figure() self.assert_(f.running) mlab.close() self.assertFalse(f.running) figs = [mlab.figure() for i in range(5)] for f in figs: self.assert_(f.running) mlab.close(all=True) for f in figs: self.assertFalse(f.running) def test_triangular_mesh_reset(self): """ When reseting the triangular mesh (ie polydata), if we are not careful, we can create a segfault by passing triangules between points that do not exist. """ # We need to run this as a full test of mlab, rather than only a # test of the source, as to get a segfault, we need a module # opened on the source. n = 100 triangles = np.c_[np.arange(n-3), np.arange(n-3)+1, n-1-np.arange(n-3)] x, y, z = np.random.random((3, n)) src = mlab.triangular_mesh(x, y, z, triangles) # Now grow the mesh n = 1000 triangles = np.c_[np.arange(n-3), np.arange(n-3)+1, n-1-np.arange(n-3)] x, y, z = np.random.random((3, n)) src.mlab_source.reset(x=x, y=y, z=z, triangles=triangles) def test_colorbar(self): """ Test that when an object with scalars hidden is created, it does not get a colorbar, unless no other is avalaible. """ a = np.random.random((5, 5)) s1 = mlab.surf(a, colormap='gist_earth') s2 = mlab.surf(a, color=(0, 0, 0)) mlab.colorbar() self.assertEqual( s2.module_manager.scalar_lut_manager.show_scalar_bar, False) self.assertEqual( s1.module_manager.scalar_lut_manager.show_scalar_bar, True) ################################################################################ # class `TestMlabPipeline` ################################################################################ class TestMlabPipeline(TestMlabNullEngine): """ Test the pipeline functions. """ def test_probe_data(self): """ Test probe_data """ x, y, z = np.mgrid[0:1:10j, 0:1:10j, 0:1:10j] r = np.sqrt(x**2 + y**2 + z**2) iso = mlab.contour3d(x, y, z, r) x_, y_, z_ = np.random.random((3, 10, 4, 2)) r_ = mlab.pipeline.probe_data(iso, x_, y_, z_) np.testing.assert_array_almost_equal(r_, np.sqrt(x_**2 + y_**2 + z_**2), decimal=2) flow = mlab.flow(x, y, z, x, y, z) u_, v_, w_ = mlab.pipeline.probe_data(flow, x_, y_, z_, type='vectors') np.testing.assert_array_almost_equal(u_, x_, decimal=2) np.testing.assert_array_almost_equal(v_, y_, decimal=2) np.testing.assert_array_almost_equal(w_, z_, decimal=3) ################################################################################ # class `TestMlabHelperFunctions` ################################################################################ class TestMlabHelperFunctions(TestMlabNullEngine): """ Test various behaviors of the mlab helper functions. """ def test_barchart(self): s = np.random.random((10, 10)) x, y = np.indices(s.shape) bar1 = mlab.barchart(x, y, s) bar2 = mlab.barchart(s) bar3 = mlab.barchart(s, auto_scale=False) # Check that auto scaling worked well in the different # configurations for bar in bar1, bar2, bar3: self.assertEqual(bar.glyph.glyph_source.glyph_source.y_length, 0.9) ################################################################################ # class `TestMlabModules` ################################################################################ class TestMlabModules(TestMlabNullEngine): """ Test the mlab modules. """ def test_volume(self): """ Test the mlab volume factory. """ scalars = np.zeros((3, 3, 3)) scalars[0] = 1 src = mlab.pipeline.scalar_field(scalars) color = (1, 0.1, 0.314) vol = mlab.pipeline.volume(src, vmin=0.5, vmax=0.9, color=color) # Test the color feature for value in np.random.random(10): # get_color() will sometimes return .314 as .313999...9995, so we # use allclose() to match the tuples. np.allclose(vol._ctf.get_color(value), color) # Test the vmin and vmax features for value in 0.5*np.random.random(10): self.assertEqual(vol._otf.get_value(value), 0) for value in (0.9+0.1*np.random.random(10)): self.assertEqual(vol._otf.get_value(value), 0.2) # Test the rescaling of the colormap when using vmin and vmax # Rmq: we have to be careful: the range of the ctf can change vol1 = mlab.pipeline.volume(src) range1 = vol1._ctf.range[1] - vol1._ctf.range[0] vol2 = mlab.pipeline.volume(src, vmin=0.25, vmax=0.75) range2 = vol2._ctf.range[1] - vol2._ctf.range[0] for value in 0.5*np.random.random(10): np.testing.assert_array_almost_equal( vol1._ctf.get_color(2*range1*value), vol2._ctf.get_color(0.25+range2*value)) # Test outside the special [0, 1] range src = mlab.pipeline.scalar_field(2*scalars) vol1 = mlab.pipeline.volume(src) range1 = vol1._ctf.range[1] - vol1._ctf.range[0] vol2 = mlab.pipeline.volume(src, vmin=0.5, vmax=1.5) range2 = vol2._ctf.range[1] - vol2._ctf.range[0] for value in np.random.random(10): np.testing.assert_array_almost_equal( vol1._ctf.get_color(2*range1*value), vol2._ctf.get_color(0.5+range2*value)) def test_text(self): """ Test the text module. """ data = np.random.random((3, 3, 3)) src = mlab.pipeline.scalar_field(data) # Some smoke testing mlab.text(0.1, 0.9, 'foo') mlab.text(3, 3, 'foo', z=3) mlab.title('foo') # Check that specifying 2D positions larger than 1 raises an # error self.assertRaises(ValueError, mlab.text, 0, 1.5, 'test') def test_text3d(self): """ Test the text3d module. """ data = np.random.random((3, 3, 3)) src = mlab.pipeline.scalar_field(data) t = mlab.text3d(0, 0, 0, 'foo', opacity=0.5, scale=2, orient_to_camera=False, color=(0, 0, 0), orientation=(90, 0, 0)) def test_contour_grid_plane(self): """Test the contour_grid_plane. """ data = np.random.random((10, 10, 10)) src = mlab.pipeline.scalar_field(data) mlab.pipeline.outline(src) mlab.pipeline.grid_plane(src) mlab.pipeline.contour_grid_plane(src) def test_barchart(self): """Test the barchart function.""" s = np.abs(np.random.random((3,3))) b = mlab.barchart(s) self.assertEqual(b.glyph.glyph.scale_mode, 'scale_by_vector_components') s += 1 b.mlab_source.update() self.assertEqual(b.glyph.glyph.scale_mode, 'scale_by_vector_components') if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_customize.py0000644000175100001440000000123011674464502022053 0ustar ischnellusers00000000000000"""Tests for mayavi.core.customize.""" import unittest # We need this import of NullEngine since importing customize first can # result in circular imports. from mayavi.core.null_engine import NullEngine from mayavi.core import customize class TestCustomize(unittest.TestCase): def test_import_contrib(self): """Test the import_contrib function.""" for mname in ('mayavi.api', 'mayavi', 'mayavi.core', 'mayavi.core.base'): mod = customize._import_contrib(mname) self.assertEqual(mod.__name__, mname) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_threshold_filter.py0000644000175100001440000000430311674464502023376 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2010, Enthought, Inc. # License: BSD Style. # Standard library imports. import unittest import numpy as np # Local imports. from mayavi.core.null_engine import NullEngine # Enthought library imports from mayavi.filters.threshold import Threshold from mayavi.sources.array_source import ArraySource class TestThresholdFilter(unittest.TestCase): def make_src(self, nan=False): data = np.empty((3, 3, 3)) if nan: data[0] = np.nan data.flat[:] = np.arange(data.size) return ArraySource(scalar_data=data) def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() s = e.new_scene() self.e = e self.s = s self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def test_threshold_filter_nan(self): src = self.make_src(nan=True) self.e.add_source(src) threshold = Threshold() self.e.add_filter(threshold) self.assertEqual(np.nanmin(src.scalar_data), np.nanmin( threshold.outputs[0].point_data.scalars.to_array() )) self.assertEqual(np.nanmax(src.scalar_data), np.nanmax( threshold.outputs[0].point_data.scalars.to_array() )) def test_threshold_filter_threhsold(self): src = self.make_src() self.e.add_source(src) threshold = Threshold() self.e.add_filter(threshold) threshold.upper_threshold = 20. self.assertTrue(20 >= np.nanmax( threshold.outputs[0].point_data.scalars.to_array() )) return if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/common.py0000644000175100001440000000126411674464502020271 0ustar ischnellusers00000000000000""" Common code for mayavi tests. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. import os.path from traits.api import HasTraits, Any, Event, Callable def fixpath(filename): """Given a relative file path it sets the path relative to this directory. This allows us to run the tests from other directories as well. """ return os.path.join(os.path.dirname(__file__), filename) def get_example_data(fname): """Given a relative path to data inside the examples directory, obtains the full path to the file. """ p = os.path.join('data', fname) return os.path.abspath(fixpath(p)) mayavi-4.1.0/mayavi/tests/test_vtk_xml_reader.py0000644000175100001440000001117211674464502023045 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import unittest import numpy # Local imports. from common import get_example_data # Enthought library imports from mayavi.core.null_engine import NullEngine from mayavi.sources.vtk_xml_file_reader import VTKXMLFileReader from mayavi.modules.outline import Outline from mayavi.modules.contour_grid_plane import ContourGridPlane from mayavi.modules.scalar_cut_plane import ScalarCutPlane class TestVTKXMLReader(unittest.TestCase): def setUp(self): e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() e.new_scene() self.e=e # Read a VTK XML data file. r = VTKXMLFileReader() r.initialize(get_example_data('cube.vti')) e.add_source(r) # Create an outline for the data. o = Outline() e.add_module(o) # Create one ContourGridPlane normal to the 'x' axis. cgp1 = ContourGridPlane() e.add_module(cgp1) # Set the position to the middle of the data. cgp1.grid_plane.position = 1 # Another with filled contours normal to 'y' axis. cgp2 = ContourGridPlane() cgp2.contour.filled_contours = True # Set the axis and position to the middle of the data. cgp2.grid_plane.axis = 'y' cgp2.grid_plane.position = 1 e.add_module(cgp2) # An interactive scalar cut plane. cp = ScalarCutPlane() e.add_module(cp) ip = cp.implicit_plane ip.normal = 0,0,1 ip.origin = 0.5, 0.5, 1.0 # Since this is running offscreen this seems necessary. ip.widget.origin = 0.5, 0.5, 1.0 ip.widget.enabled = False self.scene = e.current_scene self.cgp2=cgp2 self.cp=cp return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self): """Do the actual testing.""" scene = self.scene src = scene.children[0] mm = src.children[0] cgp1 = mm.children[1] self.assertEqual(cgp1.grid_plane.position,1) cgp2 = mm.children[2] self.assertEqual(cgp2.contour.filled_contours,True) self.assertEqual(cgp2.grid_plane.axis, 'y') self.assertEqual(cgp2.grid_plane.position,1) cp = mm.children[3] ip = cp.implicit_plane self.assertAlmostEqual(numpy.sum(ip.normal - (0,0,1)) , 1e-16) self.assertAlmostEqual(numpy.sum(ip.origin - (0.5, 0.5, 1.0)), 0.0) self.assertEqual(ip.widget.enabled,False) def test_vtk_xml_reader(self): "Test if the test fixture works" #Now test. self.check() #from mayavi.tools.show import show #show() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) cp = source.children[0].children[-1] cp.implicit_plane.widget.enabled = False self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 cp = source1.children[0].children[-1] cp.implicit_plane.widget.enabled = False self.check() #from mayavi.tools.show import show #show() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_mlab_source.py0000644000175100001440000006454711674464502022350 0ustar ischnellusers00000000000000""" Test for MlabSource and its subclasses. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. import unittest import numpy as N from mayavi.tools import sources ################################################################################ # `TestMGlyphSource` ################################################################################ class TestMGlyphSource(unittest.TestCase): def setUp(self): self.x = x = N.ones(10, float) self.y = y = N.ones(10, float)*2.0 self.z = z = N.linspace(0, 10, 10) self.v = v = N.ones((10, 3), float)*10.0 self.s = s = N.ones(10, float) src = sources.MGlyphSource() src.reset(x=x, y=y, z=z, u=v[:,0], v=v[:,1], w=v[:,2], scalars=s) self.src = src def tearDown(self): return def get_data(self): return self.x, self.y, self.z, self.v, self.s, self.src def check_traits(self): """Check if the sources traits are set correctly.""" x, y, z, v, s, src = self.get_data() # Check if points are set correctly. self.assertEqual(N.alltrue(src.points[:,0].ravel() == x), True) self.assertEqual(N.alltrue(src.points[:,1].ravel() == y), True) self.assertEqual(N.alltrue(src.points[:,2].ravel() == z), True) # Check the vectors and scalars. self.assertEqual(N.alltrue(src.vectors == v), True) self.assertEqual(N.alltrue(src.scalars == s), True) def check_dataset(self): """Check the TVTK dataset.""" x, y, z, v, s, src = self.get_data() # Check if the dataset is setup right. pts = src.dataset.points.to_array() self.assertEqual(N.alltrue(pts[:,0].ravel() == x), True) self.assertEqual(N.alltrue(pts[:,1].ravel() == y), True) self.assertEqual(N.alltrue(pts[:,2].ravel() == z), True) vec = src.dataset.point_data.vectors.to_array() sc = src.dataset.point_data.scalars.to_array() self.assertEqual(N.alltrue(vec == v), True) self.assertEqual(N.alltrue(sc == s), True) def test_reset(self): "Test the reset method." x, y, z, v, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. x *= 5.0 s *= 10 v *= 0.1 src.reset(x=x, u=v[:,0], v=v[:,1], w=v[:,2], scalars=s) self.check_traits() self.check_dataset() def test_reset1(self): "Test the reset method." x, y, z, v, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. self.x = x = N.ones(20, float)*30.0 self.y = y = N.ones(20, float)*30.0 self.z = z = N.ones(20, float)*30.0 points = N.ones((20, 3), float)*30.0 self.s = s = N.ones(20, float) self.v = v = N.ones((20, 3), float)*30.0 src.reset(x=x,y=y,z=z, u=v[:,0], v=v[:,1], w=v[:,2], scalars=s,points=points,vectors=v) self.check_traits() self.check_dataset() def test_handlers(self): "Test if the various static handlers work correctly." x, y, z, v, s, src = self.get_data() x *= 2.0 y *= 2.0 z *= 2.0 v *= 2.0 s *= 2.0 src.x = x src.y = y src.z = z src.u = v[:,0] src.v = v[:,1] src.w = v[:,2] src.scalars = s self.check_traits() self.check_dataset() def test_set(self): "Test if the set method works correctly." x, y, z, v, s, src = self.get_data() x *= 2.0 z *= 2.0 s *= 2.0 src.set(x=x, z=z, scalars=s) self.check_traits() self.check_dataset() def test_strange_shape(self): " Test the MGlyphSource with strange shapes for the arguments " x, y, z, v, s, src = self.get_data() x = y = z = v = s = 0 src.reset(x=x, y=y, z=z, u=v, v=v, w=v, scalars=None) src.reset(x=x, y=y, z=z, u=v, v=v, w=v, scalars=s) x = y = z = v = s = 1 src.set(x=x, y=y, z=z, u=v, v=v, w=v, scalars=None) src.set(x=x, y=y, z=z, u=v, v=v, w=v, scalars=s) ################################################################################ # `TestMGlyphSource` ################################################################################ class TestMVerticalSource(unittest.TestCase): def setUp(self): self.x = x = N.ones(10, float) self.y = y = N.ones(10, float)*2.0 self.z = z = N.linspace(0, 10, 10) self.s = s = N.ones(10, float) src = sources.MVerticalGlyphSource() src.reset(x=x, y=y, z=z, scalars=s) self.src = src def tearDown(self): return def get_data(self): return self.x, self.y, self.z, self.s, self.src def check_traits(self): """Check if the sources traits are set correctly.""" x, y, z, s, src = self.get_data() # Check if points are set correctly. self.assertEqual(N.alltrue(src.points[:,0].ravel() == x), True) self.assertEqual(N.alltrue(src.points[:,1].ravel() == y), True) self.assertEqual(N.alltrue(src.points[:,2].ravel() == z), True) # Check the vectors and scalars. self.assertEqual(N.alltrue(src.vectors[:, -1] == s), True) self.assertEqual(N.alltrue(src.vectors[:, :-1] == 1), True) self.assertEqual(N.alltrue(src.scalars == s), True) def check_dataset(self): """Check the TVTK dataset.""" x, y, z, s, src = self.get_data() # Check if the dataset is setup right. pts = src.dataset.points.to_array() self.assertEqual(N.alltrue(pts[:,0].ravel() == x), True) self.assertEqual(N.alltrue(pts[:,1].ravel() == y), True) self.assertEqual(N.alltrue(pts[:,2].ravel() == z), True) vec = src.dataset.point_data.vectors.to_array() sc = src.dataset.point_data.scalars.to_array() self.assertEqual(N.alltrue(vec[:, -1] == s), True) self.assertEqual(N.alltrue(vec[:, :-1] == 1), True) self.assertEqual(N.alltrue(sc == s), True) def test_reset(self): "Test the reset method." x, y, z, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. x *= 5.0 s *= 10 src.reset(x=x, scalars=s) self.check_traits() self.check_dataset() def test_reset1(self): "Test the reset method." x, y, z, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. self.x = x = N.ones(20, float)*30.0 self.y = y = N.ones(20, float)*30.0 self.z = z = N.ones(20, float)*30.0 points = N.ones((20, 3), float)*30.0 self.s = s = N.ones(20, float) src.reset(x=x, y=y, z=z, scalars=s, points=points) self.check_traits() self.check_dataset() def test_handlers(self): "Test if the various static handlers work correctly." x, y, z, s, src = self.get_data() x *= 2.0 y *= 2.0 z *= 2.0 s *= 2.0 src.x = x src.y = y src.z = z src.scalars = s self.check_traits() self.check_dataset() def test_set(self): "Test if the set method works correctly." x, y, z, s, src = self.get_data() x *= 2.0 z *= 2.0 s *= 2.0 src.set(x=x, z=z, scalars=s) self.check_traits() self.check_dataset() ################################################################################ # `TestMArraySource` ################################################################################ class TestMArraySource(unittest.TestCase): def setUp(self): x, y, z = N.ogrid[-10:10:11j, -10:10:12j, -10:10:20j] self.x, self.y, self.z = x, y, z dims = (x.shape[0], y.shape[1], z.shape[2]) self.v = v = N.ones(dims + (3,), float) v[...,2] = 2 v[...,2] = 3 self.s = s = N.ones(dims, float) src = sources.MArraySource() src.reset(x=x, y=y, z=z, u=v[...,0], v=v[...,1], w=v[...,2], scalars=s) self.src = src def tearDown(self): return def get_data(self): return self.x, self.y, self.z, self.v, self.s, self.src def check_traits(self): """Check if the sources traits are set correctly.""" x, y, z, v, s, src = self.get_data() # Check if points are set correctly. self.assertEqual(N.alltrue(src.x == x), True) self.assertEqual(N.alltrue(src.y == y), True) self.assertEqual(N.alltrue(src.z == z), True) # Check the vectors and scalars. self.assertEqual(N.alltrue(src.vectors == v), True) self.assertEqual(N.alltrue(src.scalars == s), True) def check_dataset(self): """Check the TVTK dataset.""" x, y, z, v, s, src = self.get_data() # Check if the dataset is setup right. dx = x[1, 0, 0] - x[0, 0, 0] dy = y[0, 1, 0] - y[0, 0, 0] dz = z[0, 0, 1] - z[0, 0, 0] origin = [x.min(), y.min(), z.min()] spacing = [dx, dy, dz] dimensions = (x.shape[0], y.shape[1], z.shape[2]) ds = src.dataset self.assertEqual(N.all(src.m_data.origin == origin), True) self.assertEqual(N.allclose(src.m_data.spacing, spacing), True) self.assertEqual(N.allclose(ds.dimensions, dimensions), True) vec = src.dataset.point_data.vectors.to_array() sc = src.dataset.point_data.scalars.to_array() v1 = v.transpose((2, 0, 1, 3)) self.assertEqual(N.alltrue(vec.ravel() == v1.ravel()), True) s1 = s.transpose() self.assertEqual(N.alltrue(sc.ravel() == s1.ravel()), True) def test_reset(self): "Test the reset method." x, y, z, v, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. x *= 5.0 s *= 10 v *= 0.1 src.reset(x=x, u=v[...,0], v=v[...,1], w=v[...,2], scalars=s) self.check_traits() self.check_dataset() def test_reset1(self): "Test the reset method." x, y, z, v, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. x, y, z = N.ogrid[-10:10:11j, -10:10:12j, -10:10:20j] self.x, self.y, self.z = x, y, z dims = (x.shape[0], y.shape[1], z.shape[2]) self.v = v = N.ones(dims + (3,), float) v[...,2] = 2 v[...,2] = 3 self.s = s = N.ones(dims, float) src = sources.MArraySource() src.reset(x=x, y=y, z=z, u=v[...,0], v=v[...,1], w=v[...,2], scalars=s,vectors=v) self.check_traits() self.check_dataset() def test_handlers(self): "Test if the various static handlers work correctly." x, y, z, v, s, src = self.get_data() x *= 2.0 y *= 2.0 z *= 2.0 v *= 2.0 s *= 2.0 src.x = x src.y = y src.z = z src.u = v[...,0] src.v = v[...,1] src.w = v[...,2] src.scalars = s self.check_traits() self.check_dataset() def test_set(self): "Test if the set method works correctly." x, y, z, v, s, src = self.get_data() x *= 2.0 z *= 2.0 s *= 2.0 src.set(x=x, z=z, scalars=s) self.check_traits() self.check_dataset() ################################################################################ # `TestMLineSource` ################################################################################ class TestMLineSource(unittest.TestCase): def setUp(self): self.x = x = N.ones(10, float) self.y = y = N.ones(10, float)*2.0 self.z = z = N.linspace(0, 10, 10) self.s = s = N.ones(10, float) src = sources.MLineSource() src.reset(x=x, y=y, z=z, scalars=s) self.src = src def tearDown(self): return def get_data(self): return self.x, self.y, self.z, self.s, self.src def check_traits(self): """Check if the sources traits are set correctly.""" x, y, z, s, src = self.get_data() # Check if points are set correctly. self.assertEqual(N.alltrue(src.points[:,0].ravel() == x), True) self.assertEqual(N.alltrue(src.points[:,1].ravel() == y), True) self.assertEqual(N.alltrue(src.points[:,2].ravel() == z), True) # Check the scalars. self.assertEqual(N.alltrue(src.scalars == s), True) def check_dataset(self): """Check the TVTK dataset.""" x, y, z, s, src = self.get_data() # Check if the dataset is setup right. pts = src.dataset.points.to_array() self.assertEqual(N.alltrue(pts[:,0].ravel() == x), True) self.assertEqual(N.alltrue(pts[:,1].ravel() == y), True) self.assertEqual(N.alltrue(pts[:,2].ravel() == z), True) sc = src.dataset.point_data.scalars.to_array() self.assertEqual(N.alltrue(sc == s), True) def test_reset(self): "Test the reset method." x, y, z, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. x *= 5.0 s *= 10 src.reset(x=x, scalars=s) self.check_traits() self.check_dataset() y *= 6.0 x *= 4 src.reset(x=x, y=y) self.check_traits() self.check_dataset() s *= 4.5 y /= 4 src.reset(y=y, s=s) self.check_traits() self.check_dataset() def test_reset1(self): "Test the reset method." x, y, z, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. self.x = x = N.ones(20, float)*30.0 self.y = y = N.ones(20, float)*30.0 self.z = z = N.ones(20, float)*30.0 points = N.ones((20, 3), float)*30.0 self.s = s = N.ones(20, float) src.reset(x=x,y=y,z=z,scalars=s,points=points) self.check_traits() self.check_dataset() def test_handlers(self): "Test if the various static handlers work correctly." x, y, z, s, src = self.get_data() x *= 2.0 y *= 2.0 z *= 2.0 s *= 2.0 src.x = x src.y = y src.z = z src.scalars = s self.check_traits() self.check_dataset() def test_set(self): "Test if the set method works correctly." x, y, z, s, src = self.get_data() x *= 2.0 z *= 2.0 s *= 2.0 src.set(x=x, z=z, scalars=s) self.check_traits() self.check_dataset() y *= 2.0 s *= 2.0 src.set(y=y, scalars=s) self.check_traits() self.check_dataset() ################################################################################ # `TestMArray2DSource` ################################################################################ class TestMArray2DSource(unittest.TestCase): def setUp(self): x, y = N.mgrid[-10:10:11j, -10:10:12j] self.x, self.y = x, y dims = (x.shape[0], y.shape[1]) self.s = s = N.ones(dims, float) src = sources.MArray2DSource() src.reset(x=x, y=y,scalars=s) self.src = src def tearDown(self): return def get_data(self): return self.x, self.y, self.s, self.src def check_traits(self): """Check if the sources traits are set correctly.""" x, y, s, src = self.get_data() # Check if points are set correctly. self.assertEqual(N.alltrue(src.x == x), True) self.assertEqual(N.alltrue(src.y == y), True) # Check the scalars. self.assertEqual(N.alltrue(src.scalars == s), True) def check_dataset(self): """Check the TVTK dataset.""" x, y, s, src = self.get_data() # Check if the dataset is setup right. x = N.atleast_2d(x.squeeze().T)[0, :].squeeze() y = N.atleast_2d(y.squeeze())[0, :].squeeze() dx = x[1] - x[0] dy = y[1] - y[0] origin = [x.min(), y.min(),0 ] spacing = [dx, dy, 1] ds = src.dataset self.assertEqual(N.all(ds.origin == origin), True) self.assertEqual(N.allclose(src.m_data.spacing, spacing), True) sc = src.dataset.point_data.scalars.to_array() s1 = s.transpose() self.assertEqual(N.alltrue(sc.ravel() == s1.ravel()), True) def test_reset(self): "Test the reset method." x, y, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. x *= 5.0 s *= 10 src.reset(x=x,y=y, scalars=s) self.check_traits() self.check_dataset() def test_handlers(self): "Test if the various static handlers work correctly." x, y, s, src = self.get_data() x *= 2.0 y *= 2.0 s *= 2.0 src.x = x src.y = y src.scalars = s self.check_traits() self.check_dataset() def test_set(self): "Test if the set method works correctly." x, y, s, src = self.get_data() x *= 2.0 s *= 2.0 src.set(x=x,scalars=s) self.check_traits() self.check_dataset() y *= 9.0 s *= 2.0 src.set(y=y, scalars=s) self.check_traits() self.check_dataset() ################################################################################ # `TestMGridSource` ################################################################################ class TestMGridSource(unittest.TestCase): def setUp(self): self.x = x = N.ones([10,10], float) self.y = y = N.ones([10,10], float)*2.0 self.z = z = N.ones([10,10], float)*3.0 self.s = s = N.ones([10,10], float) src = sources.MGridSource() src.reset(x=x, y=y, z=z, scalars=s) self.src = src def tearDown(self): return def get_data(self): return self.x, self.y, self.z, self.s, self.src def check_traits(self): """Check if the sources traits are set correctly.""" x, y, z, s, src = self.get_data() # Check if points are set correctly. self.assertEqual(N.alltrue(src.points[:,0].ravel() == x.ravel()), True) self.assertEqual(N.alltrue(src.points[:,1].ravel() == y.ravel()), True) self.assertEqual(N.alltrue(src.points[:,2].ravel() == z.ravel()), True) # Check the scalars. self.assertEqual(N.alltrue(src.scalars == s), True) def check_dataset(self): """Check the TVTK dataset.""" x, y, z, s, src = self.get_data() # Check if the dataset is setup right. pts = src.dataset.points.to_array() self.assertEqual(N.alltrue(pts[:,0].ravel() == x.ravel()), True) self.assertEqual(N.alltrue(pts[:,1].ravel() == y.ravel()), True) self.assertEqual(N.alltrue(pts[:,2].ravel() == z.ravel()), True) sc = src.dataset.point_data.scalars.to_array() self.assertEqual(N.alltrue(sc == s.ravel()), True) def test_reset(self): "Test the reset method." x, y, z, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. x *= 5.0 s *= 10 src.reset(x=x, scalars=s) self.check_traits() self.check_dataset() def test_handlers(self): "Test if the various static handlers work correctly." x, y, z, s, src = self.get_data() x *= 2.0 y *= 2.0 z *= 2.0 s *= 2.0 src.x = x src.y = y src.z = z src.scalars = s self.check_traits() self.check_dataset() def test_set(self): "Test if the set method works correctly." x, y, z, s, src = self.get_data() x *= 2.0 z *= 2.0 s *= 2.0 src.set(x=x, z=z, scalars=s) self.check_traits() self.check_dataset() ################################################################################ # `TestMArray2DSourceNoArgs` ################################################################################ class TestMArray2DSourceNoArgs(unittest.TestCase): """Special Test Case for MArray2DSource when both x and y are specified as None""" def setUp(self): x=None y=None self.x, self.y = x, y if x is not None and y is not None: dims = (x.shape[0], y.shape[1]) else: dims=(10,10) self.s = s = N.ones(dims, float) src = sources.MArray2DSource() src.reset(x=x, y=y,scalars=s) self.src = src def tearDown(self): return def get_data(self): return self.x, self.y, self.s, self.src def check_traits(self): """Check if the sources traits are set correctly.""" x, y, s, src = self.get_data() # Check if points are set correctly. if x is not None and y is not None: self.assertEqual(N.alltrue(src.x == x), True) self.assertEqual(N.alltrue(src.y == y), True) else: nx, ny = s.shape x1, y1 = N.mgrid[-nx/2.:nx/2, -ny/2.:ny/2] self.assertEqual(N.alltrue(src.x == x1), True) self.assertEqual(N.alltrue(src.y == y1), True) # Check the scalars. self.assertEqual(N.alltrue(src.scalars == s), True) def check_dataset(self): """Check the TVTK dataset.""" x, y, s, src = self.get_data() # Check if the dataset is setup right. nx, ny = src.scalars.shape if x is None and y is None: x, y = N.mgrid[-nx/2.:nx/2, -ny/2.:ny/2] x = N.atleast_2d(x.squeeze().T)[0, :].squeeze() y = N.atleast_2d(y.squeeze())[0, :].squeeze() dx = x[1] - x[0] dy = y[1] - y[0] origin = [x.min(), y.min(),0 ] spacing = [dx, dy, 1] ds = src.dataset self.assertEqual(N.all(ds.origin == origin), True) self.assertEqual(N.allclose(ds.spacing, spacing), True) sc = src.dataset.point_data.scalars.to_array() s1 = s.transpose() self.assertEqual(N.alltrue(sc.ravel() == s1.ravel()), True) def test_reset(self): "Test the reset method." x, y, s, src = self.get_data() self.check_traits() self.check_dataset() # Call reset again with just a few things changed to see if it # works correctly. s *= 10 src.reset(x=x,y=y, scalars=s) self.check_traits() self.check_dataset() def test_handlers(self): "Test if the various static handlers work correctly." x, y, s, src = self.get_data() s *= 2.0 src.scalars = s self.check_traits() self.check_dataset() def test_set(self): "Test if the set method works correctly." x, y, s, src = self.get_data() s *= 2.0 src.set(x=x,y=y,scalars=s) self.check_traits() self.check_dataset() ################################################################################ # `TestMTriangularMeshSource` ################################################################################ class TestMTriangularMeshSource(unittest.TestCase): def setUp(self): x, y, z = N.array([0, 0, 0]), N.array([0, 0, 1]), N.array([0, 1, 1]) s = N.array([0.1, 0.2, 0.3]) self.x, self.y, self.z, self.s = x, y, z, s self.triangles = triangles = N.array([[0, 1, 2]]) src = sources.MTriangularMeshSource() src.reset(x=x, y=y, z=z, triangles=triangles, scalars=s) self.src = src def tearDown(self): return def get_data(self): return self.x, self.y, self.z, self.triangles, self.s, self.src def check_traits(self): """Check if the sources traits are set correctly.""" x, y, z, triangles, s, src = self.get_data() # Check if points are set correctly. self.assertEqual(N.alltrue(src.x == x), True) self.assertEqual(N.alltrue(src.y == y), True) self.assertEqual(N.alltrue(src.z == z), True) # Check the scalars. self.assertEqual(N.alltrue(src.scalars == s), True) def test_reset(self): "Test the reset method." x, y, z, triangles, s, src = self.get_data() self.check_traits() # Call reset again with just a few things changed to see if it # works correctly. x *= 5.0 s *= 10 src.reset(x=x,y=y,z=z, triangles=triangles, scalars=s) self.check_traits() def test_changed_size(self): """ Change the number of the points, and establish to new points, to check that we don't get errors with the dimensions of the scalars. """ n = 100 _, _, _, _, _, src = self.get_data() triangles = N.c_[N.arange(n-3), N.arange(n-3)+1, n-1-N.arange(n-3)] x, y, z = N.random.random((3, n)) src.reset(x=x, y=y, z=z, triangles=triangles) def test_handlers(self): "Test if the various static handlers work correctly." x, y, z, triangles, s, src = self.get_data() x *= 2.0 y *= 2.0 s *= 2.0 src.x = x src.y = y src.scalars = s src.triangles = triangles self.check_traits() def test_set(self): "Test if the set method works correctly." x, y, z, triangles, s, src = self.get_data() x *= 2.0 s *= 2.0 src.set(x=x,scalars=s) self.check_traits() y *= 9.0 s *= 2.0 src.set(y=y, scalars=s) self.check_traits() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_array_source.py0000644000175100001440000001541211674464502022536 0ustar ischnellusers00000000000000""" Tests for the ArraySource class. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran Enthought, Inc. # License: BSD Style. import unittest import pickle import numpy # Enthought library imports. from traits.api import TraitError from mayavi.sources.array_source import ArraySource from mayavi.modules.outline import Outline from mayavi.modules.surface import Surface class TestArraySource(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" d = ArraySource() self.data = d def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" return def make_2d_data(self): s = numpy.array([[0, 1],[2, 3]], 'd') v = numpy.array([[[1,1,1], [1,0,0]],[[0,1,0], [0,0,1]]], 'd') tps = numpy.transpose s, v = tps(s), tps(v, (1, 0, 2)) return s, v def make_3d_data(self): s = numpy.array([[[0, 1],[2, 3]], [[4, 5],[6, 7]]], 'd') v = numpy.array([[[[0,0,0], [1,0,0]], [[0,1,0], [1,1,0]]], [[[0,0,1], [1,0,1]], [[0,1,1], [1,1,1]]]], 'd') tps = numpy.transpose s, v = tps(s), tps(v, (2, 1, 0, 3)) return s, v def test_input_validation(self): """Tests if only the correct forms of input arrays are supported.""" obj = self.data # These should work. obj.scalar_data = numpy.zeros((2,2), 'd') obj.scalar_data = numpy.zeros((2,2,2), 'd') obj.scalar_data = None obj.vector_data = numpy.zeros((2,2,3), 'd') obj.vector_data = numpy.zeros((2,2,2,3), 'd') obj.vector_data = None # These should not. self.assertRaises(TraitError, setattr, obj, 'scalar_data', [1,2,3]) self.assertRaises(TraitError, setattr, obj, 'scalar_data', numpy.zeros((2,2,2,3), 'd')) obj.scalar_data = None self.assertRaises(TraitError, setattr, obj, 'vector_data', [[1,2,3]]) self.assertRaises(TraitError, setattr, obj, 'vector_data', numpy.zeros((2,2,2,1), 'd')) obj.vector_data = None obj.scalar_data = numpy.zeros((2,2), 'd') self.assertRaises(TraitError, setattr, obj, 'vector_data', numpy.zeros((4,4,3), 'd')) obj.vector_data = numpy.zeros((2,2,3), 'd') self.assertRaises(TraitError, setattr, obj, 'scalar_data', numpy.zeros((4,3), 'i')) self.assertRaises(TraitError, setattr, obj, 'scalar_data', numpy.zeros((2,2,2), 'i')) obj.scalar_data = numpy.zeros((2,2), 'f') # Clean up the object so it can be used for further testing. obj.scalar_data = obj.vector_data = None def test_2d_data(self): """Generic tests for 2D data arrays.""" d = self.data sc, vec = self.make_2d_data() d.origin = (-1, -1, 0) d.scalar_data = sc d.vector_data = vec d.start() # Start the object so it flushes the pipeline etc. # Create an outline for the data. o = Outline() d.add_child(o) o.start() self.assertEqual(tuple(o.actor.actor.bounds), (-1., 0., -1., 0., 0., 0.)) # Create a surface module. surf = Surface() d.add_child(surf) self.assertEqual(surf.running, True) tps = numpy.transpose expect = [tps(sc), tps(vec, (1, 0, 2))] sc1 = surf.actor.mapper.input.point_data.scalars.to_array() self.assertEqual(numpy.allclose(sc1.flatten(), expect[0].flatten()), True) vec1 = surf.actor.mapper.input.point_data.vectors.to_array() self.assertEqual(numpy.allclose(vec1.flatten(), expect[1].flatten()), True) def test_3d_data(self): "Test for 3D data arrays." # Add a 3D data source d = self.data sc, vec = self.make_3d_data() d.scalar_data = sc d.vector_data = vec d.start() # Start the object so it flushes the pipeline etc. # Create an outline for the data. o = Outline() d.add_child(o) o.start() self.assertEqual(tuple(o.actor.actor.bounds), (0, 1., 0., 1., 0., 1.)) # Create a surface module. surf = Surface() d.add_child(surf) self.assertEqual(surf.running, True) tps = numpy.transpose expect = [tps(sc), tps(vec, (2,1,0,3))] sc2 = surf.actor.mapper.input.point_data.scalars.to_array() self.assertEqual(numpy.allclose(sc2.flatten(), expect[0].flatten()), True) vec2 = surf.actor.mapper.input.point_data.vectors.to_array() self.assertEqual(numpy.allclose(vec2.flatten(), expect[1].flatten()), True) def test_pickle(self): "Test if pickling works." # Test if saving a visualization and restoring it works. d = self.data sc, vec = self.make_3d_data() d.scalar_data = sc d.vector_data = vec d.spacing = [1, 2, 3] d.origin = [4, 5, 6] d.start() # Start the object so it flushes the pipeline etc. # Create an outline for the data. o = Outline() d.add_child(o) o.start() # Create a surface module. surf = Surface() d.add_child(surf) data = pickle.dumps(d) del d, surf, o d = pickle.loads(data) # We must explicitly start the object. d.start() mm = d.children[0] o, surf = mm.children # Test the unpciked state. self.assertEqual(tuple(o.actor.actor.bounds), (4., 5., 5., 7., 6., 9.)) self.assertEqual(surf.running, True) self.assertEqual(o.running, True) self.assertEqual(d.running, True) self.assertEqual(numpy.allclose(d.spacing, [1, 2, 3]), True) self.assertEqual(numpy.allclose(d.origin, [4, 5, 6]), True) tps = numpy.transpose expect = [tps(sc), tps(vec, (2,1,0,3))] sc2 = surf.actor.mapper.input.point_data.scalars.to_array() self.assertEqual(numpy.allclose(sc2.flatten(), expect[0].flatten()), True) vec2 = surf.actor.mapper.input.point_data.vectors.to_array() self.assertEqual(numpy.allclose(vec2.flatten(), expect[1].flatten()), True) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_image_data_reader.py0000644000175100001440000000701111674464502023431 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Standard library imports. import unittest import numpy # Local imports. from common import get_example_data # Enthought library imports from mayavi.sources.image_reader import ImageReader from mayavi.tests.data_reader_test_base import DataReaderTestBase class TestDEMImageReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a DEM Image file. r = ImageReader() r.initialize(get_example_data('example.dem')) self.e.add_source(r) self.bounds =(557945.0, 567725.0, 5107991.5, 5121971.5, 682.0, 682.0) def check(self, scene, bounds, error = 1.01e-02): """Do the actual testing.""" src = scene.children[0] ot = src.children[0].children[0] ot.render() # Flush the pipeline. # Check the outline bounds self.assertEqual(numpy.allclose(ot.outline_filter.output.bounds,bounds, atol=error), True) self.assertEqual(src.reader.spatial_resolution, (30.0, 30.0, 1.0)) self.assertEqual(src.reader.elevation_bounds, (682.0, 2543.0)) def test_dem_image_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestMHAImageReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a Meta Image file. r = ImageReader() r.initialize(get_example_data('foot.mha')) self.e.add_source(r) self.bounds =(0.0, 255.0, 0.0, 255.0, 0.0, 0.0) def check(self, scene, bounds, error = 1.01e-02): """Do the actual testing.""" src = scene.children[0] ot = src.children[0].children[0] ot.render() # Flush the pipeline. # Check the outline bounds self.assertEqual(numpy.allclose(ot.outline_filter.output.bounds,bounds, atol=error), True) self.assertEqual(numpy.allclose(src.reader.data_spacing,(1., 1., 1.)),True) def test_mha_image_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_pipeline_info.py0000644000175100001440000000276411674464502022666 0ustar ischnellusers00000000000000""" Tests for the pipeline_info.py module """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran Enthought, Inc. # License: BSD Style. import unittest from tvtk.api import tvtk from mayavi.core import pipeline_info class TestPipelineInfoTest(unittest.TestCase): def test_tvtk_dataset_name(self): "Can tvtk datasets can be converted to names correctly." datasets = [tvtk.ImageData(), tvtk.StructuredPoints(), tvtk.RectilinearGrid(), tvtk.StructuredGrid(), tvtk.PolyData(), tvtk.UnstructuredGrid(), tvtk.Property(), # Not a dataset! 'foo', # Not a TVTK object. ] expect = ['image_data', 'image_data', 'rectilinear_grid', 'structured_grid', 'poly_data', 'unstructured_grid', 'none', 'none' ] result = [pipeline_info.get_tvtk_dataset_name(d) for d in datasets] self.assertEqual(result, expect) def test_default_pipeline_info(self): "Is the default PipelineInfo class built right." p = pipeline_info.PipelineInfo() self.assertEqual(len(p.datasets), 0) self.assertEqual(len(p.attribute_types), 0) self.assertEqual(len(p.attributes), 0) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_poly_data_reader.py0000644000175100001440000002377211674464502023346 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Standard library imports. import unittest # Local imports. from common import get_example_data # Enthought library imports from mayavi.sources.poly_data_reader import PolyDataReader from mayavi.tests.data_reader_test_base import DataReaderTestBase class TestPDBReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a PDB data file. r = PolyDataReader() r.initialize(get_example_data('caffeine.pdb')) self.e.add_source(r) self.bounds = (3.10, 10.78, -2.39, 4.03, -10.60, -6.31) def test_pdb_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestBYUReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a BYU data file. r = PolyDataReader() r.initialize(get_example_data('cow.g')) self.e.add_source(r) self.bounds = (-4.445, 5.998, -3.608, 2.760, -1.690, 1.690) def test_byu_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestOBJReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a OBJ data file. r = PolyDataReader() r.initialize(get_example_data('shuttle.obj')) self.e.add_source(r) self.bounds = (-7.65, 7.04, -4.68, 4.68, -1.35, 4.16) def test_obj_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestParticleReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a Particle data file. r = PolyDataReader() r.initialize(get_example_data('Particles.raw')) self.e.add_source(r) r.reader.set(data_byte_order='big_endian', data_type='float', file_type='binary') self.bounds = (817.33, 826.09, 545.02, 571.02, 1443.48, 1511.18) def test_particle_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestPLYReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a PLY data file. r = PolyDataReader() r.initialize(get_example_data('pyramid.ply')) self.e.add_source(r) self.bounds = (0.0, 1.0, 0.0, 1.0, 0.0, 1.60) def test_ply_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestPointsReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a Points data file. r = PolyDataReader() r.initialize(get_example_data('points.txt')) self.e.add_source(r) self.bounds = (0.0, 1.0, 0.0, 1.0, 0.0, 1.0) def test_points_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestSTLReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a STL data file. r = PolyDataReader() r.initialize(get_example_data('humanoid_tri.stla')) self.e.add_source(r) self.bounds = (0.60, 3.47, -3.96, 3.95, 3.05, 17.39) def test_stl_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestFacetReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a Facet data file. r = PolyDataReader() r.initialize(get_example_data('clown.facet')) self.e.add_source(r) self.bounds = (-0.5, 0.69, -0.49, 0.49, -1.09, 0.5) def test_facet_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestSLCReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a SLC data file. r = PolyDataReader() r.initialize(get_example_data('nut.slc')) self.e.add_source(r) self.bounds = (0.0, 67.0, 0.0, 40.0, 0.0, 58.0) def test_slc_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_optional_collection.py0000644000175100001440000001111311674464502024072 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy as np import unittest import datasets # Enthought library imports from mayavi.core.null_engine import NullEngine from mayavi.filters.contour import Contour from mayavi.filters.optional import Optional from mayavi.filters.collection import Collection from mayavi.filters.api import PolyDataNormals from mayavi.modules.api import Surface from mayavi.sources.vtk_data_source import VTKDataSource class TestOptionalCollection(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() e.new_scene() self.e=e sgrid=datasets.generateStructuredGrid() src = VTKDataSource(data = sgrid) e.add_source(src) c = Contour() # `name` is used for the notebook tabs. n = PolyDataNormals(name='Normals') o = Optional(filter=n, label_text='Compute normals') coll = Collection(filters=[c, o], name='IsoSurface') e.add_filter(coll) s = Surface() e.add_module(s) self.coll = coll self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self,coll): """Do the actual testing.""" scene = self.scene """Check if test status is OK given the collection.""" c, o = coll.filters c = c.filter n = o.filter r = coll.outputs[0].point_data.scalars.range self.assertEqual(np.allclose(r, (6.09,6.09), atol=1.01e-03), True) # Adding a contour should create the appropriate output in # the collection. c.contours.append(200) self.assertEqual(np.allclose(r, [6.09,6.09], atol=1.01e-03), True) # the collection's output should be that of the normals. self.assertEqual(coll.outputs[0] is n.outputs[0],True) # disable the optional filter and check. o.enabled = False self.assertEqual('disabled' in o.name,True) self.assertEqual(coll.outputs[0] is c.outputs[0],True) # Set back everything to original state. c.contours.pop() o.enabled = True self.assertEqual(np.allclose(r, (6.09,6.09), atol=1.01e-03), True) self.assertEqual(coll.outputs[0] is n.outputs[0], True) self.assertEqual('disabled' not in o.name, True) def test_optional_collection(self): "Test if the test fixture works" #Now test. coll = self.coll self.check(coll) #from mayavi.tools.show import show #show() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene coll = self.scene.children[0].children[0] self.check(coll) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) coll = s.children[0].children[0] self.check(coll) # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 coll = s.children[0].children[0] self.check(coll) #from mayavi.tools.show import show #show() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_core_common.py0000644000175100001440000000571011674464502022340 0ustar ischnellusers00000000000000""" Tests for the mayavi.core.common module. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. import unittest from mayavi.core.common import get_object_path, get_engine from mayavi.sources.parametric_surface import \ ParametricSurface from mayavi.modules.outline import Outline from mayavi.core.null_engine import NullEngine class TestCoreCommon(unittest.TestCase): def setUp(self): e = NullEngine() e.start() e.new_scene() scene = e.scenes[-1] s = ParametricSurface() e.add_source(s) o = Outline() s.add_child(o) o1 = Outline() s.add_child(o1) self.scene = scene self.e = e self.s = s self.o = o self.o1 = o1 return def test_get_object_path(self): "Check if the get_object_path method works correctly." e, scene, s, o, o1 = self.e, self.scene, self.s, self.o, self.o1 value = get_object_path(scene, e) self.assertEqual(value, 'engine.scenes[0]') value = get_object_path(s, e) self.assertEqual(value, 'engine.scenes[0].children[0]') value = get_object_path(o.module_manager, e) self.assertEqual(value, 'engine.scenes[0].children[0].children[0]') value = get_object_path(o, e) self.assertEqual(value, 'engine.scenes[0].children[0].children[0].children[0]') value = get_object_path(o1, e) self.assertEqual(value, 'engine.scenes[0].children[0].children[0].children[1]') # With respect to the scene. value = get_object_path(o, scene, 'scene') self.assertEqual(value, 'scene.children[0].children[0].children[0]') # With respect to the source. value = get_object_path(o, s, 's') self.assertEqual(value, 's.children[0].children[0]') value = get_object_path(o1, s, 's') self.assertEqual(value, 's.children[0].children[1]') def test_get_engine(self): e, scene, s, o, o1 = self.e, self.scene, self.s, self.o, self.o1 self.assertEqual(e, get_engine(scene)) self.assertEqual(e, get_engine(s)) self.assertEqual(e, get_engine(o)) self.assertEqual(e, get_engine(o1)) self.assertEqual(e, get_engine(o.module_manager)) self.assertEqual(e, get_engine(o1.module_manager)) def test_remove(self): "Does obj.remove() work correctly" # Fails only when the current object is the one that is removed. self.e.current_object = self.o1 mm = self.o1.module_manager # Remove the object. self.o1.remove() # Now add another object. o1 = Outline() self.e.add_module(o1) # Is it really added? self.assertEqual(o1.module_manager, mm) self.assertEqual(o1.parent, mm) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_vtk_data_source.py0000644000175100001440000001254711674464502023223 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy import unittest import datasets # Enthought library imports from mayavi.core.null_engine import NullEngine from mayavi.sources.vtk_data_source import VTKDataSource from mayavi.modules.outline import Outline from mayavi.modules.iso_surface import IsoSurface from mayavi.modules.contour_grid_plane import ContourGridPlane from mayavi.modules.scalar_cut_plane import ScalarCutPlane from tvtk.api import tvtk class TestVTKDataSource(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() e.new_scene() self.e=e sgrid=datasets.generateStructuredGrid() src = VTKDataSource(data = sgrid) e.add_source(src) # Create an outline for the data. o = Outline() e.add_module(o) # Create one ContourGridPlane normal to the 'x' axis. cgp1 = ContourGridPlane() e.add_module(cgp1) # Set the position to the middle of the data. cgp1.grid_plane.position = 15 # Another with filled contours normal to 'y' axis. cgp2 = ContourGridPlane() cgp2.contour.filled_contours = True # Set the axis and position to the middle of the data. cgp2.grid_plane.axis = 'y' cgp2.grid_plane.position = 15 e.add_module(cgp2) # An isosurface module. iso = IsoSurface(compute_normals=True) e.add_module(iso) iso.contour.contours = [5] # An interactive scalar cut plane. cp = ScalarCutPlane() e.add_module(cp) ip = cp.implicit_plane ip.normal = 0,0,1 ip.origin = 0.5, 0.5, 1.0 # Since this is running offscreen this seems necessary. ip.widget.origin = 0.5, 0.5, 1.0 ip.widget.enabled = False self.scene = e.current_scene self.cgp2=cgp2 self.iso=iso self.cp=cp return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self): """Do the actual testing.""" scene = self.scene src = scene.children[0] mm = src.children[0] cgp1 = mm.children[1] self.assertEqual(cgp1.grid_plane.position,15) cgp2 = mm.children[2] self.assertEqual(cgp2.contour.filled_contours,True) self.assertEqual(cgp2.grid_plane.axis, 'y') self.assertEqual(cgp2.grid_plane.position,15) iso = mm.children[3] ctr = iso.contour.contours self.assertEqual(iso.compute_normals,True) self.assertEqual(ctr, [5.0]) rng = iso.actor.mapper.input.point_data.scalars.range self.assertEqual(rng[0],5.0) self.assertEqual(rng[1],5.0) cp = mm.children[4] ip = cp.implicit_plane self.assertAlmostEqual(numpy.sum(ip.normal - (0,0,1)) , 1e-16) self.assertAlmostEqual(numpy.sum(ip.origin - (0.5, 0.5, 1.0)), 0.0) self.assertEqual(ip.widget.enabled,False) def test_vtk_data_source(self): "Test if the test fixture works" #Now test. self.check() #from mayavi.tools.show import show #show() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) cp = source.children[0].children[-1] cp.implicit_plane.widget.enabled = False self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 cp = source1.children[0].children[-1] cp.implicit_plane.widget.enabled = False self.check() def test_add_child(self): """Test if adding a source as a child works correctly.""" src = self.e.scenes[0].children[0] new_src = VTKDataSource(data=tvtk.PolyData()) src.add_child(new_src) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/data/0000755000175100001440000000000011674464502017335 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/tests/data/nut.slc0000644000175100001440000015235011674464502020654 0ustar ischnellusers0000000000000011111 68 41 59 8 1.000000 1.000000 1.000000 2 5 0 1 100 87 X*AAT`````````````````````TTA**6jjj666jjj66YY̔Y(k֥k(((k֥k((((k֥kk((kk֥k(((kk֥k((((k֥kk((kk֥k(((k֥kk((k֥kk(((kɳ֥k((kkޟubbQCC>;@@I]]z֥k(((kkޟubbQCC>;@@I]]z֥k((((kxDD+ .KK~֥kk(kkd.2d֥kkkd.2d֥kDOO  KK֥DDKF֋**\\ZZ***\\ZZ*oo{++--vo GL GL #03##"ppgg"""ppgg"""VVKK"""AA  ;;"""99  55"""99  55"""88  88""">>  @@""">>  @@"""QQQQ"""rrjj"""rrjj""%3.}}%% OF߁ mm..++smmm..++sm**eeZZ*NKԉNKԉCRR  OOԤCCjjm3-dԤjjjm3-dԤj''jۋQQ. +DDxԤjj''jjƄeeK@@;=CCO``vԤj''''jŰԤj'''jŰԤj''jԤjj''jjԤj'''jjԤj''''jԤjj''jjԤj'''jjԤj''''jԤjj''jԤj''WW˓WWW˓W5hhh55*AAR_____________________RRA***AAR_____________________RRA***AAT`````````````````````TTA**6jjj666jjj66YY̔Y(k֥k(((k֥k((((k֥kk((kk֥k(((kk֥k((((k֥kk((kk֥k(((k֥kk((k֥kk(((kɳ֥k((kkޟubbQCC>;@@I]]z֥k(((kkޟubbQCC>;@@I]]z֥k((((kxDD+ .KK~֥kk(kkd.2d֥kkkd.2d֥kDOO  KK֥DDKF֋**\\ZZ***\\ZZ*oo{++--vo GL GL #03##"ppgg"""ppgg"""VVKK"""AA  ;;"""99  55"""99  55"""88  88""">>  @@""">>  @@"""QQQQ"""rrjj"""rrjj""%3.}}%% OF߁ mm..++smmm..++sm**eeZZ*NKԉNKԉCRR  OOԤCCjjm3-dԤjjjm3-dԤj''jۋQQ. +DDxԤjj''jjƄeeK@@;=CCO``vԤj''''jŰԤj'''jŰԤj''jԤjj''jjԤj'''jjԤj''''jԤjj''jjԤj'''jjԤj''''jԤjj''jԤj''WW˓WWW˓W5hhh55*AAR_____________________RRA***AAR_____________________RRA***AAT`````````````````````TTA**6jjj666jjj66YY̔Y(k֥k(((k֥k((((k֥kk((kk֥k(((kk֥k((((k֥kk((kk֥k(((k֥kk((k֥kk(((kɳ֥k((kkޟubbQCC>;@@I]]z֥k(((kkޟubbQCC>;@@I]]z֥k((((kxDD+ .KK~֥kk(kkd.2d֥kkkd.2d֥kDOO  KK֥DDKF֋**\\ZZ***\\ZZ*oo{++--vo GL GL #03##"ppgg"""ppgg"""VVKK"""AA  ;;"""99  55"""99  55"""88  88""">>  @@""">>  @@"""QQQQ"""rrjj"""rrjj""%3.}}%% OF߁ mm..++smmm..++sm**eeZZ*NKԉNKԉCRR  OOԤCCjjm3-dԤjjjm3-dԤj''jۋQQ. +DDxԤjj''jjƄeeK@@;=CCO``vԤj''''jŰԤj'''jŰԤj''jԤjj''jjԤj'''jjԤj''''jԤjj''jjԤj'''jjԤj''''jԤjj''jԤj''WW˓WWW˓W5hhh55*AAR_____________________RRA***AAR_____________________RRA**45 Xy45 Xy45 Xy45 Xy187 XG3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4G296 X/!'#/2>F>2/ 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 / 7DMD7 /2>F>2/!'#1383 X   6S[WG*)%I{~L+ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ ) /SS/ )%I{~L+ +3S[WG*,   r424 X~ #UrzvfG" & 1xۆӶ?%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG%GʏG& 1xۆӶ?( RrzvfG" ) r416 X}&%[x{kM( $ :H#TלT#TלT#TלT#TלT#TלT#TלT#TלT#TלT#TלT#TלT#TלT#TלT#TלT#TלT#TלT#TלT#TלT$ :H& Xw{kM( '{416 X|$%[x{kM( " :H!TלT!TלT!TלT!TלT!TלT!TלT!TלT!TלT!TלT!TלT!TלT!TלT!TלT!TלT!TלT!TלT!TלT" :H$ Xw{kM( %z416 X{"%[x{kM(  :HTלTTלTTלTTלTTלTTלTTלTTלTTלTTלTTלTTלTTלTTלTTלTTלTTלT :H" Xw{kM( #y544 Xz %[x }zxy} {kM(  : HT לTT לTT לTTלTTלTTלTTלTTלTT לTT לTT לTT לTT לTTלTTלTTלTTלT :H Xw }{z{ {kM( !   x643 Xy  %[x{o^K;12:I]q {kM(  :ѾHTñלTTIJלTT לTT לTTלTTלTTלTT לTTʺ לTTñלTTñלTTıלTT ־לTT לTTלTTלTTלT : H Xw{p_L;55=L`t {kM(  w740 Xx   %[x{gE:c{kM(  :ɦ|V9&#5OrHTpS@402:JcלTTŚsUA402:JcלTT 꾒lQ>9:APhלTT 巕~snq}לTT לTTלTT לTT ͯ לTTŝy\JADM^x לTTpS@414>PiלTTpS@402:JcלTTĚsUA402:JcלTT 鼐jO>9:APhלTT ⴓ}qmo{לTT לTTלTT לT : ҵ}ols H Xw|gF>f {kM( v826 Xw %[xpI HTb* JלTTj/JלTTV*  JלTT f>&  ,VלTT ߘhG6+&##&*7FYqלTT ӑhNA9536=Ib לTT Ȋ[A2,('(.8Jk לTT}F% ,Fi לTTd, (=cלTTb* &ZלTTb* JלTTj.JלTTT)  JלTT d=%  *VלTT ޗgF6+%""%)5DVnלTT ՒiNB9436=Ha לTT ˍ]B3-(').9Jl לT :کe- .Qy H Xw~g.%d{kM( t785 Xu %[xd\{kM(  :ɂ/1{HTI  4}לTTQ  2{לTT?   2{לTT H$  <לTT k:" $4KyלTT ]6"   2W לTTR.  $=o לTTX"  )P לTTJ  6YלTTI  MלTTI  4}לTTP  2{לTT>   2{לTT ߄G#  <לTT j:"#3JwלTT ^6"   2V} לTTT0  %>o לT :Ґ@ 8h H Xwd b{kM( s748 Xt  %[xh!f{kM(:́(4H  TC /{לT  TQ,xלT  T 9,xלT  T {8 4~לT  T O% %;hלT  T {=  9dלT  T8 'O לT  TI  =u לT  TB 8`לT  TB PלT  TB 0|לT  TP,xלT  T7,xלT  T z7  4}לT  T N$ $:fלT  T |>  8bלT  T9'N לT :ԋ0$\ H  Xwh'l{kM(r671 Xs %[xq,8y{kM'֑/E5:K 7ל::a1ל:: @1ל:: {2 7ל:: A  1dל:: k. (T}ל::{/ > ל::O5t ל::I>pל::I)aל::K 7ל::_1ל:: >1ל:: z1 7ל:: A  0bל:: m/ 'Rzל::}0 = ל:'ܚ5[ 5 Xwq,>~{kM r621 Xr [xzGY {kBo߬E`wbIׂ% Bׂ U Bׂ 5Fׂ > /iׂ m(  #Oxׂ3 ; ׂd8 ׂ_M ׂ_9|ׂbJׂ$ Bׂ S Bׂ 4Fׂ > .gׂ o)  "Muׂ4 : ׂoJg wXwzG _ {kBr622 Xp Pxd /{ {`p  8ݭ (l  A e  }' e  Eg  I 6  ,  %O  BD  (H  & ,f  &%T  ( m  @ e  z& e  Eg  I 4~  -  $M|  CC u/ ݭMwd 5 {` q610 Xo  mz?g p- Mߤ7#e P XV  @XX 5XX G5XX m"6XX h! LXX >-S XX l\ XXS $j XXQ H XXQ ? XXV  @XX ~5XX E5XX l"6XX i! JXX ? ,Q XX m[ X M9 Q P lz?m p-  p588 Xo  v" 7 E  0'f 'Z>'j 9 > 97 9LD@ t" 7 E  1&g &X>&h uܒ$ u Buw-h uB   p592 Xo     Bur ] uB uه1qu=< mz /v 7 v ${Q  H1X.3 .1 /M<= kz .v 7 v %{R  F1Vuׅo u Bur ` uB   p590 Xo     Bup^ uB u؂4yu;? lt .o 1o tF =+L)- ). *P:A jt ,o 1o sG <,Kue u Bupa uB    p588 Xo     Buqf uB uۆ>u@ I tw .q /q xtB 5,C*+{ *0 ,Z ? K sx .q 0q ztC 4,Auցa u Buq i uB    p584 Xo    But%r uB uސ J uN  %V " 5| 3| |~G 44?}2-} 2 6 4$f M  &X " 5| 3| ~~I 24={uيb u But%u uB    p572 Xo    Buy6# uB u+] uf/k +/ B % ;% &T  9B=A4 A B D.y e0m */ B % <% &V  7B;~uޛ'j u Buy6% uB    p584 Xo    Bu~LA uB u傻D%{ u ? B D Z8 J8 %8k FZ AY  = Y S ^ ?  A A E Z8 K8 '8l DZ ?u?"z u Bu~LA uB    p572 Xo Eu`X uEujA u 8 )X  o g (W pV < Va~K}O }%n ]  8  ,\  n i (W pV = Va~Iuc3 uEuaW uE p595 Xo  +vv: '` vv:%f vv: ; vvC 4 vv mF vv 2 =vv A%vv . #vv g#vv ?+vv: &_ vlَ  J m=ut)y u@ p634 Xo k~SE o, L bF O W >6g WW n (mWW |%NWW Z JWW + JWWqRWWk:WWk> WWl"e WW|!b WW >8k WW m )oWW }%NWW [ JWW , JWWrRWWk8W LQ#l O m~TC p-   p650 Xp  Lvr+y z_ 䂫:-| ܬ +  *^  K "R  O.  3 *  b*  C 0  @  Y  @  %[  A D  TH  +  ,a  J#T  P /  4 *  d*  C /  @  W ֎& > ܬPxs+w {`  q641 Xr  WvaQ zjAn 䂌'#n ܿv w'  '\ ց @ #Hց =!gց v#aցB aց-fց݄+=ց݄+Cց-4} ցA> ցv'  )_ց ? $Jց =!hց x$aցCaց-fց݄+;ցnj'j ܿv[xbO {kB  r703 Xs  Wv}P< zjL&~&&m ܿ49 w, -a ֛99 C &J֛99 :[֛99bR֛996 R֛99s'U֛99p&.t֛99p&5o֛99t(1q ֛99@ A ֛99v, /c֛99 B 'L֛99 :[֛99dR֛997 R֛99s'U֛99p& -r֛9'߰V R ܿ4%[x}Q: {kM r787 Xt  WvzH6 zjL'92 5{ ܿG  S @  =r ֛S  S W( 0Y֛S  S E   #\֛S  S`  R֛S  S5  R֛S  Sp*  S֛S  Sn*   *i֛S  Sn*  /b֛S  Ss-  8r ֛S  SL $O ֛S  S@  ?t ֛S  S V(1Z֛S  S F   $\֛S  Sb"  R֛S  S6  R֛S  Sp*  S֛S  Sn*   (g֛S 9ڦTFܿG %[xzH5 {kM' r810 Xu  WvxLA zjL'  9 䃠P Q ܿGS i5  !5] ֛SS ܂A$  /Kw֛SS c-  8l֛SSm1 *`֛SS݌A  )`֛SS|:  )`֛SS{: 0m֛SS{: 7f֛SS̃@  #H} ֛SSj6":k ֛SS i6   "6_ ֛SS ۂB%  1Mz֛SS e. :n֛SSo1 *`֛SSߍB  )`֛SS|:  )`֛SS{: 0l֛S :רa KݿG%[xxL= {kM(   s869 Xv  Wvz\' a zjL'  9 ёU-  +N~ ܿGS rN=5248?Lh ֛SS |P9.('(+1B\֛SS W2 &>c֛SSސR)$J֛SSa1"H֛SSЗ[."H֛SSЗ[.&P֛SSЗ\/ *L֛SSܥl>! $>e ֛SSnF/&""%+5Eh ֛SS rN>6349AMj ֛SS }P:.)'(,2C^֛SS Y3 '?e֛SSS* %K֛SSa2"H֛SSї[."H֛SSЗ[.&O֛S :ٶ}B 1gݿG%[x{\$ \ {kM(   t837 Xw  WvwZ.T zjL'  9 ߲rc_cn ܿGS ֛SS 涘 ֛SS ܤx[I@=BO`}֛SS Ǎ`?+(:X֛SSȖgC+#5S}֛SS侏b@)#5S}֛SS侏b@)%8X֛SSeC,&9T~֛SSجaJ>9:DXw ֛SS ʧ|x} ֛SS 翩 ֛SS 緙 ֛SS ާz]K@>CPb֛SS Ɏa@, (:X֛SSɖgC+#5S}֛SS侏b@)#5S}֛SS侏b@)$8X֛S :˦wK) 9b ݿG%[xuW,O {kM(    u703 Xx   Wv u_H812@Y} zjL'  9ܿGS֛SS֛SS ȷ֛SS ڵuu~֛SSв~ohku֛SSʬ|nhku֛SSʬ|nhlw֛SSϲwrz ֛SS Ʒ ֛SS֛SS֛SS֛SS ˺֛SS ܷwu~֛SSѲoiku֛SSʬ|nhku֛SSʬ|nhkv֛S :̴}i\X^p ܿG%[x r\E6.0%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF%FɎF& 0wۆҵ>'"UrzueG! ) r382 X   2RZVF*)%Iz}K* ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. ) .RR. )%Iz}K* +6RZVF*,  r293 X/ &"/1=E=2/ 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 / 7CLC7 /1=E=2/ &"0187 XG3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4G45 Xy45 Xy45 Xy45 Xy45 Xy45 Xy45 Xymayavi-4.1.0/mayavi/tests/data/caffeine.pdb0000644000175100001440000000375111674464502021572 0ustar ischnellusers00000000000000ATOM 1 N1 BENZ 1 5.040 1.944 -8.324 ATOM 2 C2 BENZ 1 6.469 2.092 -7.915 ATOM 3 C3 BENZ 1 7.431 0.865 -8.072 ATOM 4 C4 BENZ 1 6.916 -0.391 -8.544 ATOM 5 N5 BENZ 1 5.532 -0.541 -8.901 ATOM 6 C6 BENZ 1 4.590 0.523 -8.394 ATOM 7 C11 BENZ 1 4.045 3.041 -8.005 ATOM 8 H111BENZ 1 4.453 4.038 -8.264 ATOM 9 H112BENZ 1 3.101 2.907 -8.570 ATOM 10 H113BENZ 1 3.795 3.050 -6.926 ATOM 11 O21 BENZ 1 6.879 3.181 -7.503 ATOM 12 C51 BENZ 1 4.907 -1.659 -9.696 ATOM 13 H511BENZ 1 4.397 -1.273 -10.599 ATOM 14 H512BENZ 1 5.669 -2.391 -10.028 ATOM 15 H513BENZ 1 4.161 -2.209 -9.089 ATOM 16 O61 BENZ 1 3.470 0.208 -7.986 ATOM 17 N1 NSP3 1B 8.807 0.809 -7.799 ATOM 18 N1 NSP3 1C 7.982 -1.285 -8.604 ATOM 19 C1 CSP3 1D 9.015 -0.500 -8.152 ATOM 20 H1 CSP3 1D 10.007 -0.926 -8.079 ATOM 21 C1 CSP3 1E 9.756 1.835 -7.299 ATOM 22 H11 CSP3 1E 10.776 1.419 -7.199 ATOM 23 H12 CSP3 1E 9.437 2.207 -6.309 ATOM 24 H13 CSP3 1E 9.801 2.693 -7.994 TER mayavi-4.1.0/mayavi/tests/data/points.txt0000644000175100001440000000006011674464502021406 0ustar ischnellusers000000000000000 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 mayavi-4.1.0/mayavi/tests/data/foot.mha0000644000175100001440000000021111674464502020765 0ustar ischnellusers00000000000000NDims = 2 DimSize = 256 256 ElementType = MET_UCHAR ElementSpacing = 1.0 1.0 1.0 ElementByteOrderMSB = False ElementDataFile = foot.raw mayavi-4.1.0/mayavi/tests/data/example.dem0000644000175100001440000014400011674464502021456 0ustar ischnellusers00000000000000MOUNT ST. HELENS, WASH. (POST-ERUPTION) MSH-M80 3-176 09/06/80 B-8 N=2.00X1.00 S=2.00X1.00MM 9 PANEL FILES 30M X 30M INTERVAL 1 1 1 10 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 0.000000000000000D+00 2 2 4 0.557945011821133D+06 0.510799133901730D+07 0.557813813954232D+06 0.512188002753253D+07 0.567449420312715D+06 0.512197873846927D+07 0.567602487473599D+06 0.510809006713248D+07 0.682000000000000D+03 0.254300000000000D+04 0.000000000000000D+00 10.300000E+020.300000E+020.100000E+01 1 327 1 1 22 1 0.557820000000000D+06 0.512124000000000D+07 0.000000000000000D+00 0.888000000000000D+03 0.913000000000000D+03 909 910 910 910 911 912 913 911 910 911 911 911 910 911 913 913 911 906 902 896 890 888 1 2 128 1 0.557850000000000D+06 0.511806000000000D+07 0.000000000000000D+00 0.890000000000000D+03 0.120500000000000D+04 1202 1204 1203 1205 1205 1202 1198 1194 1191 1188 1185 1176 1170 1168 1166 1165 1167 1168 1167 1165 1160 1151 1158 1165 1163 1166 1170 1163 1159 1159 1154 1147 1144 1137 1130 1121 1110 1096 1098 1096 1088 1084 1081 1080 1082 1084 1080 1071 1055 1045 1039 1040 1039 1035 1029 1025 1023 1021 1018 1010 1009 1011 1010 1007 1001 998 996 996 992 988 993 995 995 994 991 983 979 977 970 965 961 955 950 948 953 958 951 943 936 935 934 932 930 928 923 922 924 918 915 916 913 910 911 912 911 910 910 910 910 910 912 912 913 911 910 911 911 912 910 909 910 907 904 900 898 895 890 890 1 3 234 1 0.557880000000000D+06 0.511488000000000D+07 0.000000000000000D+00 0.891000000000000D+03 0.123000000000000D+04 1201 1202 1207 1210 1209 1207 1206 1206 1202 1196 1185 1191 1197 1193 1193 1196 1208 1213 1212 1216 1222 1225 1227 1229 1226 1225 1228 1227 1227 1230 1225 1215 1206 1205 1218 1222 1224 1223 1223 1224 1223 1221 1216 1205 1192 1186 1180 1174 1183 1193 1204 1208 1209 1214 1216 1215 1202 1187 1172 1143 1110 1091 1072 1054 1032 1016 1014 1012 1008 994 994 1010 1020 1035 1064 1075 1080 1104 1110 1095 1087 1078 1064 1050 1037 1018 1007 1004 998 994 998 1004 1012 1028 1041 1051 1068 1083 1093 1106 1121 1130 1145 1168 1183 1195 1206 1210 1208 1209 1208 1203 1195 1188 1184 1180 1177 1173 1170 1170 1168 1167 1168 1169 1167 1162 1158 1157 1164 1169 1167 1170 1173 1165 1161 1161 1150 1138 1132 1123 1114 1106 1099 1086 1084 1081 1076 1076 1076 1070 1069 1071 1069 1067 1062 1051 1042 1041 1037 1033 1029 1026 1024 1024 1023 1013 1013 1017 1014 1012 1010 1004 997 991 987 984 985 984 979 981 982 977 972 966 962 960 959 953 949 949 946 944 942 939 936 936 935 932 931 929 923 921 921 918 915 915 912 910 911 911 910 911 910 910 911 912 912 912 913 911 910 911 912 912 910 908 907 903 900 895 893 892 891 894 1 4 340 1 0.557910000000000D+06 0.511170000000000D+07 0.000000000000000D+00 0.894000000000000D+03 0.123200000000000D+04 949 950 955 958 959 961 963 966 968 969 971 973 975 978 981 985 987 988 993 995 995 996 998 1000 1000 1000 1003 1002 999 1002 1006 1012 1016 1018 1015 1015 1017 1022 1026 1028 1032 1037 1041 1044 1047 1051 1055 1059 1065 1069 1068 1064 1060 1068 1072 1072 1076 1079 1079 1080 1083 1086 1088 1090 1092 1095 1101 1103 1104 1103 1104 1108 1113 1115 1114 1116 1118 1121 1124 1129 1133 1136 1135 1135 1134 1139 1141 1143 1148 1153 1159 1165 1169 1168 1171 1176 1180 1184 1188 1190 1191 1194 1196 1198 1203 1204 1201 1203 1207 1209 1209 1207 1207 1210 1207 1201 1190 1192 1194 1188 1191 1201 1212 1217 1215 1219 1223 1225 1228 1231 1229 1228 1229 1226 1224 1229 1225 1215 1205 1204 1217 1224 1229 1228 1229 1232 1224 1216 1211 1203 1195 1187 1183 1181 1192 1203 1211 1214 1216 1220 1222 1222 1206 1187 1166 1133 1099 1080 1062 1043 1036 1030 1028 1023 1016 1001 1001 1018 1028 1043 1074 1085 1090 1118 1124 1108 1096 1083 1068 1047 1025 1005 996 999 997 995 997 1006 1018 1035 1049 1058 1075 1090 1101 1116 1132 1143 1157 1173 1184 1195 1208 1213 1213 1213 1210 1203 1196 1189 1183 1178 1174 1173 1172 1172 1170 1169 1171 1168 1163 1160 1159 1162 1168 1173 1173 1174 1175 1169 1166 1164 1152 1138 1121 1108 1099 1095 1092 1084 1077 1073 1073 1070 1067 1063 1060 1058 1056 1055 1054 1048 1042 1037 1035 1034 1032 1029 1026 1026 1024 1017 1017 1018 1015 1012 1007 1004 1001 999 994 988 986 982 977 977 975 971 968 966 962 960 960 954 950 949 947 944 944 942 938 937 935 933 930 927 923 921 920 918 916 914 912 910 911 911 910 911 911 911 912 913 912 912 913 912 911 911 912 912 912 911 909 905 901 896 894 894 895 899 1 5 445 1 0.557940000000000D+06 0.510855000000000D+07 0.000000000000000D+00 0.834000000000000D+03 0.123600000000000D+04 982 984 986 988 994 997 988 988 991 988 989 995 991 989 991 995 1001 1000 998 992 988 985 985 980 970 946 924 907 895 884 866 850 834 857 873 875 879 883 885 888 890 883 878 879 881 883 885 886 885 888 890 888 889 891 890 888 885 885 887 895 897 896 903 908 911 915 918 923 925 925 926 928 931 930 929 928 925 921 918 914 909 902 897 896 898 902 907 911 911 914 916 917 918 920 922 925 928 932 935 938 940 943 945 946 949 951 951 955 958 959 961 963 966 968 970 972 973 975 979 982 986 988 990 993 996 995 996 998 1002 1003 1004 1006 1006 1004 1006 1008 1013 1018 1021 1019 1018 1018 1022 1026 1029 1034 1039 1041 1044 1048 1051 1055 1060 1065 1069 1071 1069 1065 1068 1071 1073 1077 1081 1082 1084 1086 1087 1089 1090 1092 1096 1103 1105 1106 1107 1109 1110 1116 1119 1118 1120 1124 1125 1127 1129 1135 1139 1140 1139 1138 1142 1144 1143 1146 1149 1153 1157 1162 1166 1169 1172 1178 1183 1183 1188 1193 1194 1196 1198 1202 1202 1202 1204 1206 1207 1209 1207 1209 1211 1210 1205 1196 1195 1193 1186 1192 1203 1216 1221 1218 1220 1223 1225 1229 1233 1231 1230 1231 1227 1224 1227 1222 1211 1204 1204 1215 1225 1233 1234 1235 1236 1221 1210 1205 1203 1200 1187 1185 1190 1201 1212 1221 1222 1221 1225 1227 1227 1208 1186 1162 1127 1092 1078 1062 1045 1052 1055 1050 1038 1024 1009 1011 1027 1037 1053 1083 1093 1100 1133 1141 1126 1102 1081 1065 1040 1013 1002 1000 1009 1007 1005 1005 1012 1023 1039 1054 1067 1082 1096 1109 1126 1144 1156 1168 1179 1187 1197 1208 1213 1215 1214 1209 1201 1197 1193 1187 1181 1176 1174 1173 1173 1171 1170 1171 1167 1160 1165 1167 1167 1173 1177 1177 1177 1176 1171 1167 1164 1157 1145 1123 1108 1098 1092 1089 1085 1081 1078 1075 1076 1075 1065 1061 1059 1055 1050 1045 1041 1039 1039 1037 1033 1030 1029 1029 1027 1024 1019 1018 1017 1015 1011 1004 1003 1002 1001 995 988 988 986 983 979 974 967 967 969 962 959 959 955 952 952 949 945 945 943 939 937 934 930 928 926 924 924 925 920 916 914 912 912 913 911 908 913 916 915 914 913 913 913 913 913 913 912 912 913 915 915 913 910 905 898 897 898 900 904 1 6 463 1 0.557970000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.836000000000000D+03 0.123900000000000D+04 912 915 914 919 922 923 935 945 950 957 964 969 975 981 986 989 991 990 989 993 995 995 1002 1004 994 993 996 993 996 1004 997 992 996 996 996 996 994 988 985 982 980 976 969 953 934 914 900 887 872 855 836 862 878 878 881 884 887 889 890 884 879 880 882 883 886 887 887 890 891 890 891 892 892 891 889 888 890 900 900 897 905 910 912 916 921 925 927 928 928 930 932 931 931 934 931 924 924 921 913 910 906 901 900 901 907 911 912 915 917 918 920 923 924 927 930 934 937 939 941 943 947 947 951 952 951 954 956 959 963 966 968 969 971 973 975 976 979 983 987 989 991 994 996 996 997 999 1002 1005 1007 1009 1010 1009 1009 1009 1010 1013 1017 1019 1020 1020 1024 1027 1029 1034 1038 1041 1044 1046 1049 1054 1061 1066 1070 1072 1072 1071 1069 1070 1073 1079 1084 1085 1086 1087 1089 1091 1092 1094 1097 1103 1105 1107 1111 1113 1113 1119 1122 1120 1124 1129 1129 1131 1133 1135 1138 1142 1143 1143 1146 1146 1145 1150 1153 1154 1156 1160 1167 1171 1174 1179 1183 1185 1191 1197 1199 1199 1198 1202 1201 1203 1205 1206 1207 1209 1207 1209 1213 1211 1208 1203 1202 1201 1194 1197 1205 1220 1225 1221 1222 1224 1226 1230 1235 1232 1231 1232 1230 1228 1228 1222 1211 1206 1207 1217 1228 1236 1238 1239 1238 1218 1205 1202 1201 1199 1193 1196 1204 1212 1221 1230 1230 1229 1230 1231 1231 1206 1184 1167 1131 1096 1094 1081 1058 1074 1082 1071 1052 1032 1019 1021 1037 1048 1065 1091 1101 1111 1148 1157 1143 1107 1075 1056 1033 1011 1006 1007 1015 1013 1011 1012 1019 1029 1046 1060 1074 1089 1104 1119 1136 1155 1166 1177 1185 1192 1199 1209 1213 1214 1213 1209 1202 1200 1197 1193 1187 1180 1176 1174 1176 1173 1171 1172 1168 1164 1168 1171 1171 1176 1181 1181 1179 1178 1169 1162 1155 1153 1146 1132 1116 1101 1094 1090 1086 1085 1083 1078 1076 1073 1068 1064 1061 1055 1050 1046 1043 1041 1037 1035 1034 1033 1032 1031 1028 1024 1019 1018 1017 1014 1011 1006 1004 1000 995 991 988 987 985 982 979 974 967 966 966 962 959 958 956 954 953 949 945 944 942 938 937 935 932 931 930 927 926 926 922 919 919 919 919 921 919 915 921 923 920 917 915 915 915 914 915 915 914 914 915 919 917 913 907 903 900 901 903 906 908 1 7 463 1 0.558000000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.845000000000000D+03 0.123900000000000D+04 924 926 925 929 931 932 939 945 953 960 966 971 979 987 991 995 998 998 998 1002 1003 1004 1009 1011 1003 1002 1003 1005 1009 1015 1004 998 1006 1003 995 993 990 984 982 979 975 972 970 958 943 924 908 894 881 863 845 867 880 879 881 884 887 888 889 885 883 883 884 884 886 887 889 891 892 892 893 895 896 895 893 892 894 903 904 902 909 914 916 919 923 927 930 930 930 930 932 930 930 939 937 931 930 926 918 918 918 910 906 904 908 911 914 915 917 919 921 924 926 928 931 935 938 940 942 943 948 948 951 953 953 955 957 960 964 967 969 970 971 974 976 979 980 983 988 990 992 996 997 999 998 999 1003 1005 1008 1011 1012 1012 1011 1010 1009 1012 1016 1020 1021 1021 1027 1031 1031 1036 1040 1041 1043 1045 1048 1053 1062 1066 1070 1074 1075 1074 1075 1074 1073 1081 1087 1088 1089 1090 1093 1095 1096 1098 1100 1103 1105 1108 1113 1115 1116 1121 1125 1123 1126 1131 1132 1134 1138 1140 1142 1144 1147 1149 1149 1149 1149 1153 1155 1155 1159 1164 1169 1173 1177 1177 1179 1183 1188 1194 1198 1199 1198 1200 1201 1204 1205 1206 1208 1209 1208 1211 1214 1213 1211 1207 1211 1211 1203 1202 1205 1224 1230 1224 1225 1226 1226 1231 1237 1235 1233 1232 1231 1231 1233 1228 1220 1213 1213 1221 1231 1239 1239 1239 1237 1217 1206 1205 1199 1196 1201 1207 1214 1222 1229 1233 1236 1237 1235 1233 1230 1203 1183 1175 1138 1104 1112 1100 1073 1096 1102 1079 1059 1041 1028 1032 1049 1062 1078 1099 1110 1122 1159 1167 1153 1110 1073 1048 1028 1013 1010 1012 1017 1017 1017 1017 1024 1034 1053 1069 1080 1097 1114 1130 1147 1165 1178 1188 1193 1197 1201 1210 1212 1210 1209 1208 1206 1204 1200 1194 1189 1184 1178 1177 1180 1176 1173 1172 1169 1167 1172 1175 1175 1181 1184 1183 1180 1176 1165 1154 1141 1138 1135 1128 1113 1098 1094 1091 1088 1088 1085 1081 1076 1072 1067 1063 1058 1055 1052 1051 1047 1043 1043 1040 1037 1036 1034 1032 1027 1023 1019 1018 1016 1012 1008 1004 1003 1000 996 991 988 985 982 979 977 973 968 968 968 964 961 960 957 955 953 949 946 944 942 939 938 937 936 934 932 929 927 927 926 926 929 930 931 933 930 923 930 931 926 924 922 919 918 918 920 919 917 917 918 921 917 911 906 904 905 904 904 907 909 1 8 463 1 0.558030000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.855000000000000D+03 0.124200000000000D+04 928 930 932 939 942 942 949 953 955 961 969 975 984 994 997 1001 1007 1008 1008 1010 1012 1014 1019 1020 1015 1013 1012 1019 1023 1026 1012 1005 1014 1007 996 990 986 981 976 972 966 962 959 956 947 932 918 904 890 873 855 870 880 883 883 883 887 888 888 887 887 888 887 886 886 887 889 891 892 893 894 896 897 897 897 897 899 905 907 909 917 920 920 924 928 930 930 931 932 932 930 931 935 944 944 940 934 929 924 927 928 917 911 907 909 912 915 918 920 920 922 924 926 929 932 935 939 941 943 944 947 948 952 953 954 956 958 960 964 967 969 970 973 975 978 980 981 984 989 991 993 998 999 1001 1000 1001 1006 1007 1008 1011 1013 1015 1014 1013 1014 1016 1018 1021 1022 1022 1028 1031 1031 1037 1042 1041 1044 1048 1049 1052 1059 1061 1065 1073 1076 1078 1080 1079 1075 1083 1089 1091 1092 1094 1096 1098 1100 1102 1104 1106 1108 1111 1118 1119 1118 1123 1127 1125 1129 1134 1135 1138 1141 1146 1149 1147 1149 1151 1152 1152 1153 1155 1157 1156 1161 1167 1172 1175 1176 1177 1179 1183 1187 1191 1198 1199 1198 1201 1202 1206 1208 1209 1210 1210 1209 1212 1216 1215 1214 1211 1218 1222 1212 1207 1207 1227 1233 1226 1226 1227 1226 1231 1239 1240 1239 1233 1230 1230 1236 1235 1231 1221 1218 1224 1234 1242 1240 1238 1236 1219 1210 1210 1201 1196 1211 1220 1225 1232 1235 1234 1238 1242 1239 1230 1218 1202 1189 1183 1146 1114 1129 1119 1092 1110 1110 1080 1062 1049 1038 1044 1062 1074 1089 1112 1123 1134 1169 1173 1153 1112 1076 1049 1030 1015 1017 1019 1021 1021 1020 1019 1025 1036 1058 1074 1087 1106 1124 1141 1159 1177 1192 1200 1201 1202 1204 1210 1209 1207 1206 1206 1205 1205 1202 1193 1189 1186 1180 1179 1182 1178 1173 1170 1169 1171 1176 1179 1180 1184 1187 1187 1178 1168 1158 1144 1130 1124 1119 1114 1105 1096 1094 1091 1091 1089 1087 1084 1079 1073 1065 1062 1059 1055 1052 1050 1047 1046 1048 1045 1040 1037 1034 1031 1028 1024 1019 1017 1016 1011 1007 1005 1004 1002 998 993 987 984 982 980 977 974 970 969 969 965 963 962 958 955 953 951 948 946 945 944 942 939 936 934 932 930 933 937 938 940 943 940 938 940 936 929 934 936 932 930 928 923 922 923 924 924 922 921 921 925 922 914 908 907 911 907 905 906 910 1 9 463 1 0.558060000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.867000000000000D+03 0.124700000000000D+04 928 932 937 948 951 949 954 956 956 962 970 978 989 1001 1003 1008 1015 1017 1018 1021 1023 1026 1031 1032 1026 1024 1023 1031 1035 1037 1022 1014 1021 1012 998 990 983 979 969 960 952 945 939 944 942 933 925 914 900 883 867 874 881 886 886 886 888 888 888 888 889 890 891 890 887 887 888 892 894 894 895 896 897 898 901 902 905 910 913 917 924 927 925 929 931 933 933 933 934 934 932 938 944 950 950 947 936 931 931 932 930 920 914 909 910 912 916 920 924 924 926 927 928 930 933 936 939 941 944 946 947 949 952 953 955 958 959 961 964 966 969 971 973 976 978 981 984 986 989 991 994 1000 1001 1004 1002 1003 1008 1010 1011 1012 1015 1019 1018 1017 1017 1019 1021 1022 1023 1025 1030 1032 1031 1038 1042 1040 1044 1048 1049 1052 1058 1061 1066 1076 1081 1084 1084 1083 1081 1086 1091 1093 1094 1096 1099 1102 1105 1106 1108 1111 1112 1114 1122 1124 1120 1126 1130 1129 1131 1135 1138 1142 1146 1148 1149 1150 1151 1152 1155 1156 1156 1159 1160 1159 1163 1168 1172 1175 1176 1178 1183 1191 1191 1192 1197 1200 1201 1203 1204 1208 1211 1211 1213 1213 1212 1214 1217 1218 1217 1213 1222 1226 1219 1212 1208 1229 1235 1228 1229 1229 1228 1233 1241 1247 1245 1236 1229 1227 1236 1241 1242 1229 1224 1229 1238 1246 1247 1241 1232 1220 1213 1212 1207 1206 1219 1228 1235 1234 1234 1234 1236 1239 1238 1230 1218 1209 1200 1191 1155 1126 1149 1140 1114 1122 1115 1085 1069 1057 1047 1056 1077 1085 1100 1128 1137 1146 1176 1174 1150 1114 1082 1057 1036 1021 1022 1023 1025 1022 1020 1019 1025 1034 1054 1074 1093 1114 1132 1147 1166 1185 1197 1204 1207 1207 1208 1213 1211 1207 1206 1205 1202 1201 1199 1193 1190 1188 1182 1181 1182 1178 1175 1172 1172 1174 1180 1184 1187 1187 1187 1186 1175 1162 1148 1135 1124 1115 1107 1101 1099 1097 1096 1094 1094 1091 1088 1086 1078 1071 1068 1065 1062 1055 1053 1055 1053 1050 1048 1046 1042 1037 1034 1031 1028 1024 1019 1016 1014 1011 1008 1005 1003 1001 996 994 993 992 987 981 980 978 973 971 969 967 965 963 959 956 954 953 951 950 949 948 944 940 938 937 936 936 943 954 951 951 953 948 943 945 941 934 938 938 933 931 930 925 924 925 926 927 927 927 928 933 927 917 912 910 913 910 907 908 911 1 10 463 1 0.558090000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.879000000000000D+03 0.125700000000000D+04 932 935 942 954 957 955 956 956 955 959 966 978 992 1006 1006 1010 1019 1021 1023 1027 1028 1029 1038 1040 1032 1032 1035 1044 1047 1046 1032 1025 1029 1017 1002 992 985 978 964 950 938 929 922 926 926 922 922 916 902 890 879 879 882 887 890 891 889 889 889 889 890 892 893 893 889 887 886 891 894 895 895 895 897 899 903 909 913 917 920 923 930 932 931 933 935 936 935 934 936 937 936 938 941 948 948 944 935 933 936 931 927 921 916 912 912 914 917 919 922 926 928 930 931 932 934 937 939 941 944 947 949 950 953 955 957 960 961 961 965 968 970 972 974 976 980 983 986 988 991 992 995 1000 1002 1004 1004 1005 1009 1011 1013 1012 1016 1022 1022 1022 1021 1024 1026 1026 1027 1029 1033 1034 1031 1038 1042 1038 1043 1050 1051 1054 1058 1064 1071 1080 1085 1088 1088 1088 1086 1089 1091 1093 1097 1100 1103 1107 1111 1110 1111 1115 1116 1117 1122 1124 1123 1130 1134 1132 1133 1135 1139 1144 1151 1150 1150 1153 1154 1154 1158 1158 1156 1160 1162 1162 1166 1170 1174 1178 1182 1182 1185 1193 1193 1193 1198 1201 1204 1206 1206 1210 1213 1213 1214 1216 1214 1217 1221 1221 1220 1217 1225 1229 1225 1217 1212 1230 1237 1234 1233 1232 1232 1236 1243 1248 1246 1238 1231 1229 1239 1245 1247 1236 1231 1235 1242 1250 1257 1247 1229 1222 1217 1213 1211 1213 1226 1234 1240 1235 1233 1234 1236 1237 1236 1234 1230 1220 1211 1204 1168 1143 1171 1164 1136 1129 1115 1091 1074 1062 1056 1068 1091 1099 1115 1144 1153 1159 1177 1170 1146 1117 1090 1065 1043 1026 1025 1026 1028 1024 1022 1021 1026 1035 1053 1073 1095 1113 1129 1141 1161 1183 1193 1203 1214 1212 1212 1216 1214 1210 1210 1207 1202 1199 1197 1196 1193 1189 1184 1182 1182 1178 1175 1176 1177 1180 1186 1190 1190 1187 1183 1177 1167 1156 1138 1125 1117 1110 1103 1101 1100 1099 1097 1095 1093 1089 1085 1083 1080 1076 1067 1064 1062 1058 1057 1060 1056 1052 1049 1046 1043 1038 1033 1028 1027 1025 1019 1015 1012 1011 1008 1005 1003 1000 999 998 998 998 991 981 981 979 975 973 971 969 966 963 961 959 956 954 954 953 951 947 945 944 941 944 947 948 956 966 961 958 958 954 950 947 943 939 940 939 935 933 931 927 926 927 928 930 933 931 931 939 931 917 915 915 916 911 908 911 914 1 11 463 1 0.558120000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.881000000000000D+03 0.125600000000000D+04 937 940 946 958 960 960 964 963 956 962 970 979 990 1001 1002 1005 1012 1014 1017 1021 1022 1022 1032 1034 1027 1035 1042 1046 1049 1052 1041 1036 1038 1023 1006 997 988 980 967 951 931 920 912 909 908 907 909 906 895 889 884 881 882 887 892 895 891 890 890 891 893 894 895 894 891 889 887 892 895 895 895 896 897 901 906 915 922 925 926 929 936 937 936 937 938 938 937 936 938 938 937 936 937 942 943 941 934 934 938 931 926 922 918 915 915 916 917 918 920 926 927 928 932 935 936 940 942 942 944 948 950 952 955 957 959 963 963 964 968 970 971 973 975 977 980 983 987 990 992 994 996 1000 1002 1004 1005 1007 1009 1012 1013 1013 1018 1023 1025 1025 1026 1030 1032 1030 1032 1035 1036 1035 1031 1039 1042 1037 1041 1048 1057 1065 1072 1078 1083 1087 1090 1091 1094 1094 1091 1090 1090 1093 1099 1105 1106 1110 1114 1113 1114 1120 1122 1123 1126 1127 1127 1134 1138 1136 1136 1138 1140 1146 1152 1150 1151 1155 1156 1158 1159 1160 1159 1161 1163 1165 1170 1174 1177 1181 1186 1185 1187 1192 1193 1195 1202 1205 1207 1210 1209 1212 1214 1215 1216 1219 1216 1219 1224 1224 1223 1222 1228 1232 1231 1223 1217 1232 1241 1245 1240 1236 1237 1240 1243 1244 1242 1240 1236 1236 1242 1247 1249 1240 1238 1241 1247 1253 1256 1244 1228 1224 1221 1218 1214 1216 1233 1240 1242 1242 1240 1237 1240 1242 1235 1235 1237 1227 1220 1216 1182 1162 1194 1185 1154 1135 1115 1096 1081 1070 1069 1084 1106 1114 1130 1160 1168 1172 1180 1167 1141 1119 1094 1065 1044 1028 1027 1028 1032 1027 1024 1023 1028 1036 1053 1070 1089 1104 1118 1131 1151 1173 1186 1202 1219 1216 1215 1218 1216 1212 1213 1211 1205 1203 1200 1194 1192 1191 1185 1183 1183 1177 1175 1180 1182 1185 1192 1188 1178 1173 1168 1161 1155 1148 1130 1119 1115 1111 1108 1105 1103 1101 1097 1094 1089 1086 1083 1082 1082 1080 1074 1070 1068 1063 1061 1062 1058 1054 1050 1046 1043 1039 1034 1028 1028 1025 1019 1014 1011 1008 1005 1003 1001 1000 1000 996 990 988 986 983 982 980 977 974 973 972 969 965 964 963 961 957 954 954 952 950 953 953 948 955 961 960 964 970 964 961 960 956 952 949 946 943 943 942 938 938 937 933 932 931 931 932 935 934 934 942 932 917 919 920 919 913 910 914 918 1 12 463 1 0.558150000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.882000000000000D+03 0.125500000000000D+04 942 945 951 962 965 966 975 973 965 973 980 983 988 993 993 997 1006 1004 1004 1012 1016 1018 1023 1025 1023 1032 1040 1039 1045 1053 1050 1048 1049 1031 1012 1003 993 983 974 958 935 922 911 903 896 892 895 894 886 884 882 882 883 886 892 895 894 893 893 894 895 896 896 895 893 890 888 893 896 896 895 896 898 902 907 917 924 929 932 935 942 942 940 940 939 938 937 937 939 939 939 939 940 942 942 941 935 936 941 933 927 922 919 917 919 919 917 918 921 926 927 927 932 935 937 942 944 942 945 950 952 954 956 959 962 965 966 967 971 973 974 975 977 978 981 984 987 990 993 995 997 999 1001 1002 1005 1008 1012 1013 1015 1016 1021 1027 1026 1027 1029 1033 1036 1035 1037 1039 1038 1037 1036 1041 1044 1043 1046 1053 1068 1079 1087 1092 1096 1100 1100 1098 1094 1094 1095 1093 1093 1094 1101 1109 1111 1113 1116 1114 1116 1124 1127 1129 1133 1133 1131 1137 1139 1138 1140 1142 1144 1149 1153 1152 1154 1159 1160 1161 1164 1164 1162 1164 1166 1168 1172 1176 1181 1184 1187 1188 1189 1192 1194 1198 1204 1206 1207 1212 1213 1215 1217 1219 1219 1221 1219 1223 1227 1227 1227 1228 1230 1232 1233 1227 1222 1234 1244 1252 1245 1240 1241 1243 1245 1244 1243 1242 1240 1240 1246 1248 1249 1243 1243 1247 1253 1255 1251 1241 1230 1228 1226 1223 1216 1218 1241 1247 1245 1247 1246 1242 1245 1245 1238 1239 1242 1235 1230 1228 1199 1183 1214 1203 1172 1144 1120 1102 1088 1079 1083 1100 1122 1129 1146 1176 1182 1182 1181 1163 1137 1114 1088 1057 1042 1032 1029 1032 1038 1031 1027 1025 1031 1038 1047 1061 1077 1092 1108 1124 1144 1163 1179 1197 1217 1217 1217 1219 1216 1212 1213 1212 1207 1206 1203 1195 1193 1193 1187 1185 1185 1180 1179 1185 1188 1190 1192 1181 1162 1156 1150 1141 1139 1137 1125 1119 1118 1114 1109 1104 1101 1098 1095 1092 1090 1089 1089 1089 1082 1078 1078 1076 1073 1067 1064 1063 1058 1054 1052 1048 1044 1041 1037 1032 1031 1027 1020 1019 1017 1011 1006 1003 999 996 995 992 989 991 991 989 986 983 980 978 976 976 974 972 969 968 971 963 956 958 958 957 964 966 961 967 973 970 968 968 963 961 960 956 952 950 949 948 950 948 943 945 947 945 942 938 935 935 938 937 937 940 932 922 926 926 924 920 917 919 921 1 13 463 1 0.558180000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.879000000000000D+03 0.126000000000000D+04 947 949 955 966 970 974 986 985 977 984 990 992 998 1003 998 1003 1015 1009 1008 1018 1021 1023 1028 1028 1025 1033 1039 1038 1045 1055 1057 1058 1058 1038 1020 1012 1000 987 980 964 939 931 924 914 900 885 887 886 882 880 879 882 883 885 892 897 899 897 895 896 896 896 896 895 894 892 891 895 897 897 896 896 900 903 907 914 921 928 935 941 946 946 943 942 940 938 938 939 940 941 943 942 941 942 942 942 936 939 944 935 927 922 919 918 920 920 919 920 922 927 928 928 933 936 937 940 942 941 946 953 955 957 959 963 966 967 969 970 973 975 974 977 979 979 983 986 988 991 995 995 997 1000 1001 1002 1005 1009 1013 1015 1017 1020 1024 1027 1026 1028 1032 1035 1037 1039 1042 1043 1042 1041 1040 1042 1046 1051 1061 1073 1082 1090 1097 1100 1104 1110 1111 1110 1104 1100 1096 1097 1098 1096 1104 1112 1116 1117 1116 1115 1119 1129 1129 1130 1136 1137 1135 1140 1142 1140 1141 1144 1147 1151 1155 1156 1158 1163 1163 1164 1169 1171 1170 1169 1170 1173 1175 1177 1182 1186 1189 1189 1191 1195 1198 1201 1206 1208 1209 1214 1215 1217 1220 1222 1223 1223 1223 1226 1230 1230 1231 1232 1232 1232 1234 1231 1229 1236 1244 1254 1249 1246 1245 1246 1248 1246 1245 1244 1244 1245 1250 1250 1249 1244 1244 1248 1256 1260 1255 1245 1234 1232 1230 1228 1220 1222 1247 1251 1249 1251 1250 1246 1249 1249 1242 1244 1249 1243 1240 1241 1218 1206 1232 1214 1178 1149 1125 1107 1095 1090 1097 1116 1139 1146 1163 1191 1192 1187 1177 1157 1132 1107 1081 1052 1045 1042 1037 1040 1046 1037 1031 1029 1034 1040 1042 1051 1065 1083 1101 1119 1138 1157 1175 1191 1207 1215 1220 1220 1216 1212 1212 1210 1205 1205 1203 1199 1197 1196 1191 1187 1183 1183 1185 1192 1194 1194 1186 1171 1150 1143 1137 1132 1129 1128 1125 1121 1116 1110 1105 1103 1101 1100 1098 1095 1095 1094 1092 1089 1084 1081 1081 1079 1076 1070 1067 1065 1059 1055 1056 1050 1044 1047 1046 1042 1041 1037 1028 1029 1030 1023 1018 1013 1003 997 998 995 993 995 994 991 990 988 986 985 985 986 987 986 980 979 984 974 966 972 972 970 975 974 968 972 974 970 968 967 963 960 960 956 953 953 953 953 956 956 950 952 954 954 950 945 941 940 942 941 939 938 932 926 932 935 933 930 927 924 926 1 14 463 1 0.558210000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.877000000000000D+03 0.126300000000000D+04 953 957 963 972 976 981 994 994 986 993 997 999 1005 1009 1005 1010 1022 1018 1019 1028 1030 1030 1038 1039 1034 1040 1044 1042 1049 1059 1067 1068 1065 1046 1030 1022 1008 992 987 972 948 943 939 929 913 896 890 885 880 877 877 882 883 884 893 900 906 903 900 898 897 898 897 897 896 895 894 898 899 899 897 897 900 903 906 907 913 923 936 945 949 947 944 943 941 939 940 940 941 943 946 943 941 941 943 943 938 940 944 936 929 923 920 919 921 922 922 921 923 929 931 932 935 936 936 939 940 941 948 955 958 960 960 965 968 969 970 973 976 976 974 977 980 981 984 987 990 993 996 996 998 1002 1002 1004 1007 1010 1013 1015 1018 1023 1024 1024 1025 1029 1035 1036 1038 1044 1047 1048 1047 1046 1044 1043 1048 1060 1074 1086 1090 1096 1102 1105 1108 1113 1114 1115 1116 1111 1102 1100 1099 1100 1106 1112 1117 1120 1121 1121 1125 1131 1130 1130 1137 1139 1139 1143 1145 1141 1145 1149 1152 1154 1156 1158 1162 1165 1166 1168 1174 1177 1178 1176 1175 1175 1176 1178 1183 1188 1192 1192 1194 1197 1201 1205 1208 1210 1211 1216 1217 1219 1222 1225 1227 1227 1228 1231 1234 1233 1234 1236 1234 1235 1238 1239 1239 1239 1245 1254 1253 1250 1246 1247 1250 1248 1247 1248 1248 1250 1253 1252 1250 1248 1248 1249 1258 1263 1260 1249 1237 1235 1235 1234 1226 1227 1250 1255 1254 1255 1254 1251 1253 1253 1247 1250 1255 1251 1250 1252 1238 1229 1240 1214 1174 1152 1129 1107 1102 1103 1112 1134 1159 1168 1183 1204 1198 1186 1172 1152 1129 1101 1076 1054 1053 1053 1045 1049 1058 1047 1038 1032 1037 1043 1044 1049 1057 1076 1094 1113 1133 1153 1168 1184 1200 1213 1221 1221 1216 1212 1211 1208 1205 1205 1204 1202 1200 1197 1189 1186 1183 1187 1192 1198 1197 1194 1183 1168 1151 1140 1134 1132 1129 1125 1122 1118 1114 1109 1107 1110 1107 1104 1101 1098 1094 1095 1092 1087 1087 1086 1082 1079 1076 1071 1071 1072 1068 1065 1062 1051 1043 1052 1056 1057 1058 1054 1043 1043 1044 1039 1033 1027 1009 1000 1002 998 994 994 993 992 995 994 990 995 1000 1001 1002 1002 996 995 998 988 980 986 984 980 979 976 971 972 973 967 965 964 960 958 959 958 956 958 958 957 960 960 957 957 956 954 954 953 947 946 948 945 943 943 938 933 941 945 945 942 937 931 933 1 15 463 1 0.558240000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.879000000000000D+03 0.126600000000000D+04 959 963 969 976 981 988 1001 1002 996 1000 1002 1002 1008 1013 1010 1015 1025 1025 1028 1036 1036 1036 1044 1046 1043 1046 1047 1046 1055 1066 1074 1076 1072 1055 1040 1032 1016 999 995 981 958 957 954 943 928 911 897 887 880 879 879 881 884 887 893 899 905 906 906 902 901 901 900 898 898 896 896 898 899 899 897 898 901 903 904 901 907 919 935 946 951 949 946 945 943 940 942 943 942 944 946 944 942 941 944 945 940 942 944 940 933 925 921 920 923 924 924 922 924 930 933 936 937 934 931 937 941 943 951 958 959 961 961 965 968 969 971 973 977 977 974 978 981 983 985 988 992 996 998 998 1000 1004 1004 1006 1008 1010 1013 1016 1019 1024 1026 1027 1028 1032 1038 1037 1039 1045 1047 1050 1053 1052 1049 1049 1056 1074 1086 1095 1097 1100 1103 1105 1108 1113 1116 1119 1123 1119 1110 1107 1104 1102 1103 1107 1114 1121 1126 1126 1128 1130 1130 1132 1139 1142 1143 1146 1146 1144 1148 1151 1153 1156 1159 1162 1165 1169 1169 1170 1175 1180 1183 1183 1181 1177 1179 1182 1185 1189 1194 1194 1197 1200 1205 1209 1211 1213 1216 1220 1220 1223 1225 1227 1230 1231 1233 1234 1235 1236 1238 1240 1238 1237 1241 1248 1252 1242 1245 1254 1257 1255 1247 1249 1252 1249 1250 1252 1252 1254 1257 1255 1252 1254 1254 1253 1261 1266 1265 1253 1241 1239 1239 1239 1231 1233 1253 1258 1259 1260 1259 1257 1258 1258 1252 1255 1261 1260 1260 1263 1257 1248 1235 1205 1170 1154 1132 1105 1108 1116 1126 1150 1178 1187 1199 1214 1201 1185 1168 1147 1125 1097 1076 1062 1063 1061 1050 1059 1076 1056 1043 1035 1041 1048 1055 1059 1061 1074 1089 1105 1125 1145 1162 1177 1192 1209 1218 1217 1215 1213 1211 1210 1208 1209 1206 1198 1195 1193 1183 1185 1190 1196 1198 1197 1189 1181 1173 1163 1154 1143 1134 1130 1127 1123 1120 1119 1118 1114 1112 1114 1111 1105 1103 1099 1094 1096 1094 1090 1089 1086 1082 1080 1078 1076 1079 1086 1082 1074 1060 1050 1045 1057 1066 1072 1074 1071 1062 1059 1056 1051 1043 1032 1013 1003 1004 999 996 996 997 998 1004 1003 993 1005 1016 1015 1013 1011 1009 1007 1007 998 991 996 991 983 980 977 974 973 971 966 964 963 960 960 962 960 960 961 960 960 961 961 960 959 958 955 955 955 952 951 953 951 949 949 946 944 952 957 957 953 947 940 940 1 16 463 1 0.558270000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.881000000000000D+03 0.127600000000000D+04 961 965 970 977 985 993 1003 1007 1008 1009 1009 1007 1012 1017 1015 1018 1024 1029 1034 1040 1039 1040 1047 1050 1051 1049 1048 1050 1059 1069 1075 1078 1078 1064 1053 1045 1029 1012 1005 990 969 973 971 957 943 928 906 891 881 881 881 883 886 889 892 895 898 903 907 906 905 904 901 900 899 897 896 900 900 898 898 899 902 903 904 905 912 923 936 946 950 952 952 949 945 942 943 944 943 946 948 945 942 940 945 946 943 943 945 943 938 930 924 921 925 927 927 927 928 931 932 934 936 933 928 937 943 946 954 958 960 961 962 964 967 968 970 972 975 976 975 978 981 983 986 988 992 995 999 1000 1002 1006 1005 1007 1009 1011 1013 1016 1020 1026 1030 1032 1033 1036 1041 1040 1041 1046 1048 1050 1055 1056 1053 1055 1061 1075 1087 1096 1098 1101 1105 1106 1108 1113 1117 1121 1127 1125 1119 1119 1116 1111 1106 1105 1111 1121 1130 1131 1131 1129 1131 1135 1143 1145 1145 1148 1149 1149 1151 1153 1155 1159 1163 1165 1168 1170 1171 1173 1178 1183 1187 1187 1185 1181 1184 1186 1186 1191 1196 1197 1200 1204 1208 1211 1215 1217 1220 1224 1224 1228 1229 1230 1231 1234 1235 1236 1237 1239 1242 1245 1241 1240 1243 1255 1261 1245 1244 1253 1260 1259 1249 1251 1255 1253 1254 1257 1255 1256 1261 1257 1254 1260 1262 1260 1264 1267 1268 1257 1245 1242 1242 1243 1235 1237 1256 1261 1262 1263 1263 1261 1263 1264 1260 1263 1268 1269 1272 1276 1267 1252 1226 1197 1169 1156 1135 1107 1118 1130 1139 1165 1195 1202 1211 1221 1204 1185 1168 1148 1126 1107 1092 1082 1076 1068 1057 1068 1087 1059 1043 1038 1046 1057 1069 1074 1075 1079 1087 1098 1117 1137 1154 1170 1186 1203 1214 1215 1215 1214 1214 1209 1203 1205 1202 1192 1192 1192 1188 1192 1198 1200 1196 1185 1172 1160 1153 1147 1144 1139 1134 1129 1127 1125 1125 1123 1121 1118 1116 1117 1112 1107 1105 1099 1094 1096 1096 1093 1090 1087 1085 1085 1086 1085 1091 1101 1092 1081 1070 1059 1054 1068 1079 1089 1086 1082 1076 1070 1063 1053 1038 1022 1010 1004 1004 1001 1001 1007 1010 1011 1016 1016 1010 1018 1025 1022 1017 1012 1010 1008 1005 1001 997 995 990 985 982 978 975 974 972 967 966 967 965 965 966 964 962 963 962 961 962 962 963 962 961 957 957 957 955 956 958 959 959 957 956 955 964 963 957 955 951 942 942 1 17 463 1 0.558300000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.883000000000000D+03 0.128400000000000D+04 961 966 971 978 986 995 1001 1006 1010 1015 1016 1013 1015 1018 1019 1021 1025 1031 1037 1041 1042 1044 1048 1052 1055 1051 1051 1054 1064 1073 1077 1079 1080 1072 1063 1056 1038 1022 1015 1001 982 987 986 973 960 944 916 897 884 883 883 884 886 888 892 894 895 898 902 906 906 904 902 901 900 897 896 900 900 898 900 901 902 903 907 917 926 935 941 946 949 952 953 950 947 944 945 945 943 946 948 946 943 940 942 943 944 945 946 945 941 934 926 922 925 928 932 933 932 931 930 931 937 934 930 938 945 949 956 958 960 961 962 963 965 967 968 970 972 974 976 979 981 983 985 988 991 994 999 1001 1003 1007 1008 1011 1013 1013 1013 1016 1020 1026 1030 1034 1036 1038 1042 1041 1043 1047 1049 1050 1054 1057 1058 1060 1063 1067 1080 1091 1096 1101 1107 1107 1110 1118 1121 1124 1129 1130 1129 1131 1129 1122 1120 1119 1117 1123 1132 1135 1135 1132 1133 1137 1145 1148 1148 1152 1153 1152 1154 1156 1157 1161 1166 1167 1169 1173 1174 1176 1181 1185 1189 1191 1189 1185 1188 1190 1190 1195 1201 1201 1204 1207 1211 1214 1217 1220 1223 1228 1228 1230 1233 1234 1235 1235 1238 1240 1241 1241 1244 1248 1244 1243 1245 1260 1267 1247 1244 1250 1262 1264 1253 1255 1258 1257 1259 1261 1257 1258 1264 1259 1256 1267 1268 1265 1268 1270 1271 1260 1248 1245 1245 1248 1239 1240 1259 1264 1266 1268 1267 1264 1268 1270 1268 1271 1275 1278 1281 1284 1262 1239 1215 1191 1169 1153 1136 1117 1131 1144 1149 1177 1210 1215 1220 1226 1206 1187 1174 1154 1134 1122 1111 1101 1091 1081 1070 1077 1089 1058 1041 1038 1052 1068 1085 1091 1092 1096 1098 1099 1113 1129 1144 1162 1182 1202 1214 1216 1214 1212 1213 1208 1200 1203 1202 1195 1200 1204 1199 1201 1205 1196 1183 1166 1157 1151 1146 1143 1139 1140 1138 1133 1131 1129 1129 1126 1122 1119 1118 1117 1112 1108 1106 1100 1096 1101 1100 1097 1093 1091 1089 1094 1098 1100 1106 1115 1101 1091 1086 1075 1068 1081 1092 1100 1096 1087 1075 1069 1062 1048 1032 1015 1009 1006 1007 1005 1007 1020 1025 1027 1029 1030 1029 1029 1027 1024 1018 1012 1010 1007 1002 1001 999 993 988 984 981 979 978 976 974 971 970 970 970 970 969 965 964 966 964 962 962 963 964 965 964 960 960 961 958 959 963 967 968 966 961 959 972 969 954 952 949 944 945 1 18 463 1 0.558330000000000D+06 0.510801000000000D+07 0.000000000000000D+00 0.883000000000000D+03 0.128500000000000D+04 961 967 974 981 985 990 996 998 1000 1011 1015 1014 1017 1019 1021 1024 1027 1032 1037 1041 1043 1046 1051 1054 1057 1057 1057 1059 1067 1075 1077 1079 1081 1076 1071 1065 1046 1028 1021 1010 996 998 995 988 976 960 930 908 890 887 885 884 883 884 891 893 892 894 898 903 904 904 902 901 901 898 896 900 900 898 902 903 902 910 919 929 936 941 943 946 949 949 950 949 947 945 946 946 945 945 946 946 945 945 943 943 946 947 948 946 942 936 928 923 924 931 938 938 935 931 929 930 937 936 935 941 946 952 955 957 958 960 962 961 963 967 968 969 971 974 976 980 981 981 985 988 989 993 999 1001 1004 1008 1009 1013 1015 1015 1013 1015 1019 1026 1030 1033 1038 1041 1043 1043 1045 1048 1049 1050 1054 1059 1063 1065 1066 1066 1073 1081 1086 1096 1107 1107 1112 1122 1126 1129 1132 1134 1136 1138 1139 1136 1134 1131 1124 1127 1134 1137 1137 1135 1137 1140 1146 1150 1152 1156 1158 1157 1158 1159 1160 1163 1166 1167 1170 1176 1176 1178 1185 1189 1193 1196 1194 1188 1193 1196 1195 1200 1206 1206 1208 1210 1214 1217 1219 1223 1227 1231 1231 1233 1236 1237 1238 1239 1241 1244 1245 1245 1248 1251 1247 1247 1249 1265 1272 1251 1246 1250 1265 1268 1258 1260 1262 1262 1265 1267 1260 1260 1270 1267 1265 1275 1275 1270 1272 1274 1274 1263 1252 1250 1251 1254 1244 1245 1261 1266 1268 1271 1271 1268 1273 1275 1275 1277 1281 1283 1284 1285 1255 1229 1210 1187 1166 1157 1144 1128 1146 1159 1159 1192 1228 1227 1226 1226 1206 1188 1181 1162 1142 1131 1116 1097 1097 1096 1087 1092 1098 1073 1054 1041 1051 1068 1095 1106 1110 1115 1115 1109 1118 1130 1144 1161 1180 1203 1216 1219 1213 1209 1215 1214 1210 1217 1215 1204 1209 1214 1209 1205 1200 1185 1171 1158 1154 1152 1153 1148 1141 1140 1139 1135 1132 1130 1128 1125 1122 1120 1118 1116 1112 1109 1109 1104 1102 1104 1103 1100 1094 1092 1094 1103 1111 1113 1116 1118 1109 1103 1102 1093 1088 1097 1095 1087 1087 1079 1062 1053 1045 1034 1024 1015 1011 1009 1008 1010 1016 1034 1041 1042 1041 1039 1036 1033 1029 1024 1019 1015 1011 1008 1005 1003 1000 995 989 984 982 981 981 980 977 974 973 973 973 972 972 968 966 967 966 965 964 965 966 967 966 962 963 964 961 963 968 969 969 971 968 965 968 966 960 956 952 951 952 mayavi-4.1.0/mayavi/tests/data/cellsnd.ascii.inp0000755000175100001440000000222111674464502022560 0ustar ischnellusers00000000000000# AVS UCD File # foo # num_nodes num_cells num_node_data num_cell_data num_model_data # nodal coordinates # cell type, topology 13 12 1 2 0 0 -1.000000 -1.000000 0.000000 1 1.000000 -1.000000 0.000000 2 1.000000 1.000000 0.000000 3 -1.000000 1.000000 0.000000 4 0.000000 -2.000000 0.000000 5 2.000000 0.000000 0.000000 6 0.000000 2.000000 0.000000 7 -2.000000 0.000000 0.000000 8 0.000000 -1.000000 0.000000 9 1.000000 0.000000 0.000000 10 0.000000 1.000000 0.000000 11 -1.000000 0.000000 0.000000 12 0.000000 0.000000 0.000000 0 1 tri 0 4 8 1 1 tri 8 4 1 2 2 tri 1 5 9 3 2 tri 9 5 2 4 3 tri 10 2 6 5 3 tri 10 6 3 6 4 tri 7 11 3 7 4 tri 0 11 7 8 1 quad 0 8 12 11 9 1 quad 8 1 9 12 10 1 quad 12 9 2 10 11 1 quad 12 10 3 11 1 1 temperature, F 0 6.000000 1 5.000000 2 6.666667 3 8.333333 4 1.500000 5 3.500000 6 5.500000 7 7.500000 8 5.500000 9 7.000000 10 8.500000 11 9.000000 12 10.500000 2 1 1 temperature, F pressure, p 0 1.000000 12.000000 1 2.000000 11.000000 2 3.000000 10.000000 3 4.000000 9.000000 4 5.000000 8.000000 5 6.000000 7.000000 6 7.000000 6.000000 7 8.000000 5.000000 8 9.000000 4.000000 9 10.000000 3.000000 10 11.000000 2.000000 11 12.000000 1.000000 mayavi-4.1.0/mayavi/tests/data/cube.vti0000644000175100001440000000151211674464502020776 0ustar ischnellusers00000000000000 _AQAAAACAAADYAAAA4wAAAA==eJwB2AAn/+csB27h5+M/Ji7JB+7D1z+Mpu8gESjQP0InK3Xy0+4/0KxaRoubzT98W7FoyO3CP3gIVUg1itE/UCjVmz4CrT/cFP5SugzsP64eRz5L0eQ/CinCD/aN3j+f28J7Y1XlPyZ226SEmeg/sMh7gnmUyz8qMwMbGivhP2kU+d4Nj+8/DOCLzY7Q7z/JfhBxSqvkP3/KeOeCO+E/dANDe4HjwT/JaYA/e3PrP6ZcB6sWmek/Xv//COTH3T9GpEN0vFLdP6cZS/1N+u0/+sPGNXE92D+4cUIBwirUPw3Ya3I= mayavi-4.1.0/mayavi/tests/data/shuttle.obj0000644000175100001440000003645211674464502021533 0ustar ischnellusers00000000000000# Viewpoint Datalabs International, Inc. Copyright 1996 mtllib ./vp.mtl g v 3.070224 -0.119728 0.996443 v 5.942016 -0.012019 4.157199 v 6.614015 -0.063428 4.157199 v 5.759114 0.000000 1.664500 v 3.070224 -0.449143 0.929434 v 5.000295 -0.539011 1.315104 v 3.070224 -0.604752 0.872464 v 3.070224 -0.866525 0.730690 v 3.070224 -0.959007 0.650256 v 3.070224 -1.053631 0.163277 v 2.983248 -1.080021 -0.880639 v 6.130317 -1.100022 -1.106943 v 3.739287 -4.334102 -0.876958 v 4.400283 -4.682100 -0.952940 v 3.038248 -4.334102 -0.811319 v 3.180259 -4.550090 -0.921939 v 2.700250 -4.334102 -0.947940 v 0.840214 -2.480049 -1.050312 v 1.208789 -1.060728 0.203820 v 1.208789 -1.054148 0.411073 v 1.208789 -0.958092 0.610367 v 1.208789 -0.875165 0.685964 v 1.208789 -0.621528 0.854704 v 1.208789 -0.467365 0.922276 v -4.649089 -1.039587 0.209476 v -4.649345 -0.922345 0.432259 v -4.649708 -0.652575 0.753550 v -4.999902 -1.012545 0.094530 v -4.999240 -0.870266 0.347384 v -4.999321 -0.802315 0.416133 v -4.906714 -0.620194 0.686502 v -4.999759 -0.491153 0.805206 v -5.568033 -0.119200 0.568687 v -5.349121 -0.814175 0.247113 v -5.348800 -0.938377 -0.030175 v -6.499984 -0.676000 -0.433500 v -6.499984 -0.610000 -0.164800 v -6.499984 -0.240000 0.109600 v -7.649984 0.000000 -0.620000 v 1.209237 -1.080021 -1.321617 v 3.070224 0.119728 0.996443 v 3.093016 0.040804 1.276300 v 6.614015 0.063428 4.157199 v 3.070224 0.449143 0.929434 v 5.000295 0.539011 1.315104 v 3.070224 0.604752 0.872464 v 3.070224 0.866525 0.730690 v 5.000295 1.149023 1.260104 v 3.070224 0.959007 0.650256 v 3.070224 1.053627 0.449897 v 5.000295 1.428028 0.442095 v 3.070224 1.053631 0.163277 v 2.983248 1.080021 -0.880639 v 5.000295 1.302926 -1.259946 v 3.739287 4.334102 -0.876958 v 4.400283 4.682100 -0.952940 v 3.038248 4.334102 -0.811319 v 3.180259 4.550090 -0.921939 v 1.209237 1.080021 -0.636414 v 2.700250 4.334102 -0.947940 v 0.169216 1.990039 -1.063281 v 1.208789 1.060728 0.203820 v 1.208789 1.054148 0.411073 v 1.208789 0.958092 0.610367 v 1.208789 0.875165 0.685964 v 1.208789 0.621528 0.854704 v 1.208789 0.467365 0.922276 v -4.649089 1.039587 0.209476 v -4.649345 0.922345 0.432259 v -4.649708 0.652575 0.753550 v -4.649856 0.514670 0.885149 v -4.649964 0.160748 0.994500 v -4.999902 1.012545 0.094530 v -4.999240 0.870266 0.347384 v -4.999321 0.802315 0.416133 v -4.999759 0.491153 0.805206 v -4.999948 0.160720 0.980689 v -5.299752 0.147914 0.811038 v -5.349121 0.814175 0.247113 v -5.348800 0.938377 -0.030175 v -6.499984 0.676000 -0.433500 v -6.499931 0.693962 -0.748535 v -6.499984 0.610000 -0.164800 v -6.499984 0.523000 -0.048800 v -6.499984 0.240000 0.109600 v 1.209237 1.080021 -1.321617 v -5.568033 0.119200 0.568687 v -5.299752 -0.147914 0.811038 v -4.999948 -0.160720 0.980689 v -4.649964 -0.160748 0.994500 v 1.208789 -0.130179 0.996071 v 1.208789 0.130179 0.996071 v 3.093016 -0.040804 1.276300 v 5.942016 0.012019 4.157199 v 7.043714 0.000000 4.157199 v 4.998233 -0.130896 1.193100 v 5.171283 -1.310384 -1.055942 v 6.130317 1.100022 -1.106943 v 2.983248 -1.080021 -1.351649 v 2.983248 1.080021 -1.351649 v -6.499931 -0.693962 -0.748535 v -4.999902 -1.000020 -0.943979 v 0.169216 -1.990039 -1.063281 v 5.000295 -1.510030 0.750093 v 5.000295 -0.874017 1.399122 v 5.000295 -1.149023 1.260104 v 5.000295 0.874017 1.399122 v -7.074984 -0.304058 -0.264426 v -7.074984 0.139529 -0.169387 v -7.074984 0.304058 -0.264426 v -7.074957 0.403450 -0.684268 v -7.074984 0.393008 -0.495246 v -7.074984 0.354637 -0.334026 v -7.074984 0.057454 -0.155083 v -7.074984 -0.354637 -0.334026 v -7.074984 -0.393008 -0.495246 v -7.074957 -0.403450 -0.684268 v -7.074984 -0.139529 -0.169387 v -7.074984 -0.057454 -0.155083 v 5.257180 -0.244260 -0.448877 v 5.275361 -0.389797 -0.446328 v 5.534085 -0.255527 -0.410058 v 5.858724 -0.171973 -0.364548 v 6.246687 -0.127423 -0.310161 v 6.245811 -0.209802 -0.310283 v 5.957494 -0.242908 -0.350702 v 5.684797 -0.367023 -0.388930 v 5.030259 -0.310424 -0.039389 v 5.218888 -0.403501 -0.175729 v 5.254566 -0.476272 -0.297997 v 5.497149 -0.409135 -0.146573 v 5.811742 -0.367356 -0.029404 v 6.194348 -0.345081 0.063191 v 6.203377 -0.386271 -0.007583 v 5.919040 -0.402825 -0.076394 v 5.661265 -0.464884 -0.221067 v 5.030257 -0.815056 -0.039376 v 5.218887 -0.721987 -0.175721 v 5.254566 -0.649223 -0.297993 v 5.497147 -0.716354 -0.146565 v 5.811740 -0.758129 -0.029394 v 6.194347 -0.780403 0.063202 v 6.203376 -0.739216 -0.007574 v 5.919039 -0.722663 -0.076386 v 5.661264 -0.660610 -0.221062 v 5.533661 -0.562752 -0.410117 v 5.257178 -0.881243 -0.448860 v 5.275359 -0.735706 -0.446319 v 5.534083 -0.869976 -0.410042 v 5.858722 -0.953530 -0.364528 v 6.246684 -0.998080 -0.310138 v 6.245809 -0.915701 -0.310265 v 5.957492 -0.882595 -0.350685 v 5.684796 -0.758480 -0.388920 v 5.151601 -0.815102 -0.904963 v 5.295470 -0.722016 -0.722016 v 5.296154 -0.649239 -0.594654 v 5.571022 -0.716382 -0.673535 v 5.905705 -0.758165 -0.699682 v 6.299025 -0.780442 -0.683500 v 6.288245 -0.739248 -0.612975 v 5.995947 -0.722692 -0.625000 v 5.708329 -0.660628 -0.556788 v 5.295474 -0.403530 -0.722041 v 5.296155 -0.476288 -0.594668 v 5.571025 -0.409163 -0.673559 v 5.905710 -0.367392 -0.699712 v 6.299029 -0.345120 -0.683534 v 6.288249 -0.386303 -0.613002 v 5.995951 -0.402854 -0.625025 v 5.708331 -0.464902 -0.556803 v 5.218888 0.403501 -0.175729 v 5.257180 0.244260 -0.448877 v 5.254566 0.476272 -0.297997 v 5.275361 0.389797 -0.446328 v 5.497149 0.409135 -0.146573 v 5.534085 0.255527 -0.410058 v 5.811742 0.367356 -0.029404 v 5.858724 0.171973 -0.364548 v 6.194348 0.345081 0.063191 v 6.246687 0.127423 -0.310161 v 6.203377 0.386271 -0.007583 v 6.245811 0.209802 -0.310283 v 5.919040 0.402825 -0.076394 v 5.957494 0.242908 -0.350702 v 5.661265 0.464884 -0.221067 v 5.684797 0.367023 -0.388930 v 5.218887 0.721987 -0.175721 v 5.254566 0.649223 -0.297993 v 5.497147 0.716354 -0.146565 v 5.811740 0.758129 -0.029394 v 6.194347 0.780403 0.063202 v 6.203376 0.739216 -0.007574 v 5.919039 0.722663 -0.076386 v 5.661264 0.660610 -0.221062 v 5.257178 0.881243 -0.448860 v 5.275359 0.735706 -0.446319 v 5.534083 0.869976 -0.410042 v 5.858722 0.953530 -0.364528 v 6.246684 0.998080 -0.310138 v 6.245809 0.915701 -0.310265 v 5.957492 0.882595 -0.350685 v 5.684796 0.758480 -0.388920 v 5.533661 0.562752 -0.410117 v 5.295470 0.722016 -0.722016 v 5.296154 0.649239 -0.594654 v 5.571022 0.716382 -0.673535 v 5.905705 0.758165 -0.699682 v 6.299025 0.780442 -0.683500 v 6.288245 0.739248 -0.612975 v 5.995947 0.722692 -0.625000 v 5.708329 0.660628 -0.556788 v 5.295474 0.403530 -0.722041 v 5.296155 0.476288 -0.594668 v 5.571025 0.409163 -0.673559 v 5.905710 0.367392 -0.699712 v 6.299029 0.345120 -0.683534 v 6.288249 0.386303 -0.613002 v 5.995951 0.402854 -0.625025 v 5.708331 0.464902 -0.556803 v 5.165639 -0.318491 0.637328 v 5.166101 -0.159250 0.913146 v 4.998497 -0.252327 1.074635 v 5.183997 -0.172954 0.637297 v 5.184248 -0.086480 0.787078 v 5.445252 -0.307224 0.636859 v 5.445698 -0.153617 0.902920 v 5.773065 -0.390779 0.636310 v 5.773632 -0.195395 0.974730 v 6.164821 -0.435329 0.635652 v 6.165453 -0.217671 1.012654 v 6.163937 -0.352950 0.635654 v 6.164450 -0.176480 0.941314 v 5.872800 -0.319843 0.636142 v 5.873264 -0.159926 0.913131 v 5.597437 -0.195729 0.636604 v 5.597722 -0.097867 0.806108 v 5.444824 0.000000 0.636860 v 5.166102 0.159236 0.913155 v 5.184248 0.086472 0.787083 v 5.445698 0.153603 0.902928 v 5.773632 0.195378 0.974740 v 6.165453 0.217651 1.012665 v 6.164450 0.176464 0.941323 v 5.873265 0.159912 0.913140 v 5.597722 0.097858 0.806113 v 5.165639 0.318491 0.637345 v 4.997765 0.504639 0.637636 v 5.183997 0.172954 0.637307 v 5.445252 0.307224 0.636875 v 5.773065 0.390779 0.636330 v 6.164821 0.435329 0.635675 v 6.163937 0.352950 0.635673 v 5.872800 0.319843 0.636159 v 5.597437 0.195729 0.636614 v 5.165176 0.159265 0.361518 v 4.997031 0.252350 0.200598 v 5.183746 0.086488 0.487521 v 5.444806 0.153631 0.370806 v 5.772497 0.195413 0.297899 v 6.164188 0.217691 0.258662 v 6.163424 0.176496 0.330003 v 5.872335 0.159941 0.359162 v 5.597153 0.097876 0.467105 v 5.165176 -0.159221 0.361493 v 4.997031 -0.252281 0.200558 v 5.183746 -0.086464 0.487507 v 5.444806 -0.153589 0.370782 v 5.772497 -0.195360 0.297868 v 6.164188 -0.217631 0.258628 v 6.163424 -0.176448 0.329975 v 5.872335 -0.159897 0.359136 v 5.597153 -0.097850 0.467090 v 5.090927 -1.067391 -0.472156 v 5.171283 1.310384 -1.055942 v 5.151606 0.310470 -0.905003 v 5.151606 -0.310470 -0.905003 v 5.030257 0.815056 -0.039376 v 5.030259 0.310424 -0.039389 v 5.090930 -0.058113 -0.472183 v 5.090930 0.058113 -0.472183 v 5.000295 -1.210004 0.173074 v 5.000295 1.210004 0.173074 v 5.000295 -1.428028 0.442095 v 4.997764 -0.504639 0.637610 v 4.998497 0.252304 1.074648 v 4.998233 0.130896 1.193100 v 5.000295 1.510030 0.750093 v 5.151601 0.815102 -0.904963 v 5.090927 1.067391 -0.472156 v 3.070224 -1.053627 0.449897 v -5.349205 0.737229 0.323968 v -5.349205 -0.737229 0.323968 v -5.349476 -0.470935 0.566062 v -6.499984 -0.098825 0.133439 v -6.499984 0.098825 0.133439 v -6.499984 -0.523000 -0.048800 v -5.349476 0.470935 0.566062 v -4.999902 1.000020 -0.943979 v 0.840214 2.480049 -1.050312 v 1.209237 -1.080021 -0.636414 v 3.804262 4.682100 -0.938960 v 5.000295 -1.302926 -1.259946 v 3.804262 -4.682100 -0.938960 v -4.649856 -0.514670 0.885149 v -4.999492 0.681710 0.569242 v -4.649417 0.860391 0.497003 v -4.906714 0.620194 0.686502 v -4.649417 -0.860391 0.497003 v -4.999492 -0.681710 0.569242 # 310 vertices # 0 vertex parms # 0 texture vertices # 0 normals g windows usemtl glass s 1 f 310 32 294 f 76 308 306 f 294 88 33 f 310 31 32 f 88 294 32 f 87 33 88 78 f 87 78 298 f 298 76 306 f 298 78 76 g tail usemtl bone s 4 f 95 3 96 4 f 4 287 43 95 f 94 2 3 43 f 3 2 93 96 f 41 1 93 42 f 41 42 287 f 43 3 95 f 287 42 94 43 f 42 93 2 94 f 96 93 1 g rearbody s 6 f 275 98 54 f 96 223 286 287 f 97 277 155 f 276 281 280 277 f 276 275 289 f 283 282 128 279 f 283 290 275 f 257 51 248 f 282 303 97 f 96 6 106 f 303 12 97 f 104 285 223 f 97 155 274 f 284 282 266 f 286 288 287 f 137 128 282 f 283 279 278 f 248 288 286 f 6 105 106 f 275 54 283 f 284 266 285 f 96 287 4 f 284 285 104 f 248 51 288 f 283 278 290 f 274 137 282 f 289 275 290 f 97 12 98 275 f 48 107 45 f 96 106 104 f 282 283 257 266 f 97 275 276 277 f 104 223 96 f 257 283 51 f 97 274 282 f 128 280 281 279 f 287 288 48 f 287 48 45 g body s 7 f 309 31 310 f 294 33 295 f 108 118 39 f 80 79 74 73 f 49 47 48 f 5 1 91 24 f 10 291 20 19 f 294 295 38 f 78 77 76 f 81 82 111 112 f 65 66 46 47 f 30 309 310 f 5 105 6 f 30 29 26 309 f 68 62 59 299 f 78 88 89 77 f 118 38 295 119 f 83 81 112 113 f 64 65 47 49 f 35 37 36 f 23 8 7 f 24 91 90 305 f 62 52 53 59 f 296 85 109 114 f 79 292 75 74 f 50 49 288 f 22 23 27 f 282 10 11 303 f 293 294 297 f 71 72 92 67 f 112 39 113 f 310 294 293 f 305 90 89 f 308 70 307 f 296 87 298 f 114 39 119 f 71 77 72 f 45 107 44 f 8 23 22 f 7 5 24 23 f 287 44 41 f 307 69 74 75 f 92 91 1 41 f 63 62 68 f 28 29 34 35 f 105 7 8 106 f 32 89 88 f 49 48 288 f 82 81 299 f 115 37 297 108 f 113 39 110 f 73 74 69 68 f 29 30 293 34 f 291 104 9 f 22 27 309 f 54 53 52 283 f 83 79 80 f 83 80 81 f 48 47 46 107 f 25 20 21 26 f 301 11 10 19 f 39 115 108 f 306 307 75 f 110 39 109 f 292 298 306 f 306 308 307 f 70 66 65 f 294 38 297 f 5 6 96 f 85 84 110 109 f 62 63 50 52 f 102 25 28 f 9 106 8 f 310 293 30 f 70 71 66 f 77 89 90 72 f 66 71 67 f 297 37 34 293 f 106 9 104 f 25 19 20 f 44 107 46 f 85 296 298 f 117 101 36 116 f 111 39 112 f 307 70 65 f 35 34 37 f 23 305 27 f 102 301 19 25 f 50 288 51 f 80 73 299 f 84 298 292 f 49 50 63 64 f 32 305 89 f 1 5 96 f 32 31 27 305 f 66 67 44 46 f 296 295 33 87 f 291 10 282 f 81 80 299 f 309 27 31 f 84 85 298 f 116 36 37 115 f 292 79 83 84 f 283 52 51 f 309 26 21 22 f 284 291 282 f 102 36 101 f 65 64 69 307 f 295 296 114 119 f 73 68 299 f 39 116 115 f 105 5 7 f 23 24 305 f 39 117 116 f 77 71 76 f 109 39 114 f 297 38 118 108 f 75 292 306 f 39 118 119 f 21 20 291 9 f 9 8 22 21 f 287 45 44 f 71 70 308 76 f 84 83 113 110 f 67 92 41 44 f 25 26 29 28 f 104 291 284 f 102 28 35 f 69 64 63 68 f 72 90 91 92 f 52 50 51 f 102 35 36 g wings s 5 f 16 15 17 f 304 15 16 f 300 57 60 f 14 13 304 f 59 53 55 57 f 60 57 58 f 18 301 103 f 300 59 57 f 304 13 15 f 56 55 53 54 f 15 13 11 301 f 61 59 300 f 57 55 302 f 103 301 102 f 17 15 301 f 303 11 13 14 f 58 57 302 f 302 55 56 f 17 301 18 f 299 59 61 g tiles usemtl fldkdkgrey s 3 f 302 56 54 f 18 103 40 f 16 17 99 f 86 61 300 f 99 304 16 f 303 14 304 f 99 303 304 f 17 18 99 f 302 54 100 f 58 302 100 f 100 86 300 f 18 40 99 f 100 60 58 f 100 300 60 f 101 117 111 82 f 102 101 82 299 f 117 39 111 f 99 100 54 303 f 303 54 98 12 f 86 100 99 40 f 40 103 61 86 f 299 61 103 102 g enginside usemtl redbrick s 9 f 238 255 246 f 194 202 201 193 f 153 162 163 154 f 144 153 154 145 f 184 194 193 182 f 238 246 237 f 272 234 232 271 f 236 237 235 234 f 204 195 186 f 134 143 144 135 f 143 152 153 144 f 204 203 195 f 237 246 245 235 f 273 236 234 272 f 238 237 236 f 185 184 182 183 f 135 144 145 136 f 154 163 146 f 195 203 202 194 f 235 245 244 233 f 264 273 272 263 f 219 185 183 218 f 187 186 184 185 f 136 145 146 f 161 169 170 162 f 204 220 212 f 255 264 263 254 f 234 235 233 232 f 186 195 194 184 f 145 154 146 f 152 161 162 153 f 204 212 203 f 246 255 254 245 f 238 236 273 f 204 187 220 f 169 125 126 170 f 126 135 136 127 f 163 171 146 f 203 212 211 202 f 245 254 253 244 f 238 273 264 f 211 219 218 210 f 170 126 127 171 f 127 136 146 f 162 170 171 163 f 202 211 210 201 f 238 264 255 f 254 263 262 253 f 212 220 219 211 f 171 127 146 f 125 134 135 126 f 204 186 187 f 220 187 185 219 f 263 272 271 262 g engout usemtl black f 251 260 259 250 f 209 217 216 208 f 157 165 166 158 f 132 141 142 133 f 179 178 176 177 f 215 177 175 214 f 270 230 228 269 f 227 241 240 225 f 191 199 198 190 f 150 159 160 151 f 131 140 141 132 f 177 176 174 175 f 230 231 229 228 f 269 228 226 268 f 229 242 241 227 f 192 200 199 191 f 139 148 149 140 f 130 139 140 131 f 180 192 191 178 f 228 229 227 226 f 268 226 224 267 f 231 243 242 229 f 176 190 189 174 f 140 149 150 141 f 149 158 159 150 f 190 198 197 189 f 243 252 251 242 f 259 268 267 258 f 216 179 177 215 f 181 180 178 179 f 121 130 131 122 f 167 123 124 168 f 208 216 215 207 f 250 259 258 249 f 252 261 260 251 f 198 207 206 197 f 158 166 167 159 f 123 132 133 124 f 166 122 123 167 f 207 215 214 206 f 261 270 269 260 f 241 250 249 240 f 199 208 207 198 f 159 167 168 160 f 122 131 132 123 f 165 121 122 166 f 217 181 179 216 f 260 269 268 259 f 242 251 250 241 f 200 209 208 199 f 148 157 158 149 f 141 150 151 142 f 178 191 190 176 f 226 227 225 224 g engmount usemtl brass s 11 f 225 240 239 222 f 164 120 121 165 f 128 137 138 129 f 196 205 289 290 f 265 221 285 266 f 206 214 213 205 f 138 147 148 139 f 174 189 188 172 f 249 258 256 247 f 221 222 223 285 f 155 277 164 156 f 274 155 156 147 f 213 173 281 276 f 258 267 265 256 f 189 197 196 188 f 120 129 130 121 f 173 172 279 281 f 239 247 248 286 f 205 213 276 289 f 137 274 147 138 f 156 164 165 157 f 224 225 222 221 f 247 256 257 248 f 172 188 278 279 f 280 128 129 120 f 188 196 290 278 f 256 265 266 257 f 214 175 173 213 f 147 156 157 148 f 175 174 172 173 f 240 249 247 239 f 222 239 286 223 f 277 280 120 164 f 129 138 139 130 f 197 206 205 196 f 267 224 221 265 g engrim usemtl dkdkgrey s off f 233 244 243 231 f 124 133 134 125 f 262 271 270 261 f 142 151 152 143 f 253 262 261 252 f 151 160 161 152 f 244 253 252 243 f 160 168 169 161 f 201 210 209 200 f 271 232 230 270 f 133 142 143 134 f 232 233 231 230 f 183 182 180 181 f 218 183 181 217 f 182 193 192 180 f 210 218 217 209 f 193 201 200 192 f 168 124 125 169 # 393 elements mayavi-4.1.0/mayavi/tests/data/edgeFaceElem.exii0000644000175100001440000002220411674464502022503 0ustar ischnellusers00000000000000CDF 3 len_string!len_lineQfour time_stepnum_dim num_nodes num_elemnum_edgenum_face num_el_blk num_ed_blk num_fa_blk num_node_sets num_edge_sets num_face_sets num_side_sets num_elem_sets num_node_maps num_edge_maps num_face_maps num_elem_mapsnum_ed_in_blk1num_nod_per_ed1num_att_in_eblk1num_fa_in_fblk1num_nod_per_fa1num_att_in_fblk1num_fa_in_fblk2num_nod_per_fa2num_att_in_fblk2num_fa_in_fblk3num_nod_per_fa3num_att_in_fblk3num_el_in_blk1num_nod_per_el1num_edg_per_el1 num_fac_per_el1num_att_in_blk1 num_nod_ns1 num_edge_es1 num_df_es1 num_face_fs1 num_side_ss1 num_ele_els1 num_ele_els2 num_glo_var num_nod_var num_edge_var num_face_var num_elem_var num_fset_var  api_version@pversion@C33floating_point_word_size file_sizetitleCreateEdgeFace Test T time_whole 4 eb_status eb_prop1 nameID ed_status ed_prop1 nameID fa_status  fa_prop1 nameID  ns_status ns_prop1 nameID es_status es_prop1 nameID fs_statusfs_prop1 nameID ss_statusss_prop1 nameID els_status els_prop1 nameIDnm_prop1 nameID edm_prop1 nameID fam_prop1 nameIDem_prop1 nameIDcoord  coor_namesd eb_names $ed_names $fa_names dns_names $0es_names $Tfs_names$xss_names$ els_namesD nmap_names$ edmap_names$( famap_names$L emap_names$peattrb1 eattrib_name1$4ebconn1  elem_type STRAIGHT2Xfattrb1 fattrib_name1$fbconn1  elem_typeQUAD4 ,fattrb2L fattrib_name2$Tfbconn2  elem_typeQUAD4xfattrb3 @ fattrib_name3 $fbconn3  elem_typeQUAD4attrib1!% l attrib_name1%Dconnect1!"  elem_typeHEX8@edgconn1!#`facconn1!$0p node_map10 edge_map1P face_map1, elem_map1Lnode_ns1& Tedge_es1'`ornt_es1'x dist_fact_es1(0face_fs1)ornt_fs1)elem_ss1*side_ss1* elem_els1+ elem_els2, vals_glo_var- < name_glo_var-D vals_nod_var.` L name_nod_var.$D name_edge_var/Dhvals_edge_var1eb1 vals_edge_var2eb1!L edge_var_tab / name_face_var0$vals_face_var1fb1!vals_face_var1fb3@! face_var_tab 0  name_elem_var1$vals_elem_var1eb1!"< elem_var_tab 1  name_fset_var2$ vals_fset_var1fs1)"L fset_var_tab2 0dXx@l, @@@@??@@@@??@@@@XYZEli WALLACHAldo GIUFFRELivio LORENZONClaudio SCARCHILLIJohn BARTHAEnnio MORRICONERada RASSIMOVEnzo PETITOLuciano VINCENZONIClint EASTWOODLee VAN CLEEFLuigi PISTILLIAntonio CASALESandro SCARCHILLIBenito STEFANELLI?@@@@@&@*@1@3@7@=@?@B@D@E@G@J@M@N@PSergio LEONE    @Q@R@GOOD @SBAD@T@V@@X@@Y@@Y@Z@[@@\@UGLY     @_@Y@@a @a`SPAGHETTIWESTERN        @@???? CALIBERGUNPOWDERRHOGAMMA1GAMMA2PHIEPSTRNPHI0?@B@B??陙?ٙ?333333??陙??333333?ffffff?ٙ?333333@4@3@2@1@0@.@,@*@(@&@$@"@ @@@@@@?????@@????@@@@??@@@@ @ ?@@@E@E?ffffff??333333??ə?ffffff??陙?ə?333333?@5@4@3@2@1@0@.@,@*@(@&@$@"@ @@@@@@????@@????@@@@@?@@@ @"@;mayavi-4.1.0/mayavi/tests/data/foot.raw0000644000175100001440000020000011674464502021007 0ustar ischnellusers00000000000000         %!#,%"*'! "!##!+"($(!"  ))# *                &"+(&"")-/ '3*).%%(),+"!3%!#0%+! +"#,0  &"                 "%%&&(++)) $(%/$+,!$+2,-( .+!*'))#'+' #  "41%+"!'!                    &$!--1&(),53'3"&*+(%6,#**23.),./*4; !.$,3('!#%  5,"*(0-                             +9),0,4(.).0*/3&34,0-#!1--.#&0,1.5.2+",+**'&)-  $3/(& !-%                       (.&)$.)*3'+.'''198"(4-.6**)0..)%*'/445"6(/2!''"*.+  #-/32& #"                  **$1$,/&+'$&*53$%/7%35*--*0(.2/.+,'6363/1&//$--"/6% %4(++")))"                     !-.(21&#*-5),./,***'&-'--(*-'/0(1/*+227$30./+.-+'*-& (=3/(++/0                     %,0(,,&'.69'054310((.-'7)--&,05//,%,.1'+-'1,$/1+0+.) )752-4 -/$!                             &-!%(15"$;<.#04&,.48%&2)'8*05'/(*+(+).2-'6)%15/04(/22  ,<4(%+&!$                        &. +)-/3487&,+/.$,9&'-2)0-,7.*./$&5,'2(1)#6610/50,+( +;8-%#!"',                      (!!6-$1:142%(-96/&-*(4*14*13-66,&*+)0!(#+72*058:-1- %30/2./!9'                             ()6331,57-,),/+%*-($!0/-,4/#$6+15,(0,1060).3.)84*;&*;28;)                        "$#4+(2+&0/(1&)8-+*(,*)+1-*3-$'0//63($0,*,,23.15+12, 1<$-<0.B0$                       #)*2+'&+2/1..!7+!2-/9/4-0*'4+*1)252."&+0,,,52160;1 02".5$3?'                    ""-!!4972''1-5,&$-.!/83-(<1-3(1+,71463-%.-)(+1---40)1.  3,'246< !                     )$ ,E/434)/*%*7- %%/7/!)5<3284-'3500:4-091,.6:/+36-)10  7/+2084%                     +?=# &4-7&$,,2"#(#(*(#,38/,5/.#'4'/0015-2478042/*05/51 $B7%%'3(                         $4>'!76+1-<2 02(&'-<1562.31!.1-832122:2+0,..1.3:79( ,A7$ &-'%                      ?7()057+ &6/*)!!$$:>/7531<&#>579,23.2634334**79..6 :B0"#$.,&#                  ,@/#0507/#.-'-#.$'-><,8958=-G/7=)(4/*/78/,00.,-4/( @?(%0# #*)                  !6<432293)5*04%0# !#.)82C42908>#/?,6B8/2.36*+30,+21.=6$ (G0-: &!2%#                    &+1<=?;246)(/,3$$#1*@-;2278760=/2172753.(2413&0B;55" /H*/8%+"                    +&'2962222- !"*, $)&#:3# *=4:01.407"9C561<.77151.+.7(+<3,3  5A'4>) +,                     ! %1!.206277/0.+ !*+&+.)(0;7 $825;750326!2256.6/7.1<42417-,9:3$ @<"%08( && "                       , <: ):2,><7) 20/336.1<70).2' %(A(*9;+*585(1-5=51.9861,01.04;9:7% "@*!3.89 $#(                 &2925=?//8-$!+3%/>;:32<83/)(#3A(#3;31;?3&42/54;19?1*00*264999/ !4%-8477 (                     '& 6M1&:94,53*"&95+97-;=3;;:.$$77113883/?8(=>3357-32.-16./60130' /#/:82+"#8&                      ,,.?7*6<0+-@9-0!$- -*(=:<<48%-',2545755.;50=6044312(2/*96234834,%1!96--2( #                           > %@3%9<5114"#( !#53681,,*34(-:;@:<898/<:29:504.04-*.79.(   !"576"-9.07=33;3734;974=:842552042*(2=<11 *$-6502?*                  #"%/&;A!&<<9-  #)').1$68,8:9;?<-626916=<790332312865994/. "$*;62559' %              )+-5>D+%12  !  .)#7043<40':55==;9:,=@27A=51481-/73,.3989/ '%-855207' +*                         !27=?E>%, #6%C3.A=44,5-3>=<6:27329>42,/=)1+"                 *7-CTME.  #! %5%!7&-:=78/17958218?93A;1:6/==67167,26>;,  ;/%54=;.6B')'(                  %37P[F/%(*" '*( & %>C0+3587=8;:269>74% '#&"# , 8G)7=435A9:;55>;:641<57>588641890&.D1079A52764-                   (,/27A:=:3-5<7. +$ 48(595:4764<,%                  #4.9MQD$#;M6  #( #-$(1)C>2415:4.2@:,;!%47" /&?/*9/7:138;! ,                    /0-8E>B*    'QQ<  ' '(63:>94:?:70576?7422=7/D< #2<.+!2>%4=0=;/397# "1                 %/6$;O?   ,MS3 $% *#% ',327:7><8766;?88;<822=( *55 ;33&@F689:611%!0$                               (3,8B. &' 7RG #)#',-$3' "-'0=6<67?9?A=:7;?;34A!61+ &: 7&,A=;23EA;;%$&!                     #" /:9'/#FX'  (, -/&!%6*%9% $(+14@984/>;5>82:<5=?3:C?:;>?910F6:D9'8 7+><-68C>00: 5+"!                     (#%3/' ", 2*"C:D@6=78A924D*#E8* 3.;(#A9305><9;>7=-                   %-+*'$  !2.3$ +E!' (G0"<#.: "$$!.@C?FB/;?:927==%0;4' 2" #-#/A<71?>;=B? **                       (%$  $B1).# )$$8%  +/$+"$9?2!1>?;>F33@BD30@9%6=4 &.  #%"@$'                       #'!& "%$<* %'1140.#++I2 !(2<:3@20<=I?4C3(?E/2,#.&IA-B@9<>18@9                      &*  "#(" !1# )-9>)% #"."'6@:>36=;FD9>50:;$(0)+ O>2?:9>C9:8?                    (    (9!8+ +,8G>)##!+'%8."$!)AC942:?=8;22:9* 3+7&$1J;84<<9B==70                         !(!! # .1"3'*4 %2!#+4..%$2$+2! >:291@E98?'0G: 240(%AD8:8G=5=;=96"                     #!#" "03)  &6'($3&#" ./# #%"/4-5;DB>B='>G0  A%'%$EB;=8B@:<@>5#E-                 '  "! +*())   '3. %&*'4)*% 2/*-L3!>63=@:2E:( -;%&#&BC<;;AE?8>=, "               ,0  !A&4&$! &.!!$%$+) $I=!=.2=?19C2  9)&&%%AA9:EAC@6=:$                      1+""   '-/&:#,(1)+)#$")/ *G698D@>*8C)  (1'$#7B>>=C<@>:B:$"                   $(%  "%#0'+!  "#6+&=6) #/3<%ND7ELC6,<=7)' (ABC@:=:AB?@8& 2                   *$ ' #2 ''+=9/! %*7,%$'.0)0'66, 9N?CHG06F. ->'%)$;CE?D<8CHB93 %@&!                    $$ % *#&#   !.H@I376 $1=J4@&"(;+26'( " H@?>E07D0/!,/#,;C@DH=AEA944,J,%                      '&&--C(/    +?@@4 "/#@to[:((Q7@8$1&#& $9?A:<+/4 '('"#42*3<@@@;=G@;69B+7               % *2,+#+#%2/4Q,'2G[OEV]Y>&;KWB",%(. /@B@9&4$-"#=0+0%6HD>;1"$ "8A=JC=A:BDDG80G3"                       '-  ./2285,'!'./)!%7'/%"BE2I8!./91119<@O980) @;B%4$ ;4"H0!-$!(7==GB=AC;=KK0 '1%                   +, )&);'12+*1*2- $9B*9#.FOV=#"4&'0765?%A=1$64F51C" +('17>97B?:D@EE9# 6.!                      % 6,C()-/&%)1& #(.;%(-#20=$ 398.BH?A2QYPO:3+J=".,&'CG4655<25:EC?NI;8;KE8 >3)                      - $8&!&!!!#  ,G-+ & *=L':J@14?/GJ;BCEA@EF9 20                    * *=%-#C(   "ZL! 0938C&C?/."( (,8=D00'/')ZmZC>3 (F6=#$#84=9?/KFC,2/)/'EBGM`R  ,. -Ug]LL>"&36'*1"!C=5H:)# /83;==ORMDEID>:B:D1              #2/7J"-)# ! *A'>W2(A0)0#"*4 JC59Ahb&7,'3. %$            ##60  (.)-(   *W6WZ,&?S;-6)(qP2./?A>Z\\kOAIbZCCG626D5!+$!"AB$2]sk`<,VN48;809CE6 #''=9IFCH'(BLB:A<><8/#*/(:J5GFAD&,GK@>C@4FTC<@91F>@A50) +,$8N:>DA04KJ;>I?>NMBFI?==B:0                   '69&0;R E9 #5>N1* -II`@>2YI_J=AKtUNTT?@ ,2)() ))'3-BC,5HKGDEEOJ B=+                       ! +=9' @>1D) !4, 0 &=DLD/6r@@n?"2*SK L]X\iK) &-%2A5>H8@F?+&7GLLBEDHH&/A2.0#"$$, +6/0@FFA>c+It:,K+ IwvE]VNQ`+ *2/'%$+,?2>NAJL:/39GOA9IEHJBFHG=6PH,W5*;"              %-#IM>HF1$/HPK@>PNJBHOFJI5DF$-G6(-#                       $)&)EC3Q%)%"+D>30MZH=EH-("<4''&-3>2&:4VffJ>XP4 %-QBPN6ibT>GWQ8?$$('"5HDHH=EGJIGLI@@/01*/30$@EIU:NI*%( ,4!/1#6B-GF+hbUOQQlrAM;0&4Y* &4IUL:LRF=9+1-D?/BIGNJ8&>PIOG."/  >^[WeJ@*D6;8">@='J6NqLF]xfemC`QK*3^'$0MSURLF),+'7NO2(JKFA9'9JJQ@ANLHKKKLA?IQATU*                  *?)PN:Y:D8&& 7OjhRYD!.$MD1P9NO?Y{~ZJSLENX7+E#7!F`aILD9"- '3AT/KWIC,"6@ASGGNFGMPGCKJDI=# UJ                   2)GZ)NKBKD. "(   M;TSW[STH5+$)+0>?#DPMB*>EINGGFABHDGILN<=K:957?$,            44*@PH$GVRaJ9"% ) DYNU07>QRM Gk_NZN?3 2G)XK& 247A<[aQVYOC:"#$ )0=1 -FLD3"9H?PUJHHHFHFIHJIABJ8'2!# ;<0P7#1&4-1.0Y\L]\CA;(("%1?=*:)/Q8 8O?GOMNKKJ;7FNLK@>M<UM&-.%'&                 672=S=JhM'9(5%(%=$2/1#)! 7C%>3G_E^QE?$"# 831.$-2D[8)HHG._bV39(261E-57((1&8S,CNTWdY=B>)$(&+207F:9N="0EC3BJFNNQO6 ']A!+-/(                 #*((:0".F)Fe$+' 5;;/#=6$)+QO[cdX?:>1$)'/30CFC@@J9 9ACBSOBBC?DD;AJJJMSR;*D,(QK&%             11#,B>'G9>nk$&>*&'4/'+,Q9.<).>$'K8&[=#0!.(jt[O;\P2#!;*',$1>5),JXBTcZPKK;2+,*2-7MLGIMD/.A;=HTRNIGHCAEFHPMBIVF8H?<<*+                &81+57, 1:@,/2#BX #$0"$R:6<$">!0fQSWA%b{c`b$3>C+AjS@>NcIBJ==0)DJ8CRAGCLN?0.(9H@IHCDJIIIIBIG?BJC7*0>A21                  %)33>2,-+9;E<) 60#$P"4K42[)6:+AC/.?$?B)/aoIAB:ZPMS9 6cR3NP?@8L[MNT=435GE4?MDE=CHD-.*[8@ M=BNLMGFMNLKB' DFNT4 ,2                   (,110, 0'5@7=D6:8 410,B5 6J!8*D1eN-*=/W;:3 @I-S;QZZIdgZfokm{o>A/\^=O+OX9CFHC1@OYWIOWK>MRIORKFJK@*(UDW>2,&%#             !./40#%!1VYPOM>,B: #)?'#/>9/E10]7(C%9&<$Y(0D>80EjlsP1${p`ciomiG#5< =WHTG(MFFGMH@8EGHP>ELJAOQ4-+5C;?LND?LQLHYTJTQKHIKC./gSX' O6(1,                   0328( 4 DZ:HR9&!3@)6===g['3xe<",);0(4' HOV\4AQ@TLRPH;JNLSKFKJFRV8++IH4LNEDFEJPLXQKRKKOQN:(;h::"9g>&.!                     2?@) '/0@TZ##\D!"*35-@,$,J?"(AF9,1>"-Y2",0 #Y_M5Of:CSWIC# 8TVUJ#]KBMCJPR;=KKFHENCAKL.,:S;+RQPNHNG>IYPHRPJLRN;"DK(0R1%%              '-8E.  >1fZ'/d[#;^7 16(";%4#6M98A/2W'B9#VzL75B+FU<JR2xtdM$#%6O_dMI+n8CHMOQLE>CCFJ9OJ@BF%5]`35UKNKEPI=IUPBGLIEIRH &Q6"F>7#3-             $5/302/NjPJaZ6,KB     8%QE<-3@EjI#e:KOM4I@ >WQ^+IRBMTZU`lbF?>e0CDNHUGPBB@KN8NLHMG#GbV9S\F>HIDNOLPNBGKJFFTF 1H:EYZF:+ 1%                     *52# '82XE;a;%BO3   ,:D(51F_.%EC<@`??PnQA 8IYjoXejsgV/KTEPAHI@FGOS;6TX;2GQTLOJILBBEJJJKKNTPA!$I>I8'L;0'D!             08;( *&J+6QG*5@+ &:.3>MQ@O]RLSPGFIT>3PUMHJMVJ;?KNJHPMBOXCKQ3JQ>OF(: :               !14,  3RMXgVA5;.1# A[L4J\bS>BAESL;LWHETLOYN. 5wmW>:'L7(#..          ., >$!.Aqj[]]O?E:5 BGOe}]4(3Zw\E$ ;IEL,;9&!(1bf[= $4+KDC^WD+CS7>AG6YQF<>A@SJ3OXG?DLM@6JUEIQHMXUJJZLCOUIOQG*LjeP0 S<$)-+                #&/ &MR:RONydW]TKGA/-*WsL6 #[sbV(3?=DD9IJ''3"P^blVA/&%I9FYH:AQ@:KHBXDRL<@F[8?FGBJJPI6QP8FVMQ[OFKSIITULLSI'$]rmC(4C*0+&               -*&! ,7:*Enl`hVYYKPM92*5[{C *12)-E`n]) GPP3O^J&J:Vc]^SWJ)%6&EN;5ND?>FHRRC[W=;IZ1-2D>JAD6=S=6GYMCLJDFHKQUQSNPF33(3ejM) 2$MM5OCPU=@E$)BRHM;'PCPJDIXTLGJHHNR@(">DIJ*%A7@CI[QJHRHIYLBMTQHLD80:=0EQK@)aE8,**              ,-2D,$.-$81FTG Cy\^bVR/5_GG1.0+*77,4.$3-.;$ D'4*("(:A';922C(&$NBEH (L=) 5I-):45G!CU<#'3:,JF,sQ/;/1B.               (*AWD/0-WYJYMQiQhyPBW]5'LHTD;0$.MNEFZSCFT7- ,C4%A#.;):@O]LHVZRNZSHISXD5@-"3S^[OMXM]q5,"(2           "'5TA3.332DTfODSDMRLe`PO]N(AY8UK%$1 EE/+A$404''1 67=/5+)E$ O%(:0)%54?(,:N]Q]S%IYbi[E+D7WB=BJFFYI5PW?*IHD+-tQL*'@8FYYRKMIJGQUMUD1B5+GUP.17$,O/,))             %#:EDe_BAXG5W_gs_ZQaba^UM@MJPgM#E[(S``hH,FF/E6 7@4623,&O'2QN'/OG(-9CI(@TBCsN9]HMdV9&M=VGID?GEPH5OZ3%! i]7I'3EAPZPLNTIMSQMNQ?7D9G\lX8BbgO( %PO6%&,-         '*&*K`MKOMHUxbYr^\{mMMAD]YXeIDE -3VfwtEOP",EG7:@5-R5$93#.J2+Nl4;83(-:RO)0BLfTip6EZHGbZ?$R8MBK>;JJHH;JZ%*""/~zF?;9DIFU\IRVNFY^RJKA:>D?BSB(M\jlYL$7_93G@8,                  +. 5foTPYSKh~||l^osWTtgSH=baV^[D-E%1:=^V@IQ]]9)CE<2(20+B'31MR DL@)Oj>:,$!8OECS@ivRT.ARROdQ@'Q3PGLF?DQRG>G\*'9 $)4E+#1,4-'*FRPaLHMRRQMM?;F:4FL%1meliO^HLf"#VL1#                ,)*M{lQ?EEcsXi~moyVI_gSE7FnNIW]Y=.L`\sJ07Dz_+/nL;F*, 5++&@^7BD<0#OY7DC4!"CM8RRR1P@N96^_Q`FC*R2SMKMB=FQM@?R<,":3.40.("GIN`HMZRPRTO3+&F^;8gngrlSH'Ai%A4!(           7!+UnqQDACZOWWQ{qgfHViLERUN5DOdm=?FGXeBG\Izl"`;4V'-9 -&-P-,E>7853HL@LSK0;OH4L+1\,1GM:3U\R]OK U6VOFLG??ON@CGD- 3:$.-5(&!+GCKURSQIQQH9$'H?%Kxdanm^0/#()00!           "%3LamF#,PQ^u_KWp[daZvg=CbaE+1ScJ =5KV\_eXSu"Ty)%X.&>'*G2&OD4=63AND<@.(3+@CE:$J\XTI/<:5X_XLZ7XNCLGGOTEBSG5+, 6)'%5-%DMBSUQOEH`W?2,7U/8nrW]nbR:##$DC1<1&            '$(19[vof\ENhZ_qvhGIoedsO\`5NxS9R+'Y^6BLdOYv\EvxP7jB,F&&&/A-.+(I==C2-:BDG!BV!.407)0G]G#  @=";?$);>SdcU2OPIMWI:NRQPEHI\ #54$1" #:(4KOIDL]SIIHK?"$:fjSboY^od*)`Fe7+J&:+(?"             (&+/*;z]Dgklo]B"%PNLlzmNH=9mO8lG8;:`@)Mlfgh,3/WV_NG?78CFLM/L%IE$+DtT A[<,UaG:9&/AdeR?:TFKHHIKIPN?GJZ%?>74.0.2,(>ASEFJXMJIDA,$8aOGnvW]qhN:J[#+<257+;0I)              ("+U?CXDUbqnTS]Za51kiwHAXNZ8Rs;)F?"7NOJE>Hza8g^ZYbB;Y83!:K;&' .T92/2yc ( !!!axL'0]lVJ1'&HeX;DCMILG?NUJ9>PNO1;&)-74#1JAdxB1tlK('FRLDHLSTPAPXGB:AO#4*+-)&7!Zb<84->PUQLF<(!5Sb@5aWFu~i6(%0,&bm7 47*H+           "! (HTdfKU|}}`SbwukNARB`q@@exj$J}BBU`50,*%*K/-L@'CbkcE;9%<93U<5UC$67##Al-*3 4Zgb1$Lcie[;0?T@7KLPXSHQOEG>JN&&. ,*,;$9_H,,*DVQPPG8" CnR'Da\Y[>:C>H=$9+%-8")E+           &+%)2-R_Ujown\<=^SUrjUG5P_RKhG apS*$F\1/CHRTCT}n?,8N# &8'O>2I68K &" MV('3# /6T[00S|_[IBC2NNPDISIH>4M7;*$#.,+;;GJ'#.,CVSOOH1,[rF?|wkeF':0$(.#NI1"#7"'B%       $""-8YM9V[mwu}XP`V8HzoG5^{lS[UJ]wyB a~C Hp'-bH@mP#/5E.!B!8S3486/&/WD    1CSO\zqXUl\UGRHDGGEME7F7&='+-56,.9BG5*/15OUEED&*Oj^<`~fed_P`phePFTKUcK:[M0(D'         "* />SOVotXM:64*$$6UhW=5Qua?EdswZMUɉ4.M3N]'1=,E% /IYRB:EPYRb|k9L}upeHN[^K 8+F059-Z}%-C"F+5O46&@j?5,:Q%<448JE61;/,Peedf`\SX_mzeZ`zLjU512 +I*,80         4)*QB5TV(&64BTL@FV]G  ;R=1KhwqqoXYKMreIWH#6"@38.hHXO *2'HM(CM4?B7-&2b-@" 0'" 4AGOPMTZ,#.SLUXMDG;995, 5*$>A*0G5-81,$81alN43 3EWhBCKBQK[yoRWh^# :WimSWj^Tp]EXlkH ,'1: ,'5W#RM) ;^/$=9+(EW3 *' :( Q2 -KMIMNMB+0=0  IV 39&(69<<(6Q2OQHrqXai[YZQ=X~{rVH\^AE_F*_XH,%B#      ! (ANMX[:  /QnVL]TLKJos\`_fwT(ojafpuedoF/o[" /*#.5!3#'=5Y!/?J{f"srYe|kJDM>7?cwHUr_io:1rX7(L        $+!39IK:18Ti\IS`UaRXLCfpS@UP@Xyk/`wXiwlem^:M3!/(6,$ (+":/+3%,hv\rV@WX,En=Vc(4;518**XX!$ "!1=:qdRTXW^dfUA39Khp<>RDVU(.ZLG /X#            !&&(CJA0%! -IWeZNo^8eWGB#AgG2I<"*YW96IF[iciOFxp:  '2$/A";+%9"&[`ejhRIg9N<9S)-8)MB1%0&.^?  2,B/ 6R/A&,;XetH@;,%!)+78< 61 8EWic'CA( +<@ULANAJ--0gh\^II_]OECD, :]<'0INZ0*Q3          %!'785:E9$ .dd6I_eU66k\>_`FFI;CU;*?TV/6jrttmu;Lp/("!")$MA!5S08;_]ND^/bW[fT)1!5(><2(AQ<8+2^cagn3,MY?IQJN@I5')m]D[hfXE@MSSC,1C@)IN1b]F*$=5         +!187BA@8  ;KdlHEPL5NcRSR[hB%]bKjkN(3: ?hoguQLK #%%"6&8C&.5(=.*<(#M6L2!QU:"((-.%NG A3&LREKYKoc4AI0C;Micd\9G. !..+02AI@E4Dlp\Ub; !FXP>PKMSYuvycT@S; (Aڇa~~M58!$:.(2.5,wD*&'.+.;465:T'/879KDJjo2@)=F9aYM4Xo:<< 5&2:A9.F.=b_IO_O= /OE:CTG:<>JE)"XJLfA.ZbmaIadmpnhP'0eLC_E+>Q%         90)QVTD93 "NCCyT0DD>LKFJPE6DGHUP_`[sS$ 5n`el376.M .C=-DC#1/5DS/JR "+--=1LB&%SDAT)LP&wj=3^mH3F/(7+-1GJOG3KJG[QE[lHBseVnmol[_xXAsp2sE';D-        $  A(:`H6%'<%6(Hf3.A>*hhThJJTA;V\jdMC.!2$2%4GE_@(FLUNYREFK@INRCJPALLCN[B=PDUYTVYK?7CBNk~xxYw'=`3#5*            !'7BgfKG5 ;EG8TFILg>JrWI<@nK6RNGfdU<#".=%A@1U4-1GADAJ^6,D?4;PHN3sj:Zb5b]_yNWpBJ<-= *-(#''&A8AU^O''A5#8RF?BQXEQ`GLbML`^E*$;%/10"/9 'WI+5C(        #  &D9/- Zc:VsYSZqYBNS;*$$*&5IFKLUR?SaG?YXXC #QiQ#*% 40-)=<*C:     !"4:   *5QpFn{\gaq`GqYBMSZpQK5A}d_xhjQR42N#  ASB7#!!.4=D/.;6:n5MlCHQ5;3%551O9/PTZ'. +&&3&&B*FK9=C[[GO^x]64XjU7!3esls358=^2(H8        (>3)$$$' .6I,ClaiqceegYZW:9M_]_A+/\VC?JMcbd-4# 8KB*  "212J4;I+_;02,0:Kj]O)%5(%,5<#*8.29SPp`FRU7-ViwaKcO4Gy+RA %"$AJIDG&HMDEFIHLKMJVP)1+3Y`bkJ*>NXmrqy;HZ7)$"7G*      ! $--8KAGF;EG  ";L-&k\I?;NPXiRZB.^sa0?FBVT.8:T^E9XB, 2F> !# 1)/.&<,)4' SOCZOWO5FE(':* :0"''&%8LgoC4C+-2/FAG=^gUUWH\j[cbb\ESE6cf_  "# 1 /CHBE1NCDTFKVLCB@FP:+;RjY_H-GiiouiWE"$LUH&*JP6#5G2    !!+&4695;98QXLCC  07:Kb3EG2`[8rf[L=REC8EPDUQ;ROM\V:;3!#   :N4  (* '$%;4NO)=@81QvU_7@M).7DG7.4.1TlSDN6=>=5NG6L=LkXSNhjYS[TNZiLKLI],)&/ 738+0;C&0H>:CCIRIDMJKWA&;Y_NXN[oNYmOUB,=I;KLNO?.#NE     %);:DBC=AH41 9\XR>cB5OF8>+fk;7DJAXO@IGBTQXhSQkMBF! "GS5#,"';&3D>T<6NaGCFO`9FBN@?B&9;:>:'#N[KHW?#9Y[- +*'.YPgtG]`qq^YuvUTWOli'4V2G2;; @+0,64/8(64@DGD@BDMLKWG!$LOIN_|n|87WIC`f\l^;1'L?    ! &3-6GTN?NY+%_\JQfZBWzL2cU!=S[taCD91)6P1       2,/+'=>-=BH@57*4_7)9)E>8R@-P*FJ,LO--3>OI0=H6If?FH<:iL"  AH*( E,),+LE<9CMQjROY>?NZQ@HZ33pe9DZTI=>'AA=>;9ES7/SD1$%[>     !*2"05CC',80DD2:9>,   %395?<$EO$)6ED8U;5N?&T>#aT2TA(irJspX]b82  $MR( ( - +&,9--9NcaSVVY&BQU_@DNC ,*>Z.3(2PZK)$ .)'6%LGB;ITmU=cfKnmWUVXWGWaONCs\aaGD!+.B$ (/?MD?FLH=?GTRFKI+(L[DFCAaS)'([pZpb%APE Q>  "$'26983;.>K7GG.1+"  0.GCA21F151InUS+)>:73^4L=2LA(NMQj94TF8@% GR5 /#,,.2%/@OFCn[SVB%KRE_fVDJ8-PF]T>kbM-.ZA?((!$AGWpO4hs]YK=S\if[_E?bdKBQ[TQAhN&-+H/ 8SFEA@HE?AOOE@1$"'?'+-:NRE]v\M#[dP0*M-  $  &8=5AD@CP@1?7"'$ 179La8M_@JddA@`84>W<)89XD[\9XXVdSOsY.0866LN2<@;FP;C[FKJ>JIVYKPC-# JZ< %#!-),JF7JNFKA5CR=4R[TBUUWH1LL& <]=8./PL7,"E@ .4C?cYCNBGDELWOFXURSeX=?R2'`O /RlN+B=UmG !&!&>LKJ`XLBiiA=:L;MmobULRqTNKPTXJEf8DGB@IPIG'&ZzZcomJ)geWZOUB33B:04G ")#$#1..! (('      $EJV"SC+^0;rh);G>WY@PlD>F7=2:49>]SUS'C\K;`L>UF  G\L" !%.>,/JI.5PF/MX?.U^[MOkW<_nE1/;;-35?=^wiT>*10Zk/ !7< =-;9?BNHFLLDIB"'Rtcpod`O:),867=)4A$ G;4YO3201C59RSKVN:<=$LNZZ\N/0>DHpC7L5 3WP&,4610(HH/V:@YK:C9VUJVFE\R5<89;$ 5PB_o0  #%)N2>W5CT68)`NHd[8:\IDkpk_SWM8QmbCH5;/'G#  =>4GKHEFKK@D;,HiWcd__K5*?,4^@+-,$5=T7))"-)%%#(&7!    0A6/0K<9APIO5B621DS5<.3B51@>7?C:A8SR4]a6'0C8*Y<=J5 (L\; !#)1,032P?PG'-CLSH4;EPFFRS@ZcPPJ$N?m0  RP:JF>@B>AGK:!2^IQRSHDN66+<[# +C05O#(*!%#% ))&HWB92$$Q?$,GM/D&,.R6*F1!)3(-7,0D7^P(IL1;86>!C[A,) )G[O "%,5@@;??BUL5+>W^YBAD:WYGCKUU7!(71+!1+U4#,,;%WKBLCN,BYwa7G/3oJ >NDAG1)6("!!%!   #?)Wg%BJ G$ 9ZP)W7-)'N,'671>81<2#,ESJ40>2>@DW8>T#(@.  #XWCDRQVV`\IcqSDYC:GNUKJF2<%-/#2  $7KFTQBBLUL.8'$kbc^]YUhgaXczC9[*OH8IT-,: (+%!$'       ' <0:;7H M+;9)-,Q+>75Q@?13BO)&=R:.37FOM?J52.(>+*O%5?   *LZ2! '6$6KE91#6?LCK^6@4;UPNYHWdRIL?A-BE*K#% )"#!7K-=!DB=.K?3dlNQW_ULgK4^T?LSDWQSSIK@7$23.)  ,PNDUD6GKJJBC,,q_LZdMNX-Jl8E*G++=J-07 #     # %91AVA-*H<.K+1KR=UC7W[iS-67)6D#:L?]K>S]O?--(,?P81*  0JW7 &*#0(4T/.+@OI57W>BBBDHLQGMSSV><3 *5+S.T1+. " <"(E(*3-<NY.NQQM?S: K`>EXLLOD]^]O7=F:5HA3 652^IJG7E>CWV>*2SZV]Yc}`1.@U(<102C,+#&     !'*2FDCf%?D+`9XaCW99G66JAB=2<*CG%8+<97TH4FN=7EU1/W>+GA# )HK>" *(*+&%7"!)<./Q7)>LLFBJ64WZEGI94$D$V7T<,5 -M6)8%Z57SI^U>?QA;&*   3A&@MAI;LLNSR?&7GOUcb}C(6,3z6>C1=D2= $      %-205>Gd?US] 'lG8@=U*,R5$/%+AI@]H#37?)>E;+3C17LTOEaT:ZJ  IUR@('&3'2C.0]H*<7;>8:=DLQGC?<0+4$1M3F( #60=80)LO,>%3Z*=]4H*EU2?BMG0BFKD7614?'0!!5 'C(&O:NJOOPG=87R9/dq^G 8ijkV/}r4(08H:.3       - #(:4;B1b$I"&58@G\A1P.&2PUK%',&$$92: $C>;;724!47859G7D@?899% #%$_7G#$  " '- 4C$=+,/4*2aT*CI8FK[;(M<@V@I73L>BPA-6B6!-&@))  :9:K,FOLC>BA0(35.MK+.]xY[}-34 ,PA&"-      ## 101-&+)8OO745,.^nKMY3);G^;d[/=0!A7&(*=*$I?"""GWNUA2G+"!?STE!#+#,$8*,*-51K7%C7."H@+>M.=XS0Fb4Li81;FA6DG9.<=!%:Q/ (?;P4AERO?B<"."I= )U`81/N85>N?0R*2B/9@:QI.72+6HA*$R"";-220DF8.)2F$# $05LKE&!8/I>>&<;:SF1GB:G&FkCE* $$(88$:9+7+QQ2/:>/:MKTKL`L>K8GOPB3N60D:-%/8<) #:?6I6<>SPF4 (")S1'Hfp\Ylj}<M? #,+(!    $/K5'58:X 0$>7=>[7'C@0"?'9R/4D@@B&.<1I-3)M'"*'.*1*92()* &, (FHMC!1#!6)1=9B:,FN:=H@A9'!OE3C`bKCgBBv(GB-(    1: "/2B/J*2=HI8,,;"/"8IQ\3('HI=9I6,(I- L0.;7..4=<786;":9"-0# !3AURW,(/""!#.0)3I6/>51?B7:8]L3"OC&6*  (",RK[N-'(%-7&#D&(81-+.A2GN.8GF=#%)$!Z?J*  %2'J7#A9HH,66<6$! 57?RXflxcbl %Q)6S4-K7$'"        "!+JA,8F  /"$29!LU0KB5U*QN=YA)QG&+"=4ET22<'(OS)7.'2 $( GP[`.!"$7/*@%'*M<+G9%:L-:G80,"$_KM 4&7'E=!L/FbOf?-92%5<-N`->6O<M&7H67$,[?/85@B =;&/:9>6/1  /''1%5D=EZ[6/g?6Y,2ED*FA8A1.*0/$'',"   9A*'7!#=?>cQaI(YK92Bc0+fX58!579/)1:.*2((%*7/&C8'721:H8 *?=  G1,uq3W7+PK!&8&-/+"'-$#")*##  '+"(%)*% -E2(>IC+>&<@!,AJ&6^G;<K*4469-2I67#++"!   ,\LH %*D.:C;.5$1B70#1,;)E(BG1AI+ F>';24=EbV?=_i%;?/PXN!'"! # '!'!,  +&25FS&&69!:B%,7$+&/?.==,/_6 ,`A)&! "JNaj<'&/%%:M;GKB/CS'3:06/*$"! 7dQJ    L08>HED>765K>RUnIJIPCP $$)-<7,P,,.;G0-6,?3,L28\C?  /17%+B52. %R:*A+"D93:,0.A9$!# $' "@AQc=%-.",.):3.5C'64)'-8-!%5R8<  >3AA2+<-B;4809M(8S]TiIBSWUWL3YMXO4RB:NI",/$;7,%H-%-62+.c(4NF1 -OXT_TOK0 !>e@!*L5'B0       )&"&#,!*01;8C6+4/ &.(&   =/5A$"1A,397TH4@4AQ>"#!  .MPZO:2'& #&%+?+);&-3"-*(,M78",IKD,(78=:)42/61/31."&1:A@:**)  ,$#434#5W/1E55?6.*CL7^\B]K:J>DAA>@,8@4JB/"&;) -(@0&!+\,:0   0DP?# &BPGNiQ34)?5"%<-    %-(089@LEIF801;<783167=450/5<3DRPTMB?;@8-*" /0)XI>=*.C6$,14.3<# (DGQkT;5.1,3345E<+/2.95.5(*S>3%201%&CA:3/07B27A6;4>GDE?FU99E@I:BF&@NC<#3949#"*"$3L$&: 0S 0KX9 0N>)/@IF<2DV/9nG -%(    %!$-81;A:8B3-32/$-4..3*.3:@GDNQ=2978.8B;@;/! ).#DJ8>=.*. (.: ;"*0,#.AMQRU=" &$ )**( 1I@;:%-I4-',T=+!  4'21$(3,!666F)35:OK:EbT398';JHL*H[4CEQB,356"9%-*+,/$ 4B" UY+ "?PD0CF)3C)0Pnf@2g[(& ! #&&*,&* 00)-526E5(C7()+**2,+=<508GKBEE93;.-1/1.73/;E6!&&+8^C!-G.- 0:!'4 $"-%0-DJKTE$#"3$"#334=:/0=10),H/9! ).>/3<7*3.G]+AI2BO>CNIGoL:K2!,H/% #%!3:)23%"181579452)'+%)1=5/=6./351+1---;0*6JJ=<<<75*+,*+-62-7@A53C#$F\.";<#0*0--)!)$ ! %4')@22ISD.(&2 $,-%25(>@306: ! 4D3E)' "-$(@)1"7.'4:(6:=4868B57AD?<;=7<[A7(%+10.4>@637DC1$4+)1::#/A& #(%'=+"BJHI* '."*(!(05,&@1;>%!CFEK3DC<3>'9=3!I.==9A7NV7TYFOB@3>*/@#:4(%B97,24:>>J (DLaI%BF9)+=7FZ> $KCOYW==OEB?@;/().430'E2;C+ $H=QZ1!!F[6$8"+*4>G98L%PNEI!4D74KOFI6?;797BCP6I<$92(/.3:"+4@2==@`3"$ +7# .2.'+<97##C7!;8JR=<3YW $")&)%%&+&)/(--36?;6=DA0.)#)3+!)$#N`llmGD<*,:71/&+22/0155247((058109<;89A:7++63'%-&&1%%,)"4DOJ?$ '#'$$#,)/=(+ 2L=V]3/ 8"!AB>3+&G 6B/.FY"8D /N78>SPQ8?71HLA?7HL!  )**$,***)('"'"%#'880;7-6,07>=-"+=;3%+ ,$")!'#%CK<45&"%%!! &)!BA5?6?#( 2(*("&L ;;&1-C453B/1:+-BB=4EJH1LL1HWA37G.A0,+6)-&9'0B>9A[JD.++*$Hhzpj[6%!+>>CI<%)CAZn!6#%1-1*!'-/17/0*+*'.*+"+2%*$#&$%!"!$ '$#!",0*/+(7**295.*''" 6"20+' )3*%'CNDM=0$ $ =:&E!'&%41+  +":K1#A(; 2N006)7.4@;F=ON:JKO2:MA=,GE8+8;*/#1 '?'":54G>8E/'# ,DYw|cKA9#$1?SD 1BF+";6D3@LFNNS"!&&' $% !"&+*""$+2#   !'%#"*%% ,<-'8/(&  '%(,)++,<;@IDHTQ>1.-## # 6>F"?J$6&&>448 '#'",*:+6 A)6M@,I;:=BL@?JU*GF-X/AA(>%06+(5!/4:7%:+:CF=.##<3$ " -4%1J:A42U-C^!"* '%#+1*.###-,-39=/31'(# ("")" &#*+05*.7+  "%'.0.6834?7570/9<711<*#.8FPQPND>;;1.5-&.87FcnPKf_<:%41Pb2<1#$;4CR$/'*&& $ -2/*56CN:=5TN8DF2AY4K.(H;5,<7,%8+3)7A=4:00))#%6*61B@88FOYXaXPONOC;<@:0*.0**$#6aobVVleC# ! #  &Vb@=7'3=M$( !!=// +135.$9:?/AR2JU:FV6LS==4C8I<3F12+.*#+ /.3.9<#5*"**!#&%c]ajgg^WSI:2/&!, =/1D?5MB#"/&$-)" 1-(3,)-!"&(#.1   +%-11(    *,+86::9;<96:<5648@;=7<=35?<341/21:22:33EdxeJZulF70/#EFGHM#$$# 3$F'&+(*   %,0;79F=58A=:@74>50:<-/A9-1=305+3=7;>76.(0&.9@%>A?;7;9JELF82CJ9:F@AH5&>>1 #2*"75!;<:S8)10LH!/3'3KTN2/% < 5.OD-" ! )"!!%#(/+$!335/)6'    !#+5228?7:B?8-+2=58<2612;1:?;7457749682+8.FmO3GZZQN; 078M= 1>]? '))##"?,420+E<5Q:6G:7.:E@>N:`J)G>EDG74%.'*/&+%'& 3? 59-%0CeU86/24 "35)"'A7$.H;7G0       #**'  .66078239;51744:<83495:8.7A950./0.358893+5>=;8296**N`R,-<:;% ..7>GF0;DM*  14+%31)%3A).O5&<57:AF.7P;'R7EZ:AJGD/1.!("- ,*#/"'+'"KdwD3HMLC5(15?*Wb=V?':/8@  &'-03/.++ !!!!)+5:89:7503@:6317858508<722-(3;01156-465-.64185*488713::+#'FX(0'# 6G *626ADFB91"JX & &'731)2-<0,2316;=5/BF;26K?PF4,TZ8EL7<-%)0/ (*!9/.8==MODZ6)?Z[D@#$!2J*:) .6I=$DD7B:=GO=?<- A"-:'$*#"!$1><$14/C<.DD.3?/4I1KU:IVB?E=;:( !(+!,*!A3 &4@8GD?_ed`^Q7LL1 ' '>,NQ#"$.+()(+&5.--%$!%$ #%&# ''&#%!&*(+.6-<8,45--2..7.1215536<85::3450./)*44/3:6./767<35@<8;64::;:68:??1/3,5C82/8"%/9O5:O8@IH4=J8.2. #)('#"(=,3,1*3%/1A=13J<=9@==@A64XM>JB;N@0?5!1)'4'*6)5>;FcvrmUAE>@QB")$+:C.PH  $#(!,')&&+-')*-538457:1%/1'-352664229=6164+.15*(;5-:G?5924?67><;<<2.65763?:4?832457A% "370/"#D13BRHLG7.5;40>6  %(;?'(*' #,;'.?-.AA#9?6DJ88D9JO@EK2.D6 !)"! !,995B1,24<@>5BI?Pb\W$0),<4K>!  #%* ".#/,-3528:/42&/3.0.1487@B8..04424-*:,)@1*9:30:@4564:64>@,2:,9;070.2/D>088;%C@"):/U5 9F[L/IC1:608;6-B4*" 0" %"*/'.+..().616,896KO51P[=HU9+9))- "= "(% 8://9##10<<:FJPNCBL_5 +=(=>Z9#!!"#(2&/1264+51.--66..3,),33664932@7-6/'+*+1(,74643;5.8927>8344439:67;2(5>1)$"(%>UN8YjE:ZWOI?SO:BG.:ID6?6$?D?A>3/0 ;) %/'+- "3-2<+6B:092,CKM@>P=EV7)! &!!*5##'$,+23)-2;;>QDDRUPNP/ +.5f;%!*    '"3.(:64:30,210:1.-13++1,06487.6;1-20$-.)1/2;0.<4;706/7D;16561.779?.)1&!%!-'0$*"$#+:7//4D\O?F>+@LJTVH@-"&,@#((2#9 $- 55%*-;=91.71?Q70M<1>A@KF&++% ',*+/;E:53*(3616?PUSV@ .1\1!+("#     #'.*!5628@9/47.1,53/3*005824230.66.16.4739.*8.0A3512<9:8,1C:9>8;4("   +($2-*>YMKbWGKI>>LNDO3# ,7A3!'4!-")/1 ://$-:.'-%'1JK-E.573+*&!*/*1605TKFS?7EB>.&6- BA*#/2! "&$()8<.F=7;;9B?7;AHA86;;1/)4,,456) %+ -*,1+--*+(*)+01+178960254281#12$0>-,674+7>392((*0+ '$*%0.;>:=;5>2 # -'"*.%!6HGA>#(N   &+!' $('&$3*%00@?:?;FB;7)+,--/$*/ %'%42 $995>2-?>BACP>(0:#%=3,F3&1)"%!20-7,-*()00&)2.4<=<6?>5641++1%   ,0*++"#4825;24750+:7,176:5-/,-)'$  %42-/*#!( -(2."24062/8(+*& %(("0/&#'& 7Q0& '0*!BG#+/$7-2<<7C9:JE>L+:42$;,, $" )/$%"*?OMG/"'($:6 #+!# &*('$##$*%2#%+4% $ &.*#!&-,(+0/(/8-%.4),-#))  0650265)-,,4)-0(,?;++3B>5+9'-5194'-/372#<8'3 @9 ,$.*+#!#-S;*M33&,.,G79E31:&9/#!$%')1,!%) ->'!!&+1:#  #()*#%"& $&-+"25+*20(6* (%  "+,<,02'(&")$*$#  &)..4==@H92E6(417@:3).- %3'3=4917;8/;.7H/1:+8?1. ,H-2*0$ "$ /= )867.&/(& #C4$535&,9-$  ##'%2<34/*)(  -'#(52?5 $"$$!'(()/5/;?>;9=DG2-9>79:7:501-.=+ +$+,      &&$+'.1$-50<8+32%)20-+@7/75<44)(,:67=/<0(8I;;G/,.'12%B:4::/84"%'+%7 "?()%% "/A996494@.")87"8:00$%-* *%+37HE6:61% 3 #!7+-!(:E*      .5 %79; #(%7/5C01+& )$ '$&)$!,31*"")&:69:-+##.9,02952<)14$$096A;,'/4=(5;8> ,9!4#))$ "('%@5B=)::09<375: )=%))'- ++)-($>=5C?:.0;949 R+,*G3        $"$).43(;@94*(!$)(,+"%&$'(!")0, &*(1:7DG8+20,OE-:;000/ 10" +%+7$&430GHB1-.ID)A/8A!           $)2&$4 %,&)()#(.*-**/35;84468BC=B@90272*7A,0831#$)&/(,3)#/--?;18@13=8>38>1,2*$&90*,&-6,1+-+5" . %( !"3#%1)-#II)RH,;I3,6-0.& "! *4"$7''7;5%(+D^E# =-:G0                (* ,1/44656:::=?HA<@5091*23+2B:0=82A:,-1-4347$)-/3"2<4>C<=9:=3;9*.$/92-%*4*,1,0,"//,<#! *<1 +8*<9)>806C6%/',$  * '$%.' $-.0& $'(5H7               +"%),-&(0-$,-01$AM'                    !#&' *3)./)*,2*202+'/32.BF79#&*)))$*6-3/*2<<1:6+=B7;6=&'4&/1/0)"$*6 935<*%"  &.&18# &%''*-;P9                    &+*$'**(680=60/45;5''($" #17 "2%**9#*.7)"    ,(501)$ )60#(56>3                          !!%&0(',&$+4CD?PHFI+:<6F5!'-:=21620*6@)(42+#*.#'%"& 1! "  #$ $'2700%7-31'$,  !                         (-15.723<*5<;9CABD?C5:60+/-%D'%,/#$ #,!& #   $'-(8&  ,($&!&./!                             ,%%2521@CRBCR>$ 728K(/.(' . # # ,4 7( # ,2'(-0                                +2&" ,6*-:3$   %$$/:.(* 738MD%)4                                      4- +4>$,#$+ 1M:,,4*                                 (!$.'!'/ # *'&                              $ % !*"&.$                      &         '+-*'.&"&,#!%!3)(                           $$     ! !*,%"%*(,"((''"(                                              ## """!%##%%% 1!$''(. *)#1,                             #% &""#!7/+2,,*"                                 $*& &#7 *(                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          mayavi-4.1.0/mayavi/tests/data/tiny.xyz0000644000175100001440000000113011674464502021067 0ustar ischnellusers00000000000000<<`?@?@?@?@??@@??@@????@@@@``@@@@@@@@@@@@??@@??@@????@@@@``@@@@@@@@@@@@??@@??@@????@@@@``@@@@@@@@??@@??@@????@@@@``@@@@@@@@??@@??@@????@@@@`mayavi-4.1.0/mayavi/tests/data/README.txt0000644000175100001440000000204111674464502021030 0ustar ischnellusers00000000000000Here are the sources of the various files in this directory. VTKData is distributed under a BSD license. John Burkardt's pages (http://people.sc.fsu.edu/~burkardt/data/data.html) list some of the data as LGPL and others without an explicit mention of any licensing. Unfortnately, we are unable to find his email address anywhere. Attempts to contact him at certain addresses results in bounced emails. Particles.raw - VTKData caffeine.pdb - VTKData cellsnd.ascii.inp - http://people.sc.fsu.edu/~burkardt/data/data.html clown.facet - VTKData cow.g - VTKData cube.vti - Self made. edgeFaceElem.exii - VTKData example.dem - Self made from VTKData. foot.mha - VTKData foot.raw - VTKData humanoid_tri.stla - http://people.sc.fsu.edu/~burkardt/data/data.html nut.slc - VTKData points.txt - VTKData prism.neu - VTKData pyramid.ply - http://people.sc.fsu.edu/~burkardt/data/data.html pyramid_ug.vtu - Self made. shuttle.obj - http://people.sc.fsu.edu/~burkardt/data/data.html tiny.q - Contributed by Francesco Poli tiny.xyz - Contributed by Francesco Poli mayavi-4.1.0/mayavi/tests/data/cow.g0000644000175100001440000010400111674464502020271 0ustar ischnellusers000000000000001 676 579 1737 1 579 3.247406e+00 2.073333e+00 0.000000e+00 7.338180e-01 1.740873e+00 0.000000e+00 -2.057387e+00 1.841308e+00 0.000000e+00 -3.392806e+00 1.769365e+00 0.000000e+00 -2.348005e+00 -1.869142e+00 0.000000e+00 -3.650579e+00 -1.142448e+00 0.000000e+00 -3.242148e+00 2.021387e+00 0.000000e+00 5.177545e+00 1.932323e+00 2.140000e-04 5.908842e+00 1.388968e+00 1.026000e-03 3.280681e+00 5.108020e-01 -4.994340e-01 3.136321e+00 1.712370e-01 -5.398960e-01 2.406969e+00 1.645328e+00 -3.414790e-01 2.266003e+00 1.273253e+00 -6.633320e-01 2.500490e+00 -3.502900e-02 -1.170179e+00 1.859443e+00 1.450393e+00 -6.322220e-01 2.081700e+00 2.832490e-01 -1.259461e+00 1.640095e+00 -6.745560e-01 -1.246804e+00 2.079600e+00 -1.473377e+00 -6.549970e-01 2.893096e+00 -1.302168e+00 -3.320490e-01 2.721135e+00 -1.520418e+00 -3.623780e-01 1.373216e+00 -5.646200e-01 -1.106245e+00 1.219163e+00 -1.218342e+00 -8.230290e-01 5.423860e-01 -1.882270e-01 -1.370375e+00 -1.795480e-01 1.471301e+00 -5.445130e-01 1.808600e-02 2.865000e-03 -1.523199e+00 -7.194100e-02 -1.346991e+00 -1.237379e+00 -7.847000e-02 -1.688266e+00 -8.002300e-01 -1.126660e-01 -1.806584e+00 -3.833240e-01 -7.524160e-01 4.381320e-01 -1.467349e+00 -5.050450e-01 -9.138270e-01 -1.645612e+00 -5.242310e-01 -1.399266e+00 -1.313019e+00 -5.640740e-01 -1.789401e+00 -8.031910e-01 -1.115594e+00 1.392380e+00 -8.196230e-01 -1.053434e+00 -3.425530e-01 -1.690245e+00 -1.077209e+00 -8.696490e-01 -1.633144e+00 -1.582187e+00 1.567542e+00 -9.823490e-01 -1.721806e+00 1.391264e+00 -1.113307e+00 -2.433366e+00 7.822200e-02 -1.371269e+00 -2.765069e+00 -1.561164e+00 -1.084313e+00 -3.109047e+00 1.789477e+00 -5.653230e-01 -3.085650e+00 1.657340e+00 -7.844790e-01 -2.930312e+00 1.103580e+00 -1.125876e+00 -3.395381e+00 1.726325e+00 -5.547700e-01 -2.901159e+00 -6.195040e-01 -1.284058e+00 -2.982676e+00 -1.264649e+00 -1.133596e+00 -3.734989e+00 1.589433e+00 -2.952020e-01 -3.655148e+00 1.190938e+00 -5.736920e-01 -3.504525e+00 4.055740e-01 -8.470660e-01 -2.462835e+00 -1.305500e+00 -9.663950e-01 -1.576196e+00 -1.696317e+00 -3.581460e-01 -2.126375e+00 -1.866497e+00 -5.028950e-01 -2.699966e+00 -1.850397e+00 -5.700700e-01 -2.811581e+00 -1.798210e+00 -7.127370e-01 -2.614084e+00 -1.905005e+00 -4.049250e-01 -2.503818e+00 -1.909806e+00 -4.314560e-01 -3.533910e+00 -1.330455e+00 -5.682460e-01 -3.335942e+00 -1.365685e+00 -7.245900e-01 2.024692e+00 -1.074646e+00 -1.172465e+00 1.497480e+00 -1.283201e+00 -1.088258e+00 1.801794e+00 -1.465464e+00 -1.129277e+00 1.953911e+00 -2.462233e+00 -8.305940e-01 1.529305e+00 -2.092083e+00 -8.873530e-01 1.451825e+00 -1.576856e+00 -7.027580e-01 1.596751e+00 -1.722028e+00 -6.846550e-01 1.721745e+00 -3.116887e+00 -9.791110e-01 1.811797e+00 -3.135641e+00 -9.330960e-01 1.848272e+00 -2.927260e+00 -9.969660e-01 2.110033e+00 -3.148257e+00 -1.068816e+00 1.919391e+00 -3.487078e+00 -1.110869e+00 1.741854e+00 -3.114181e+00 -7.007050e-01 2.002745e+00 -3.523420e+00 -6.881030e-01 2.236407e+00 -3.220054e+00 -8.920300e-01 2.440236e+00 -3.509564e+00 -1.019417e+00 2.377814e+00 -3.484109e+00 -8.003320e-01 1.857471e+00 -2.950722e+00 -6.699880e-01 1.998099e+00 -3.039955e+00 -7.188550e-01 2.059222e+00 -3.230076e+00 -6.749360e-01 -3.490520e+00 -1.760417e+00 -9.031040e-01 -3.498521e+00 -1.762078e+00 -7.624720e-01 -2.969687e+00 -2.097941e+00 -9.828360e-01 -3.144012e+00 -2.333635e+00 -8.299940e-01 -3.008323e+00 -1.759271e+00 -6.556720e-01 -3.381262e+00 -1.828189e+00 -6.970340e-01 1.947264e+00 -2.443232e+00 -7.325870e-01 -3.302419e+00 -3.017921e+00 -1.128514e+00 -3.016419e+00 -2.765091e+00 -9.666710e-01 -3.316551e+00 -3.168932e+00 -1.121229e+00 -3.217306e+00 -3.187686e+00 -1.075214e+00 -2.729567e+00 -3.602976e+00 -1.377236e+00 -3.098730e+00 -3.539123e+00 -1.252987e+00 -3.031250e+00 -3.095940e+00 -1.106355e+00 -3.294389e+00 -3.166225e+00 -8.428230e-01 -3.006867e+00 -3.575464e+00 -8.302210e-01 -2.749355e+00 -3.272099e+00 -1.034148e+00 -2.583580e+00 -3.514267e+00 -1.278598e+00 -2.593513e+00 -3.536154e+00 -9.424500e-01 -3.166971e+00 -3.002766e+00 -8.121060e-01 -3.040038e+00 -2.780893e+00 -9.146140e-01 -3.900566e+00 1.002504e+00 0.000000e+00 -3.821694e+00 1.477989e+00 -1.076310e-01 -3.675638e+00 1.471512e+00 -6.220100e-02 -1.985123e+00 -2.116661e+00 -3.942560e-01 -2.109684e+00 -2.112345e+00 -5.039970e-01 -2.041110e+00 -2.075348e+00 -5.402010e-01 -2.513187e+00 -2.130859e+00 -4.979200e-01 4.935880e+00 2.051665e+00 -5.728530e-01 4.881855e+00 2.194544e+00 -5.302880e-01 4.472349e+00 2.325133e+00 -4.437640e-01 4.733657e+00 1.227371e+00 -5.928830e-01 5.931474e+00 1.233483e+00 -2.156130e-01 5.998088e+00 1.231874e+00 -1.073340e-01 5.755282e+00 1.004149e+00 -3.075050e-01 5.773410e+00 1.225861e+00 -3.230250e-01 5.851404e+00 9.290080e-01 -1.053620e-01 5.153735e+00 8.315750e-01 -2.183480e-01 5.021254e+00 9.513080e-01 -4.055670e-01 4.366867e+00 1.131136e+00 -6.305810e-01 4.339601e+00 2.592642e+00 -2.588910e-01 4.440760e+00 2.490353e+00 -6.292870e-01 4.636909e+00 2.750501e+00 -8.568590e-01 5.096148e+00 2.759720e+00 -7.747290e-01 4.296769e+00 2.260422e+00 -5.287960e-01 4.444739e+00 2.475795e+00 -8.605150e-01 4.554459e+00 2.511417e+00 -8.505710e-01 4.262798e+00 2.490674e+00 -8.183160e-01 4.138494e+00 2.557838e+00 -6.526510e-01 4.035753e+00 2.399538e+00 -3.439570e-01 4.354196e+00 2.711836e+00 -8.265450e-01 3.928869e+00 2.223919e+00 -6.156830e-01 4.170470e+00 2.458441e+00 -1.241846e+00 4.046673e+00 2.341349e+00 -1.452530e+00 4.094112e+00 1.889281e+00 -5.784350e-01 4.150297e+00 1.820575e+00 -9.170460e-01 4.152166e+00 1.868775e+00 -1.225002e+00 4.369751e+00 2.298204e+00 -9.218940e-01 4.354941e+00 2.064161e+00 -7.788510e-01 4.274886e+00 2.220030e+00 -6.790750e-01 4.278045e+00 1.976660e+00 -5.373510e-01 4.153175e+00 2.084823e+00 -7.996680e-01 4.232341e+00 1.903079e+00 -5.343620e-01 3.758215e+00 2.206683e+00 3.230510e-01 4.120842e+00 6.271410e-01 1.854610e-01 3.650974e+00 1.463740e+00 4.786170e-01 2.668936e+00 2.061820e-01 1.014371e+00 1.859443e+00 1.450393e+00 6.322220e-01 2.081700e+00 2.832490e-01 1.259461e+00 2.141790e+00 -5.530600e-02 1.295565e+00 2.108912e+00 -3.571280e-01 1.259691e+00 2.663024e+00 -1.440487e+00 4.781490e-01 2.079600e+00 -1.473377e+00 6.549970e-01 2.960118e+00 -9.418270e-01 2.847430e-01 2.893096e+00 -1.302168e+00 3.320490e-01 8.573230e-01 9.850010e-01 8.740290e-01 9.603210e-01 -3.080520e-01 1.243133e+00 -1.795480e-01 1.471301e+00 5.445130e-01 1.808600e-02 2.865000e-03 1.523199e+00 -7.194100e-02 -1.346991e+00 1.237379e+00 -7.847000e-02 -1.688266e+00 8.002300e-01 -7.524160e-01 4.381320e-01 1.467349e+00 -5.050450e-01 -9.138270e-01 1.645612e+00 -5.242310e-01 -1.399266e+00 1.313019e+00 -5.640740e-01 -1.789401e+00 8.031910e-01 -1.053434e+00 -3.425530e-01 1.690245e+00 -1.077209e+00 -8.696490e-01 1.633144e+00 -1.582187e+00 1.567542e+00 9.823490e-01 -1.996117e+00 1.387891e+00 1.064104e+00 -2.684930e+00 1.262021e+00 1.110490e+00 -2.765069e+00 -1.561164e+00 1.084313e+00 -3.109047e+00 1.789477e+00 5.653230e-01 -2.930312e+00 1.103580e+00 1.125876e+00 -3.395381e+00 1.726325e+00 5.547700e-01 -3.278080e+00 1.432418e+00 8.539580e-01 -2.901159e+00 -6.195040e-01 1.284058e+00 -2.982676e+00 -1.264649e+00 1.133596e+00 -3.734989e+00 1.589433e+00 2.952020e-01 -3.655148e+00 1.190938e+00 5.736920e-01 -2.462835e+00 -1.305500e+00 9.663950e-01 -1.576196e+00 -1.696317e+00 3.581460e-01 -1.936032e+00 -1.829617e+00 4.889160e-01 -2.126375e+00 -1.866497e+00 5.028950e-01 -2.811581e+00 -1.798210e+00 7.127370e-01 -2.614084e+00 -1.905005e+00 4.049250e-01 -2.503818e+00 -1.909806e+00 4.314560e-01 -3.314868e+00 -1.599081e+00 4.396260e-01 -3.533910e+00 -1.330455e+00 5.682460e-01 -3.569993e+00 -7.319890e-01 6.494350e-01 -3.251210e+00 -9.482050e-01 8.198680e-01 2.158585e+00 -9.558090e-01 1.113647e+00 1.497480e+00 -1.283201e+00 1.088258e+00 1.801794e+00 -1.465464e+00 1.129277e+00 1.953911e+00 -2.462233e+00 8.305940e-01 1.596751e+00 -1.722028e+00 6.846550e-01 1.721745e+00 -3.116887e+00 9.791110e-01 1.848272e+00 -2.927260e+00 9.969660e-01 2.110033e+00 -3.148257e+00 1.068816e+00 2.254361e+00 -3.550931e+00 1.235118e+00 2.113587e+00 -3.011851e+00 8.516480e-01 1.919391e+00 -3.487078e+00 1.110869e+00 1.709173e+00 -3.133145e+00 8.079400e-01 2.002745e+00 -3.523420e+00 6.881030e-01 1.736425e+00 -2.743683e+00 7.368660e-01 2.440236e+00 -3.509564e+00 1.019417e+00 2.377814e+00 -3.484109e+00 8.003320e-01 1.857471e+00 -2.950722e+00 6.699880e-01 1.972647e+00 -2.728848e+00 7.724960e-01 2.059222e+00 -3.230076e+00 6.749360e-01 -3.490520e+00 -1.760417e+00 9.031040e-01 -3.498521e+00 -1.762078e+00 7.624720e-01 -3.177824e+00 -2.279173e+00 1.047580e+00 -3.030605e+00 -2.311222e+00 8.843300e-01 -3.144012e+00 -2.333635e+00 8.299940e-01 -3.316551e+00 -3.168932e+00 1.121229e+00 -2.729567e+00 -3.602976e+00 1.377236e+00 -2.981824e+00 -3.608887e+00 1.346959e+00 -2.770335e+00 -3.208138e+00 1.031045e+00 -2.909010e+00 -3.084482e+00 1.089147e+00 -3.098730e+00 -3.539123e+00 1.252987e+00 -3.031250e+00 -3.095940e+00 1.106355e+00 -3.119570e+00 -3.183977e+00 8.707390e-01 -3.294389e+00 -3.166225e+00 8.428230e-01 -3.006867e+00 -3.575464e+00 8.302210e-01 -2.583580e+00 -3.514267e+00 1.278598e+00 -2.593513e+00 -3.536154e+00 9.424500e-01 -3.166971e+00 -3.002766e+00 8.121060e-01 -3.040038e+00 -2.780893e+00 9.146140e-01 -3.581963e+00 -1.413947e+00 -1.043710e-01 -3.828485e+00 -1.786559e+00 -1.140950e-01 -3.890388e+00 -1.750102e+00 1.761200e-02 -4.029726e+00 -1.043238e+00 1.363050e-01 -3.975337e+00 -5.549610e-01 -1.440700e-02 -4.333831e+00 -1.548255e+00 5.693800e-02 -4.201904e+00 -1.668554e+00 -2.148590e-01 -3.742608e+00 -7.293740e-01 -1.493610e-01 -3.820370e+00 -1.239135e+00 -2.698070e-01 -3.977657e+00 -1.750845e+00 -1.849090e-01 -3.860740e+00 -1.883490e-01 -2.496200e-02 -3.817430e+00 1.503430e-01 4.862900e-02 -3.698925e+00 1.787941e+00 6.664000e-02 -3.469715e+00 1.961504e+00 7.114100e-02 -3.821694e+00 1.477989e+00 1.076310e-01 -3.413372e+00 1.871604e+00 1.423090e-01 -3.703185e+00 1.631820e-01 8.426200e-02 -3.640457e+00 -1.953790e-01 -2.490900e-02 -3.805575e+00 -1.901090e-01 -1.014470e-01 -3.817381e+00 1.503480e-01 -4.872800e-02 -1.933565e+00 -1.993029e+00 4.389020e-01 -2.060902e+00 -2.031698e+00 3.720790e-01 -1.985123e+00 -2.116661e+00 3.942560e-01 -2.144393e+00 -2.117408e+00 4.644890e-01 -2.552923e+00 -2.142304e+00 4.545790e-01 -2.695135e+00 -2.121650e+00 5.080640e-01 -2.527301e+00 -2.106246e+00 5.507350e-01 4.971872e+00 1.924079e+00 5.860260e-01 4.936066e+00 1.737668e+00 5.572760e-01 4.837331e+00 2.235678e+00 4.793320e-01 5.604888e+00 1.546091e+00 1.137730e-01 5.783731e+00 1.341471e+00 2.845900e-01 5.998088e+00 1.231874e+00 1.073340e-01 5.755282e+00 1.004149e+00 3.075050e-01 5.773410e+00 1.225861e+00 3.230250e-01 5.803425e+00 1.260787e+00 2.100020e-01 5.370709e+00 9.802720e-01 3.374320e-01 5.356900e+00 8.511200e-01 1.383240e-01 4.712067e+00 7.705120e-01 3.333930e-01 4.366867e+00 1.131136e+00 6.305810e-01 4.636909e+00 2.750501e+00 8.568590e-01 5.096148e+00 2.759720e+00 7.747290e-01 4.442912e+00 2.343263e+00 5.332200e-01 4.296769e+00 2.260422e+00 5.287960e-01 4.444739e+00 2.475795e+00 8.605150e-01 4.615662e+00 2.633870e+00 9.779860e-01 4.104292e+00 2.537976e+00 5.434370e-01 4.084506e+00 2.570978e+00 2.181680e-01 4.354196e+00 2.711836e+00 8.265450e-01 4.176065e+00 2.636883e+00 6.183840e-01 4.025364e+00 2.359300e+00 4.025970e-01 3.928869e+00 2.223919e+00 6.156830e-01 4.044730e+00 2.254459e+00 7.442090e-01 4.307092e+00 2.367482e+00 8.772020e-01 4.170470e+00 2.458441e+00 1.241846e+00 4.046673e+00 2.341349e+00 1.452530e+00 4.094112e+00 1.889281e+00 5.784350e-01 4.152166e+00 1.868775e+00 1.225002e+00 4.036947e+00 1.992167e+00 1.072561e+00 4.369751e+00 2.298204e+00 9.218940e-01 4.354941e+00 2.064161e+00 7.788510e-01 4.211815e+00 2.282490e+00 1.039137e+00 4.278045e+00 1.976660e+00 5.373510e-01 4.153175e+00 2.084823e+00 7.996680e-01 2.254361e+00 -3.550931e+00 -1.235118e+00 2.254361e+00 -3.550931e+00 -1.235118e+00 2.377814e+00 -3.484109e+00 -8.003320e-01 -2.729567e+00 -3.602976e+00 -1.377236e+00 -2.593513e+00 -3.536154e+00 -9.424500e-01 -3.413372e+00 1.871604e+00 -1.423090e-01 -3.380836e+00 1.819691e+00 -1.232730e-01 -3.675638e+00 1.471512e+00 -6.220100e-02 -3.348274e+00 1.767738e+00 -8.500000e-05 5.895899e+00 1.182730e+00 -1.647050e-01 4.170470e+00 2.458441e+00 -1.241846e+00 4.046673e+00 2.341349e+00 -1.452530e+00 4.152166e+00 1.868775e+00 -1.225002e+00 4.369751e+00 2.298204e+00 -9.218940e-01 4.354941e+00 2.064161e+00 -7.788510e-01 -2.176551e+00 -1.873816e+00 4.457820e-01 2.254361e+00 -3.550931e+00 1.235118e+00 2.377814e+00 -3.484109e+00 8.003320e-01 -3.008323e+00 -1.759271e+00 6.556720e-01 -2.729567e+00 -3.602976e+00 1.377236e+00 -2.593513e+00 -3.536154e+00 9.424500e-01 -3.581963e+00 -1.413947e+00 -1.043710e-01 -3.828485e+00 -1.786559e+00 -1.140950e-01 -3.828485e+00 -1.786559e+00 -1.140950e-01 -3.890388e+00 -1.750102e+00 1.761200e-02 -4.445835e+00 -2.015127e+00 1.897020e-01 -4.445835e+00 -2.015127e+00 1.897020e-01 -4.445835e+00 -2.015127e+00 1.897020e-01 -3.820370e+00 -1.239135e+00 -2.698070e-01 -3.860740e+00 -1.883490e-01 -2.496200e-02 -3.817430e+00 1.503430e-01 4.862900e-02 -3.817381e+00 1.503480e-01 -4.872800e-02 5.895899e+00 1.182730e+00 1.647050e-01 4.266263e+00 2.421651e+00 1.054799e+00 4.170470e+00 2.458441e+00 1.241846e+00 4.046673e+00 2.341349e+00 1.452530e+00 4.150297e+00 1.820575e+00 9.170460e-01 4.152166e+00 1.868775e+00 1.225002e+00 4.082988e+00 2.175805e+00 1.450930e+00 4.369751e+00 2.298204e+00 9.218940e-01 4.354941e+00 2.064161e+00 7.788510e-01 4.278045e+00 1.976660e+00 5.373510e-01 -2.583580e+00 -3.514267e+00 -1.278598e+00 -2.583580e+00 -3.514267e+00 1.278598e+00 -3.330406e+00 -3.185190e+00 9.500580e-01 -3.316551e+00 -3.168932e+00 -1.121229e+00 -3.330406e+00 -3.185190e+00 -9.500580e-01 -4.309744e+00 -1.743848e+00 1.503290e-01 -4.405365e+00 -1.921443e+00 1.929780e-01 2.113587e+00 -3.011851e+00 -8.516480e-01 2.236407e+00 -3.220054e+00 8.920300e-01 4.150297e+00 1.820575e+00 9.170460e-01 -2.749355e+00 -3.272099e+00 1.034148e+00 -3.294389e+00 -3.166225e+00 8.428230e-01 -3.294389e+00 -3.166225e+00 -8.428230e-01 1.721745e+00 -3.116887e+00 9.791110e-01 1.721745e+00 -3.116887e+00 9.791110e-01 1.811797e+00 -3.135641e+00 9.330960e-01 1.721745e+00 -3.116887e+00 -9.791110e-01 1.721745e+00 -3.116887e+00 -9.791110e-01 1.811797e+00 -3.135641e+00 -9.330960e-01 -3.316551e+00 -3.168932e+00 -1.121229e+00 -3.217306e+00 -3.187686e+00 -1.075214e+00 -3.316551e+00 -3.168932e+00 1.121229e+00 -3.217306e+00 -3.187686e+00 1.075214e+00 -3.166971e+00 -3.002766e+00 -8.121060e-01 -3.166971e+00 -3.002766e+00 8.121060e-01 -3.302419e+00 -3.017921e+00 -1.128514e+00 1.972647e+00 -2.728848e+00 -7.724960e-01 4.636909e+00 2.750501e+00 -8.568590e-01 4.554459e+00 2.511417e+00 -8.505710e-01 5.096148e+00 2.759720e+00 -7.747290e-01 1.741854e+00 -3.114181e+00 -7.007050e-01 1.741854e+00 -3.114181e+00 -7.007050e-01 1.741854e+00 -3.114181e+00 7.007050e-01 1.963153e+00 -1.588839e+00 6.128610e-01 1.963153e+00 -1.588839e+00 -6.128610e-01 1.963153e+00 -1.588839e+00 -6.128610e-01 -3.821694e+00 1.477989e+00 1.076310e-01 4.094112e+00 1.889281e+00 5.784350e-01 4.094112e+00 1.889281e+00 -5.784350e-01 -2.126375e+00 -1.866497e+00 5.028950e-01 -2.060902e+00 -2.031698e+00 3.720790e-01 4.936066e+00 1.737668e+00 5.572760e-01 4.842883e+00 2.021333e+00 6.380250e-01 -3.294389e+00 -3.166225e+00 -8.428230e-01 4.728963e+00 2.036146e+00 6.143070e-01 4.751519e+00 2.039658e+00 6.341340e-01 -3.040038e+00 -2.780893e+00 9.146140e-01 4.104292e+00 2.537976e+00 5.434370e-01 4.104292e+00 2.537976e+00 5.434370e-01 4.636909e+00 2.750501e+00 8.568590e-01 4.444739e+00 2.475795e+00 8.605150e-01 4.444739e+00 2.475795e+00 8.605150e-01 5.096148e+00 2.759720e+00 7.747290e-01 5.096148e+00 2.759720e+00 7.747290e-01 -3.008323e+00 -1.759271e+00 -6.556720e-01 -3.335942e+00 -1.365685e+00 -7.245900e-01 4.444739e+00 2.475795e+00 -8.605150e-01 4.307092e+00 2.367482e+00 8.772020e-01 4.266263e+00 2.421651e+00 1.054799e+00 4.036947e+00 1.992167e+00 1.072561e+00 -2.147113e+00 -1.903198e+00 -3.624620e-01 -2.527301e+00 -2.106246e+00 5.507350e-01 -2.527301e+00 -2.106246e+00 5.507350e-01 -2.462835e+00 -1.305500e+00 9.663950e-01 -2.527301e+00 -2.106246e+00 -5.507350e-01 -2.503818e+00 -1.909806e+00 -4.314560e-01 -2.513187e+00 -2.130859e+00 -4.979200e-01 -2.462835e+00 -1.305500e+00 -9.663950e-01 5.783731e+00 1.341471e+00 2.845900e-01 5.783731e+00 1.341471e+00 2.845900e-01 -1.933565e+00 -1.993029e+00 4.389020e-01 5.783731e+00 1.341471e+00 -2.845900e-01 5.783731e+00 1.341471e+00 -2.845900e-01 4.296769e+00 2.260422e+00 -5.287960e-01 4.296769e+00 2.260422e+00 -5.287960e-01 4.296769e+00 2.260422e+00 -5.287960e-01 4.274886e+00 2.220030e+00 -6.790750e-01 4.274886e+00 2.220030e+00 -6.790750e-01 5.177545e+00 1.932323e+00 2.140000e-04 4.881855e+00 2.194544e+00 -5.302880e-01 1.734568e+00 -2.965876e+00 -9.863960e-01 -3.675638e+00 1.471512e+00 -6.220100e-02 -2.770335e+00 -3.208138e+00 -1.031045e+00 -2.770335e+00 -3.208138e+00 -1.031045e+00 -2.770335e+00 -3.208138e+00 1.031045e+00 -3.031250e+00 -3.095940e+00 1.106355e+00 4.025364e+00 2.359300e+00 4.025970e-01 4.554459e+00 2.511417e+00 -8.505710e-01 4.554459e+00 2.511417e+00 -8.505710e-01 4.138494e+00 2.557838e+00 -6.526510e-01 4.440760e+00 2.490353e+00 -6.292870e-01 -2.629199e+00 -2.138983e+00 -4.362270e-01 -2.629199e+00 -2.138983e+00 -4.362270e-01 -2.614084e+00 -1.905005e+00 -4.049250e-01 -2.699966e+00 -1.850397e+00 -5.700700e-01 -2.126375e+00 -1.866497e+00 -5.028950e-01 -2.144393e+00 -2.117408e+00 -4.644890e-01 -3.821694e+00 1.477989e+00 1.076310e-01 4.472349e+00 2.325133e+00 -4.437640e-01 -3.330406e+00 -3.185190e+00 9.500580e-01 -3.144012e+00 -2.333635e+00 8.299940e-01 -3.144012e+00 -2.333635e+00 8.299940e-01 -3.030605e+00 -2.311222e+00 8.843300e-01 -3.016419e+00 -2.765091e+00 -9.666710e-01 3.928869e+00 2.223919e+00 -6.156830e-01 4.150297e+00 1.820575e+00 9.170460e-01 4.094112e+00 1.889281e+00 -5.784350e-01 4.232341e+00 1.903079e+00 -5.343620e-01 4.059885e+00 1.860460e+00 -4.700870e-01 5.773410e+00 1.225861e+00 3.230250e-01 4.150297e+00 1.820575e+00 -9.170460e-01 4.354941e+00 2.064161e+00 -7.788510e-01 5.895899e+00 1.182730e+00 -1.647050e-01 -1.985123e+00 -2.116661e+00 -3.942560e-01 -3.821694e+00 1.477989e+00 1.076310e-01 -3.900566e+00 1.002504e+00 0.000000e+00 1.734568e+00 -2.965876e+00 9.863960e-01 1.736425e+00 -2.743683e+00 7.368660e-01 1.721745e+00 -3.116887e+00 9.791110e-01 1.721745e+00 -3.116887e+00 9.791110e-01 1.848272e+00 -2.927260e+00 9.969660e-01 1.953911e+00 -2.462233e+00 8.305940e-01 1.857471e+00 -2.950722e+00 6.699880e-01 -2.695135e+00 -2.121650e+00 -5.080640e-01 1.857471e+00 -2.950722e+00 -6.699880e-01 -3.975337e+00 -5.549610e-01 -1.440700e-02 -3.703185e+00 1.631820e-01 8.426200e-02 3.247406e+00 2.073333e+00 0.000000e+00 4.936066e+00 1.737668e+00 5.572760e-01 4.369751e+00 2.298204e+00 9.218940e-01 4.082988e+00 2.175805e+00 1.450930e+00 4.082988e+00 2.175805e+00 1.450930e+00 4.211815e+00 2.282490e+00 1.039137e+00 4.025364e+00 2.359300e+00 4.025970e-01 3.928869e+00 2.223919e+00 6.156830e-01 4.036947e+00 1.992167e+00 1.072561e+00 4.307092e+00 2.367482e+00 8.772020e-01 -3.031250e+00 -3.095940e+00 -1.106355e+00 -3.016419e+00 -2.765091e+00 -9.666710e-01 -3.177824e+00 -2.279173e+00 1.047580e+00 -2.770335e+00 -3.208138e+00 1.031045e+00 -2.699966e+00 -1.850397e+00 5.700700e-01 -2.699966e+00 -1.850397e+00 5.700700e-01 -2.462835e+00 -1.305500e+00 9.663950e-01 -2.811581e+00 -1.798210e+00 7.127370e-01 -2.614084e+00 -1.905005e+00 4.049250e-01 -2.462835e+00 -1.305500e+00 -9.663950e-01 -2.699966e+00 -1.850397e+00 -5.700700e-01 -2.811581e+00 -1.798210e+00 -7.127370e-01 -2.981187e+00 -2.142718e+00 -8.583530e-01 -2.126375e+00 -1.866497e+00 -5.028950e-01 -2.699966e+00 -1.850397e+00 5.700700e-01 -2.527301e+00 -2.106246e+00 5.507350e-01 4.354941e+00 2.064161e+00 7.788510e-01 4.712067e+00 7.705120e-01 -3.333930e-01 4.712067e+00 7.705120e-01 -3.333930e-01 4.094112e+00 1.889281e+00 5.784350e-01 1.848272e+00 -2.927260e+00 9.969660e-01 -3.698925e+00 1.787941e+00 6.664000e-02 -3.675638e+00 1.471512e+00 -6.220100e-02 -2.981187e+00 -2.142718e+00 -8.583530e-01 -3.030605e+00 -2.311222e+00 -8.843300e-01 -2.981824e+00 -3.608887e+00 -1.346959e+00 -3.098730e+00 -3.539123e+00 -1.252987e+00 -3.098730e+00 -3.539123e+00 -1.252987e+00 -3.031250e+00 -3.095940e+00 -1.106355e+00 -3.042899e+00 -3.193907e+00 -1.207296e+00 -2.981824e+00 -3.608887e+00 1.346959e+00 -3.098730e+00 -3.539123e+00 1.252987e+00 4.138494e+00 2.557838e+00 -6.526510e-01 4.354196e+00 2.711836e+00 -8.265450e-01 4.296769e+00 2.260422e+00 5.287960e-01 4.354941e+00 2.064161e+00 7.788510e-01 4.044730e+00 2.254459e+00 7.442090e-01 4.354941e+00 2.064161e+00 7.788510e-01 2.960118e+00 -9.418270e-01 2.847430e-01 4.296769e+00 2.260422e+00 5.287960e-01 -2.144393e+00 -2.117408e+00 4.644890e-01 -2.144393e+00 -2.117408e+00 4.644890e-01 -2.060902e+00 -2.031698e+00 3.720790e-01 -2.048681e+00 -2.165282e+00 4.508430e-01 4.472349e+00 2.325133e+00 -4.437640e-01 4.296769e+00 2.260422e+00 -5.287960e-01 4.296769e+00 2.260422e+00 -5.287960e-01 -3.413372e+00 1.871604e+00 1.423090e-01 -3.821694e+00 1.477989e+00 1.076310e-01 1.972647e+00 -2.728848e+00 -7.724960e-01 1.721745e+00 -3.116887e+00 -9.791110e-01 -3.392806e+00 1.769365e+00 0.000000e+00 -3.413372e+00 1.871604e+00 -1.423090e-01 -1.985123e+00 -2.116661e+00 -3.942560e-01 -2.124030e+00 -2.137733e+00 -4.068530e-01 -2.147113e+00 -1.903198e+00 -3.624620e-01 -2.144393e+00 -2.117408e+00 -4.644890e-01 4.035753e+00 2.399538e+00 -3.439570e-01 4.025364e+00 2.359300e+00 4.025970e-01 4.339601e+00 2.592642e+00 -2.588910e-01 4.339601e+00 2.592642e+00 -2.588910e-01 4.138494e+00 2.557838e+00 -6.526510e-01 5.998088e+00 1.231874e+00 1.073340e-01 5.908842e+00 1.388968e+00 1.026000e-03 5.908842e+00 1.388968e+00 1.026000e-03 5.998088e+00 1.231874e+00 -1.073340e-01 5.998088e+00 1.231874e+00 -1.073340e-01 5.423860e-01 -1.882270e-01 -1.370375e+00 5.423860e-01 -1.882270e-01 -1.370375e+00 5.021254e+00 9.513080e-01 -4.055670e-01 5.021254e+00 9.513080e-01 -4.055670e-01 5.783731e+00 1.341471e+00 -2.845900e-01 5.370709e+00 9.802720e-01 -3.374320e-01 3.758215e+00 2.206683e+00 3.230510e-01 9.603210e-01 -3.080520e-01 1.243133e+00 4.863709e+00 2.722151e+00 9.366600e-01 -2.695135e+00 -2.121650e+00 5.080640e-01 -2.552923e+00 -2.142304e+00 4.545790e-01 -2.552923e+00 -2.142304e+00 4.545790e-01 1.848272e+00 -2.927260e+00 -9.969660e-01 -3.469715e+00 1.961504e+00 7.114100e-02 -3.302419e+00 -3.017921e+00 -1.128514e+00 -3.413372e+00 1.871604e+00 -1.423090e-01 -3.698925e+00 1.787941e+00 6.664000e-02 -3.498521e+00 -1.762078e+00 7.624720e-01 2.110033e+00 -3.148257e+00 -1.068816e+00 -3.251210e+00 -9.482050e-01 8.198680e-01 4.150297e+00 1.820575e+00 -9.170460e-01 5.356900e+00 8.511200e-01 1.383240e-01 4.354196e+00 2.711836e+00 8.265450e-01 -2.969687e+00 -2.097941e+00 -9.828360e-01 4.881855e+00 2.194544e+00 -5.302880e-01 5.177545e+00 1.932323e+00 2.140000e-04 5.604888e+00 1.546091e+00 1.137730e-01 -3.008323e+00 -1.759271e+00 6.556720e-01 -3.251210e+00 -9.482050e-01 8.198680e-01 -3.314868e+00 -1.599081e+00 4.396260e-01 -3.302419e+00 -3.017921e+00 1.128514e+00 -2.909010e+00 -3.084482e+00 1.089147e+00 -3.177824e+00 -2.279173e+00 1.047580e+00 1.848272e+00 -2.927260e+00 -9.969660e-01 1.811797e+00 -3.135641e+00 -9.330960e-01 1.596751e+00 -1.722028e+00 6.846550e-01 -7.847000e-02 -1.688266e+00 8.002300e-01 1.497480e+00 -1.283201e+00 1.088258e+00 2.440236e+00 -3.509564e+00 -1.019417e+00 4.094112e+00 1.889281e+00 -5.784350e-01 2.110033e+00 -3.148257e+00 1.068816e+00 2.254361e+00 -3.550931e+00 1.235118e+00 2.440236e+00 -3.509564e+00 1.019417e+00 4.262798e+00 2.490674e+00 -8.183160e-01 -3.166971e+00 -3.002766e+00 -8.121060e-01 -3.166971e+00 -3.002766e+00 -8.121060e-01 -3.217306e+00 -3.187686e+00 -1.075214e+00 -2.981187e+00 -2.142718e+00 -8.583530e-01 -3.302419e+00 -3.017921e+00 -1.128514e+00 -3.490520e+00 -1.760417e+00 -9.031040e-01 -2.982676e+00 -1.264649e+00 -1.133596e+00 -2.982676e+00 -1.264649e+00 -1.133596e+00 -2.969687e+00 -2.097941e+00 -9.828360e-01 -2.147113e+00 -1.903198e+00 -3.624620e-01 -3.490520e+00 -1.760417e+00 9.031040e-01 4.444739e+00 2.475795e+00 8.605150e-01 4.296769e+00 2.260422e+00 5.287960e-01 -3.698925e+00 1.787941e+00 6.664000e-02 5.153735e+00 8.315750e-01 -2.183480e-01 1.497480e+00 -1.283201e+00 -1.088258e+00 1.640095e+00 -6.745560e-01 -1.246804e+00 2.079600e+00 -1.473377e+00 -6.549970e-01 1.991892e+00 -1.748400e+00 -6.527700e-01 4.733657e+00 1.227371e+00 -5.928830e-01 4.733657e+00 1.227371e+00 -5.928830e-01 5.021254e+00 9.513080e-01 -4.055670e-01 5.153735e+00 8.315750e-01 -2.183480e-01 4.615662e+00 2.633870e+00 9.779860e-01 -3.098730e+00 -3.539123e+00 -1.252987e+00 -3.217306e+00 -3.187686e+00 -1.075214e+00 -1.985123e+00 -2.116661e+00 -3.942560e-01 -2.041110e+00 -2.075348e+00 -5.402010e-01 -2.041110e+00 -2.075348e+00 -5.402010e-01 -3.395381e+00 1.726325e+00 -5.547700e-01 5.998088e+00 1.231874e+00 1.073340e-01 5.783731e+00 1.341471e+00 2.845900e-01 -3.119570e+00 -3.183977e+00 8.707390e-01 -3.294389e+00 -3.166225e+00 8.428230e-01 1.919391e+00 -3.487078e+00 -1.110869e+00 -3.217306e+00 -3.187686e+00 1.075214e+00 -3.890388e+00 -1.750102e+00 1.761200e-02 2.079600e+00 -1.473377e+00 6.549970e-01 -3.006867e+00 -3.575464e+00 8.302210e-01 -3.119570e+00 -3.183977e+00 8.707390e-01 1.947264e+00 -2.443232e+00 7.325870e-01 1.736425e+00 -2.743683e+00 7.368660e-01 1.596751e+00 -1.722028e+00 6.846550e-01 -4.333831e+00 -1.548255e+00 5.693800e-02 -3.703185e+00 1.631820e-01 8.426200e-02 -4.029726e+00 -1.043238e+00 1.363050e-01 4.084506e+00 2.570978e+00 2.181680e-01 4.104292e+00 2.537976e+00 5.434370e-01 4.442912e+00 2.343263e+00 5.332200e-01 -3.006867e+00 -3.575464e+00 8.302210e-01 -2.981824e+00 -3.608887e+00 1.346959e+00 5.755282e+00 1.004149e+00 -3.075050e-01 4.615662e+00 2.633870e+00 9.779860e-01 4.354196e+00 2.711836e+00 8.265450e-01 5.021254e+00 9.513080e-01 -4.055670e-01 5.021254e+00 9.513080e-01 -4.055670e-01 5.773410e+00 1.225861e+00 -3.230250e-01 5.895899e+00 1.182730e+00 -1.647050e-01 -2.462835e+00 -1.305500e+00 9.663950e-01 -2.462835e+00 -1.305500e+00 9.663950e-01 -5.242310e-01 -1.399266e+00 1.313019e+00 -2.462835e+00 -1.305500e+00 -9.663950e-01 -2.462835e+00 -1.305500e+00 -9.663950e-01 1.811797e+00 -3.135641e+00 9.330960e-01 1.857471e+00 -2.950722e+00 6.699880e-01 4.120842e+00 6.271410e-01 1.854610e-01 4.232341e+00 1.903079e+00 5.343620e-01 1.497480e+00 -1.283201e+00 1.088258e+00 1.734568e+00 -2.965876e+00 9.863960e-01 1.801794e+00 -1.465464e+00 1.129277e+00 1.596751e+00 -1.722028e+00 -6.846550e-01 1.596751e+00 -1.722028e+00 -6.846550e-01 1.947264e+00 -2.443232e+00 -7.325870e-01 1.953911e+00 -2.462233e+00 -8.305940e-01 -3.335942e+00 -1.365685e+00 -7.245900e-01 -3.533910e+00 -1.330455e+00 -5.682460e-01 -3.166971e+00 -3.002766e+00 -8.121060e-01 -3.006867e+00 -3.575464e+00 -8.302210e-01 -2.749355e+00 -3.272099e+00 -1.034148e+00 3.247406e+00 2.073333e+00 0.000000e+00 -2.503818e+00 -1.909806e+00 4.314560e-01 -3.977657e+00 -1.750845e+00 -1.849090e-01 -3.977657e+00 -1.750845e+00 -1.849090e-01 -3.742608e+00 -7.293740e-01 -1.493610e-01 -3.742608e+00 -7.293740e-01 -1.493610e-01 -3.820370e+00 -1.239135e+00 -2.698070e-01 -3.820370e+00 -1.239135e+00 -2.698070e-01 -3.860740e+00 -1.883490e-01 -2.496200e-02 -3.817381e+00 1.503480e-01 -4.872800e-02 2.110033e+00 -3.148257e+00 -1.068816e+00 2.254361e+00 -3.550931e+00 -1.235118e+00 2.663024e+00 -1.440487e+00 4.781490e-01 4.354196e+00 2.711836e+00 -8.265450e-01 4.354196e+00 2.711836e+00 -8.265450e-01 4.636909e+00 2.750501e+00 -8.568590e-01 5.096148e+00 2.759720e+00 -7.747290e-01 8.573230e-01 9.850010e-01 8.740290e-01 597 58 -11 11 19 -597 19 20 -597 597 20 -64 11 58 -14 642 27 -32 27 28 -32 50 641 -32 641 50 -51 51 397 -641 39 478 -52 39 52 -480 5 57 -54 54 397 -5 5 397 -392 51 392 -397 178 5 -50 392 50 -5 6 47 -655 5 6 -57 56 57 -6 655 44 -654 61 72 -67 569 66 -65 291 614 -669 69 548 -554 570 548 -69 67 72 -68 61 67 -412 363 348 -350 570 69 -71 73 670 -72 68 72 -670 73 72 -292 70 570 -75 570 76 -75 72 358 -456 71 77 -570 76 570 -77 77 72 -456 292 77 -71 72 77 -292 574 74 -71 290 71 -69 39 80 -45 583 587 -588 654 45 -79 78 79 -45 584 481 -86 481 584 -585 81 493 -53 83 81 -53 83 386 -79 387 79 -386 81 83 -344 79 344 -83 79 78 -344 550 344 -78 597 61 -60 519 62 -60 63 62 -518 349 518 -62 412 60 -61 71 290 -574 362 518 -349 518 84 -63 653 366 -651 650 652 -339 597 60 -58 58 60 -595 63 22 -595 598 18 -367 62 63 -595 60 62 -595 19 11 -10 127 659 -528 10 575 -117 11 13 -12 1 10 -12 11 12 -10 117 487 -10 575 10 -1 12 2 -1 13 2 -12 13 11 -14 58 16 -14 13 14 -16 2 13 -15 2 15 -25 25 15 -23 22 30 -23 3 2 -24 25 33 -24 33 25 -29 29 25 -34 25 30 -34 31 30 -26 31 26 -642 27 642 -26 37 36 -33 29 38 -37 34 38 -29 38 34 -35 31 478 -35 595 17 -16 15 13 -16 17 23 -16 58 595 -16 64 572 -63 64 20 -572 21 596 -59 572 22 -63 21 59 -537 22 23 -595 596 21 -536 23 15 -16 25 24 -2 23 30 -25 30 22 -26 26 22 -27 22 28 -27 33 3 -24 29 37 -33 34 30 -35 35 30 -31 478 38 -35 33 36 -3 36 40 -3 37 40 -36 40 37 -42 42 37 -38 7 3 -40 42 41 -40 45 478 -39 521 609 -4 41 47 -43 41 48 -47 520 43 -46 46 43 -47 38 44 -48 45 44 -478 38 478 -44 48 42 -38 41 42 -48 41 43 -40 7 40 -551 43 551 -40 585 586 -481 39 480 -492 492 559 -39 493 81 -435 497 357 -470 414 497 -470 357 88 -87 496 91 -89 496 89 -494 495 469 -498 88 497 -604 85 335 -336 92 351 -352 605 90 -657 88 357 -497 89 94 -95 89 91 -94 95 94 -96 375 582 -97 658 98 -581 657 580 -605 96 656 -93 94 656 -96 332 294 -657 293 657 -90 657 293 -332 415 435 -98 81 344 -355 581 98 -81 435 81 -98 654 44 -45 655 48 -44 47 48 -655 236 99 -245 100 244 -447 244 100 -101 100 552 -101 552 295 -101 298 491 -296 50 606 -51 607 51 -606 103 482 -104 428 589 -427 50 392 -445 399 55 -396 49 398 -479 455 425 -426 397 54 -105 424 105 -54 525 102 -524 608 522 -523 479 398 -423 108 560 -106 107 561 -106 106 109 -140 561 562 -106 430 255 -411 514 108 -106 514 106 -140 109 106 -562 562 116 -109 533 540 -256 110 540 -535 533 535 -540 258 111 -9 111 258 -114 111 114 -631 634 113 -444 116 562 -636 444 113 -404 534 299 -403 541 631 -114 263 541 -114 541 263 -538 258 259 -114 263 114 -259 115 539 -557 264 486 -594 486 600 -594 601 602 -599 109 487 -117 140 117 -438 255 410 -411 421 529 -255 128 126 -120 421 255 -430 430 119 -421 120 126 -124 119 419 -421 513 515 -422 515 123 -422 420 422 -123 125 673 -388 388 405 -125 388 673 -121 674 675 -672 502 579 -501 407 501 -579 526 501 -407 528 530 -127 406 436 -127 659 127 -436 440 129 -408 406 409 -436 301 137 -300 301 370 -137 370 301 -134 135 131 -130 122 138 -137 303 137 -136 137 303 -300 136 137 -138 135 443 -139 135 139 -131 139 443 -556 304 439 -442 637 112 -635 140 109 -117 302 139 -133 131 139 -302 129 440 -459 359 360 -361 442 439 -132 82 54 -57 82 480 -54 52 54 -480 507 188 -617 507 617 -152 152 617 -671 152 19 -507 19 152 -20 152 149 -20 149 150 -20 638 162 -158 162 395 -178 28 22 -158 158 162 -28 32 28 -162 32 162 -50 178 50 -162 395 179 -178 395 305 -179 183 305 -395 168 182 -639 168 181 -182 5 182 -184 182 5 -183 5 305 -183 247 305 -5 246 5 -178 185 184 -187 563 555 -565 46 47 -186 186 47 -185 6 185 -47 5 184 -6 185 6 -184 187 186 -185 186 176 -46 340 453 -489 194 198 -195 195 198 -196 643 198 -194 576 340 -489 643 200 -198 204 199 -193 201 204 -193 347 452 -346 202 340 -577 577 340 -576 202 203 -340 454 345 -364 340 206 -205 621 205 -206 200 643 -206 644 206 -643 200 206 -203 203 206 -340 578 200 -307 306 198 -200 207 168 -174 187 208 -174 590 174 -208 207 209 -168 209 434 -168 211 308 -210 553 308 -211 564 308 -553 334 553 -211 431 566 -208 566 590 -208 191 617 -190 448 649 -647 648 189 -449 449 189 -192 489 453 -450 190 451 -191 200 578 -306 197 620 -191 621 622 -205 622 365 -205 191 620 -617 617 188 -190 188 573 -190 192 189 -154 142 143 -151 19 10 -507 659 527 -528 282 143 -142 143 282 -659 141 659 -282 659 141 -527 143 145 -144 659 145 -143 142 151 -486 10 487 -507 144 151 -143 188 151 -148 147 148 -151 146 147 -144 151 144 -147 144 145 -146 145 659 -2 2 156 -153 676 160 -154 3 155 -2 155 159 -156 159 163 -156 156 163 -160 640 157 -160 161 638 -157 158 157 -638 155 3 -165 165 166 -155 159 166 -163 640 164 -639 148 543 -573 2 153 -145 153 146 -145 147 146 -153 543 148 -153 147 153 -148 148 573 -188 150 571 -20 20 571 -572 154 158 -192 156 2 -155 676 156 -160 160 157 -154 154 157 -158 159 155 -166 163 164 -160 164 640 -160 639 164 -163 165 3 -169 166 165 -169 169 167 -166 163 166 -167 167 170 -163 239 3 -7 239 169 -3 169 170 -167 174 168 -639 241 520 -171 172 171 -176 172 176 -187 173 187 -174 175 171 -46 520 46 -171 175 176 -171 175 46 -176 176 186 -187 174 639 -173 163 173 -639 170 173 -163 187 173 -170 172 187 -170 170 169 -172 171 172 -169 171 169 -239 168 434 -181 434 209 -432 567 209 -207 472 568 -216 353 615 -207 500 213 -417 500 214 -213 615 217 -218 219 212 -343 629 630 -619 354 619 -630 224 220 -334 207 218 -567 615 218 -207 213 222 -342 213 342 -417 222 310 -342 225 612 -613 342 612 -225 618 612 -310 310 612 -342 333 221 -223 309 499 -221 416 567 -218 221 333 -309 215 378 -471 211 224 -334 356 433 -378 471 378 -433 226 228 -227 624 229 -226 229 228 -226 337 338 -228 228 229 -337 229 231 -337 242 230 -625 231 315 -337 230 232 -625 625 232 -623 623 232 -316 232 663 -235 664 234 -661 232 235 -316 368 447 -320 319 320 -447 447 368 -100 100 368 -593 7 551 -490 368 320 -458 238 429 -549 624 226 -446 517 491 -516 413 240 -243 298 516 -491 101 663 -244 237 667 -230 244 663 -232 321 244 -232 667 232 -230 233 297 -311 666 233 -311 666 311 -313 318 312 -314 662 665 -616 616 317 -662 458 320 -457 667 668 -232 178 179 -248 510 248 -179 179 371 -510 249 180 -511 177 394 -660 475 474 -484 250 473 -477 183 393 -182 546 182 -393 402 512 -372 509 372 -512 473 250 -251 545 547 -252 545 252 -483 265 373 -376 253 255 -376 255 253 -410 460 410 -253 376 508 -265 373 262 -8 508 376 -255 265 262 -373 262 256 -8 262 260 -256 533 256 -400 611 531 -532 256 260 -400 322 401 -441 261 610 -257 263 259 -262 262 259 -441 263 264 -594 262 264 -263 264 262 -265 486 264 -142 265 142 -264 508 142 -265 646 488 -645 529 626 -255 558 266 -379 627 628 -626 381 270 -380 270 381 -385 626 628 -255 628 627 -383 255 268 -508 508 268 -591 382 384 -603 632 274 -592 465 592 -274 544 603 -384 274 275 -465 273 276 -272 276 273 -118 267 633 -271 418 542 -505 418 505 -506 506 269 -418 279 504 -278 389 277 -467 281 390 -391 468 284 -323 281 280 -390 281 391 -327 282 466 -141 282 327 -466 466 327 -391 285 324 -325 325 328 -285 503 330 -331 504 279 -285 279 324 -285 289 329 -464 329 289 -485 287 461 -463 485 289 -341 464 437 -289 441 259 -322 254 374 -377 369 286 -326 283 437 -464 462 283 -464 369 288 -286 184 182 -181 476 210 -308 mayavi-4.1.0/mayavi/tests/data/prism.neu0000644000175100001440000000335211674464502021203 0ustar ischnellusers00000000000000 CONTROL INFO 1.2.1 ** GAMBIT NEUTRAL FILE Example PROGRAM: Gambit VERSION: 1.2.1 4 Jan 2000 13:07:49 NUMNP NELEM NGRPS NBSETS NDFCD NDFVL 10 4 2 1 3 3 ENDOFSECTION NODAL COORDINATES 1.2.1 1 0.0000000000e+00 0.0000000000e+00 0.0000000000e+00 2 1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 3 0.0000000000e+00 1.0000000000e+00 0.0000000000e+00 4 -1.0000000000e+00 0.0000000000e+00 0.0000000000e+00 5 0.0000000000e+00 -1.0000000000e+00 0.0000000000e+00 6 0.0000000000e+00 0.0000000000e+00 1.0000000000e+00 7 1.0000000000e+00 0.0000000000e+00 1.0000000000e+00 8 0.0000000000e+00 1.0000000000e+00 1.0000000000e+00 9 -1.0000000000e+00 0.0000000000e+00 1.0000000000e+00 10 0.0000000000e+00 -1.0000000000e+00 1.0000000000e+00 ENDOFSECTION ELEMENTS/CELLS 1.2.1 1 5 6 9 6 10 4 1 5 2 5 6 10 6 7 5 1 2 3 5 6 8 6 9 3 1 4 4 5 6 7 6 8 2 1 3 ENDOFSECTION ELEMENT GROUP 1.2.1 GROUP: 1 ELEMENTS: 2 MATERIAL: 2 NFLAGS: 1 fluid 0 1 2 ENDOFSECTION ELEMENT GROUP 1.2.1 GROUP: 2 ELEMENTS: 2 MATERIAL: 4 NFLAGS: 1 solid 0 3 4 ENDOFSECTION BOUNDARY CONDITIONS 1.2.1 node.2 0 4 0 24 2 5 7 10 ENDOFSECTION mayavi-4.1.0/mayavi/tests/data/clown.facet0000644000175100001440000007127411674464502021476 0ustar ischnellusers00000000000000FACET FILE FROM VTK 4 Element0x806a640 0 86 0 0 0 0 0.5 0 0 -0.5 0.216942 0 0.450484 0.390916 0 0.311745 0.487464 0 0.11126 0.487464 0 -0.11126 0.390916 0 -0.311745 0.216942 0 -0.450484 0.195458 0.0941276 0.450484 0.352203 0.169612 0.311745 0.43919 0.211503 0.11126 0.43919 0.211503 -0.11126 0.352203 0.169612 -0.311745 0.195458 0.0941275 -0.450484 0.135261 0.169612 0.450484 0.243732 0.30563 0.311745 0.303929 0.381115 0.11126 0.303929 0.381115 -0.11126 0.243732 0.30563 -0.311745 0.135261 0.169612 -0.450484 0.0482741 0.211503 0.450484 0.0869869 0.381115 0.311745 0.108471 0.475242 0.11126 0.108471 0.475242 -0.11126 0.0869869 0.381115 -0.311745 0.0482741 0.211503 -0.450484 -0.0482741 0.211503 0.450484 -0.086987 0.381115 0.311745 -0.108471 0.475242 0.11126 -0.108471 0.475242 -0.11126 -0.086987 0.381115 -0.311745 -0.0482741 0.211503 -0.450484 -0.135261 0.169612 0.450484 -0.243732 0.30563 0.311745 -0.303929 0.381115 0.11126 -0.303929 0.381115 -0.11126 -0.243732 0.30563 -0.311745 -0.135261 0.169612 -0.450484 -0.195458 0.0941275 0.450484 -0.352203 0.169612 0.311745 -0.43919 0.211503 0.11126 -0.43919 0.211503 -0.11126 -0.352203 0.169612 -0.311745 -0.195458 0.0941275 -0.450484 -0.216942 -1.89657e-08 0.450484 -0.390916 -3.41749e-08 0.311745 -0.487464 -4.26155e-08 0.11126 -0.487464 -4.26155e-08 -0.11126 -0.390916 -3.41749e-08 -0.311745 -0.216942 -1.89657e-08 -0.450484 -0.195458 -0.0941276 0.450484 -0.352203 -0.169612 0.311745 -0.43919 -0.211503 0.11126 -0.43919 -0.211503 -0.11126 -0.352203 -0.169612 -0.311745 -0.195458 -0.0941276 -0.450484 -0.135261 -0.169612 0.450484 -0.243732 -0.30563 0.311745 -0.303929 -0.381115 0.11126 -0.303929 -0.381115 -0.11126 -0.243732 -0.30563 -0.311745 -0.135261 -0.169612 -0.450484 -0.0482741 -0.211503 0.450484 -0.0869869 -0.381115 0.311745 -0.108471 -0.475242 0.11126 -0.108471 -0.475242 -0.11126 -0.0869869 -0.381115 -0.311745 -0.0482741 -0.211503 -0.450484 0.0482741 -0.211503 0.450484 0.086987 -0.381115 0.311745 0.108471 -0.475242 0.11126 0.108471 -0.475242 -0.11126 0.086987 -0.381115 -0.311745 0.0482741 -0.211503 -0.450484 0.135261 -0.169612 0.450484 0.243732 -0.30563 0.311745 0.303929 -0.381115 0.11126 0.303929 -0.381115 -0.11126 0.243732 -0.30563 -0.311745 0.135261 -0.169612 -0.450484 0.195458 -0.0941275 0.450484 0.352203 -0.169612 0.311745 0.43919 -0.211503 0.11126 0.43919 -0.211503 -0.11126 0.352203 -0.169612 -0.311745 0.195458 -0.0941275 -0.450484 1 Element0x806a640 168 3 3 9 1 0 0 9 15 1 0 0 15 21 1 0 0 21 27 1 0 0 27 33 1 0 0 33 39 1 0 0 39 45 1 0 0 45 51 1 0 0 51 57 1 0 0 57 63 1 0 0 63 69 1 0 0 69 75 1 0 0 75 81 1 0 0 81 3 1 0 0 8 2 14 0 0 14 2 20 0 0 20 2 26 0 0 26 2 32 0 0 32 2 38 0 0 38 2 44 0 0 44 2 50 0 0 50 2 56 0 0 56 2 62 0 0 62 2 68 0 0 68 2 74 0 0 74 2 80 0 0 80 2 86 0 0 86 2 8 0 0 3 4 10 0 0 3 10 9 0 0 4 5 11 0 0 4 11 10 0 0 5 6 12 0 0 5 12 11 0 0 6 7 13 0 0 6 13 12 0 0 7 8 14 0 0 7 14 13 0 0 9 10 16 0 0 9 16 15 0 0 10 11 17 0 0 10 17 16 0 0 11 12 18 0 0 11 18 17 0 0 12 13 19 0 0 12 19 18 0 0 13 14 20 0 0 13 20 19 0 0 15 16 22 0 0 15 22 21 0 0 16 17 23 0 0 16 23 22 0 0 17 18 24 0 0 17 24 23 0 0 18 19 25 0 0 18 25 24 0 0 19 20 26 0 0 19 26 25 0 0 21 22 28 0 0 21 28 27 0 0 22 23 29 0 0 22 29 28 0 0 23 24 30 0 0 23 30 29 0 0 24 25 31 0 0 24 31 30 0 0 25 26 32 0 0 25 32 31 0 0 27 28 34 0 0 27 34 33 0 0 28 29 35 0 0 28 35 34 0 0 29 30 36 0 0 29 36 35 0 0 30 31 37 0 0 30 37 36 0 0 31 32 38 0 0 31 38 37 0 0 33 34 40 0 0 33 40 39 0 0 34 35 41 0 0 34 41 40 0 0 35 36 42 0 0 35 42 41 0 0 36 37 43 0 0 36 43 42 0 0 37 38 44 0 0 37 44 43 0 0 39 40 46 0 0 39 46 45 0 0 40 41 47 0 0 40 47 46 0 0 41 42 48 0 0 41 48 47 0 0 42 43 49 0 0 42 49 48 0 0 43 44 50 0 0 43 50 49 0 0 45 46 52 0 0 45 52 51 0 0 46 47 53 0 0 46 53 52 0 0 47 48 54 0 0 47 54 53 0 0 48 49 55 0 0 48 55 54 0 0 49 50 56 0 0 49 56 55 0 0 51 52 58 0 0 51 58 57 0 0 52 53 59 0 0 52 59 58 0 0 53 54 60 0 0 53 60 59 0 0 54 55 61 0 0 54 61 60 0 0 55 56 62 0 0 55 62 61 0 0 57 58 64 0 0 57 64 63 0 0 58 59 65 0 0 58 65 64 0 0 59 60 66 0 0 59 66 65 0 0 60 61 67 0 0 60 67 66 0 0 61 62 68 0 0 61 68 67 0 0 63 64 70 0 0 63 70 69 0 0 64 65 71 0 0 64 71 70 0 0 65 66 72 0 0 65 72 71 0 0 66 67 73 0 0 66 73 72 0 0 67 68 74 0 0 67 74 73 0 0 69 70 76 0 0 69 76 75 0 0 70 71 77 0 0 70 77 76 0 0 71 72 78 0 0 71 78 77 0 0 72 73 79 0 0 72 79 78 0 0 73 74 80 0 0 73 80 79 0 0 75 76 82 0 0 75 82 81 0 0 76 77 83 0 0 76 83 82 0 0 77 78 84 0 0 77 84 83 0 0 78 79 85 0 0 78 85 84 0 0 79 80 86 0 0 79 86 85 0 0 81 82 4 0 0 81 4 3 0 0 82 83 5 0 0 82 5 4 0 0 83 84 6 0 0 83 6 5 0 0 84 85 7 0 0 84 7 6 0 0 85 86 8 0 0 85 8 7 0 0 Element0x806afc8 0 15 0 0 2.46519e-32 1.11022e-16 -1.09 -0.5 4.93038e-32 -0.09 -0.450484 0.216942 -0.09 -0.311745 0.390916 -0.09 -0.11126 0.487464 -0.09 0.11126 0.487464 -0.09 0.311745 0.390916 -0.09 0.450484 0.216942 -0.09 0.5 -2.05104e-10 -0.09 0.450484 -0.216942 -0.09 0.311745 -0.390916 -0.09 0.11126 -0.487464 -0.09 -0.11126 -0.487464 -0.09 -0.311745 -0.390916 -0.09 -0.450484 -0.216942 -0.09 1 Element0x806afc8 26 3 14 13 15 0 0 13 12 15 0 0 15 12 2 0 0 2 12 3 0 0 3 12 4 0 0 4 12 5 0 0 5 12 6 0 0 6 12 7 0 0 12 11 7 0 0 7 11 8 0 0 8 11 9 0 0 10 9 11 0 0 1 2 3 0 0 1 3 4 0 0 1 4 5 0 0 1 5 6 0 0 1 6 7 0 0 1 7 8 0 0 1 8 9 0 0 1 9 10 0 0 1 10 11 0 0 1 11 12 0 0 1 12 13 0 0 1 13 14 0 0 1 14 15 0 0 1 15 2 0 0 Element0x806b668 0 50 0 0 0.5 0 0.2 0.5 0 -0.2 0.586777 0 0.180194 0.656366 0 0.124698 0.694986 0 0.0445042 0.694986 0 -0.0445042 0.656366 0 -0.124698 0.586777 0 -0.180194 0.56136 0.0613604 0.180194 0.610568 0.110568 0.124698 0.637876 0.137876 0.0445042 0.637876 0.137876 -0.0445042 0.610568 0.110568 -0.124698 0.56136 0.0613604 -0.180194 0.5 0.0867767 0.180194 0.5 0.156366 0.124698 0.5 0.194986 0.0445042 0.5 0.194986 -0.0445042 0.5 0.156366 -0.124698 0.5 0.0867767 -0.180194 0.43864 0.0613604 0.180194 0.389432 0.110568 0.124698 0.362124 0.137876 0.0445042 0.362124 0.137876 -0.0445042 0.389432 0.110568 -0.124698 0.43864 0.0613604 -0.180194 0.413223 -7.58626e-09 0.180194 0.343634 -1.367e-08 0.124698 0.305014 -1.70462e-08 0.0445042 0.305014 -1.70462e-08 -0.0445042 0.343634 -1.367e-08 -0.124698 0.413223 -7.58626e-09 -0.180194 0.43864 -0.0613604 0.180194 0.389432 -0.110568 0.124698 0.362124 -0.137876 0.0445042 0.362124 -0.137876 -0.0445042 0.389432 -0.110568 -0.124698 0.43864 -0.0613604 -0.180194 0.5 -0.0867767 0.180194 0.5 -0.156366 0.124698 0.5 -0.194986 0.0445042 0.5 -0.194986 -0.0445042 0.5 -0.156366 -0.124698 0.5 -0.0867767 -0.180194 0.56136 -0.0613604 0.180194 0.610568 -0.110568 0.124698 0.637876 -0.137876 0.0445042 0.637876 -0.137876 -0.0445042 0.610568 -0.110568 -0.124698 0.56136 -0.0613604 -0.180194 1 Element0x806b668 96 3 3 9 1 0 0 9 15 1 0 0 15 21 1 0 0 21 27 1 0 0 27 33 1 0 0 33 39 1 0 0 39 45 1 0 0 45 3 1 0 0 8 2 14 0 0 14 2 20 0 0 20 2 26 0 0 26 2 32 0 0 32 2 38 0 0 38 2 44 0 0 44 2 50 0 0 50 2 8 0 0 3 4 10 0 0 3 10 9 0 0 4 5 11 0 0 4 11 10 0 0 5 6 12 0 0 5 12 11 0 0 6 7 13 0 0 6 13 12 0 0 7 8 14 0 0 7 14 13 0 0 9 10 16 0 0 9 16 15 0 0 10 11 17 0 0 10 17 16 0 0 11 12 18 0 0 11 18 17 0 0 12 13 19 0 0 12 19 18 0 0 13 14 20 0 0 13 20 19 0 0 15 16 22 0 0 15 22 21 0 0 16 17 23 0 0 16 23 22 0 0 17 18 24 0 0 17 24 23 0 0 18 19 25 0 0 18 25 24 0 0 19 20 26 0 0 19 26 25 0 0 21 22 28 0 0 21 28 27 0 0 22 23 29 0 0 22 29 28 0 0 23 24 30 0 0 23 30 29 0 0 24 25 31 0 0 24 31 30 0 0 25 26 32 0 0 25 32 31 0 0 27 28 34 0 0 27 34 33 0 0 28 29 35 0 0 28 35 34 0 0 29 30 36 0 0 29 36 35 0 0 30 31 37 0 0 30 37 36 0 0 31 32 38 0 0 31 38 37 0 0 33 34 40 0 0 33 40 39 0 0 34 35 41 0 0 34 41 40 0 0 35 36 42 0 0 35 42 41 0 0 36 37 43 0 0 36 43 42 0 0 37 38 44 0 0 37 44 43 0 0 39 40 46 0 0 39 46 45 0 0 40 41 47 0 0 40 47 46 0 0 41 42 48 0 0 41 48 47 0 0 42 43 49 0 0 42 49 48 0 0 43 44 50 0 0 43 50 49 0 0 45 46 4 0 0 45 4 3 0 0 46 47 5 0 0 46 5 4 0 0 47 48 6 0 0 47 6 5 0 0 48 49 7 0 0 48 7 6 0 0 49 50 8 0 0 49 8 7 0 0 Element0x806bd08 0 480 0 0 0.198199 1.31144e-08 -0.0567085 0.203909 -0.0574068 -0.0468194 0.220168 -0.106074 -0.0186577 0.220168 -0.106074 -0.0186577 0.244502 -0.138592 0.0234893 0.273205 -0.150011 0.0732051 0.273205 -0.150011 0.0732051 0.301908 -0.138592 0.122921 0.326242 -0.106074 0.165068 0.326242 -0.106074 0.165068 0.342501 -0.0574068 0.19323 0.348211 -7.73183e-17 0.203119 0.348211 -7.73183e-17 0.203119 0.342501 0.0574068 0.19323 0.326242 0.106074 0.165068 0.326242 0.106074 0.165068 0.301908 0.138592 0.122921 0.273205 0.150011 0.0732051 0.273205 0.150011 0.0732051 0.244502 0.138592 0.0234893 0.220168 0.106074 -0.0186577 0.220168 0.106074 -0.0186577 0.203909 0.0574068 -0.0468194 0.198199 -1.31144e-08 -0.0567085 0.220199 1.36135e-08 -0.0760018 0.226126 -0.0595914 -0.0657364 0.243004 -0.110111 -0.036503 0.243004 -0.110111 -0.036503 0.268264 -0.143866 0.00724785 0.298059 -0.15572 0.0588555 0.298059 -0.15572 0.0588555 0.327855 -0.143866 0.110463 0.353115 -0.110111 0.154214 0.353115 -0.110111 0.154214 0.369992 -0.0595914 0.183447 0.375919 -8.34708e-17 0.193713 0.375919 -8.34708e-17 0.193713 0.369992 0.0595914 0.183447 0.353115 0.110111 0.154214 0.353115 0.110111 0.154214 0.327855 0.143866 0.110463 0.298059 0.15572 0.0588555 0.298059 0.15572 0.0588555 0.268264 0.143866 0.00724785 0.243004 0.110111 -0.036503 0.243004 0.110111 -0.036503 0.226126 0.0595914 -0.0657364 0.220199 -1.36135e-08 -0.0760018 0.233141 1.50347e-08 -0.102246 0.239687 -0.0658126 -0.0909084 0.258327 -0.121606 -0.0586232 0.258327 -0.121606 -0.0586232 0.286223 -0.158886 -0.0103048 0.31913 -0.171977 0.0466906 0.31913 -0.171977 0.0466906 0.352036 -0.158886 0.103686 0.379932 -0.121606 0.152004 0.379932 -0.121606 0.152004 0.398572 -0.0658126 0.18429 0.405118 -8.99542e-17 0.195627 0.405118 -8.99542e-17 0.195627 0.398572 0.0658126 0.18429 0.379932 0.121606 0.152004 0.379932 0.121606 0.152004 0.352036 0.158886 0.103686 0.31913 0.171977 0.0466906 0.31913 0.171977 0.0466906 0.286223 0.158886 -0.0103048 0.258327 0.121606 -0.0586232 0.258327 0.121606 -0.0586232 0.239687 0.0658126 -0.0909084 0.233141 -1.50347e-08 -0.102246 0.235055 1.71617e-08 -0.131444 0.242527 -0.0751232 -0.118503 0.263803 -0.13881 -0.0816505 0.263803 -0.13881 -0.0816505 0.295647 -0.181364 -0.0264965 0.333208 -0.196307 0.0385622 0.333208 -0.196307 0.0385622 0.37077 -0.181364 0.103621 0.402613 -0.13881 0.158775 0.402613 -0.13881 0.158775 0.42389 -0.0751233 0.195628 0.431362 -9.57815e-17 0.208569 0.431362 -9.57815e-17 0.208569 0.42389 0.0751233 0.195628 0.402613 0.13881 0.158775 0.402613 0.13881 0.158775 0.37077 0.181364 0.103621 0.333208 0.196307 0.0385622 0.333208 0.196307 0.0385622 0.295647 0.181364 -0.0264965 0.263803 0.13881 -0.0816505 0.263803 0.13881 -0.0816505 0.242527 0.0751232 -0.118503 0.235055 -1.71617e-08 -0.131444 0.225649 1.96706e-08 -0.159153 0.234213 -0.0861059 -0.14432 0.258601 -0.159103 -0.102079 0.258601 -0.159103 -0.102079 0.295099 -0.207878 -0.038862 0.338152 -0.225006 0.0357079 0.338152 -0.225006 0.0357079 0.381205 -0.207878 0.110278 0.417704 -0.159103 0.173495 0.417704 -0.159103 0.173495 0.442091 -0.0861059 0.215736 0.450655 -1.00065e-16 0.230568 0.450655 -1.00065e-16 0.230568 0.442091 0.0861059 0.215736 0.417704 0.159103 0.173495 0.417704 0.159103 0.173495 0.381205 0.207878 0.110278 0.338152 0.225006 0.0357079 0.338152 0.225006 0.0357079 0.295099 0.207878 -0.038862 0.258601 0.159103 -0.102079 0.258601 0.159103 -0.102079 0.234213 0.0861059 -0.14432 0.225649 -1.96706e-08 -0.159153 0.225649 1.96706e-08 -0.159153 0.234213 -0.0861059 -0.14432 0.258601 -0.159103 -0.102079 0.258601 -0.159103 -0.102079 0.295099 -0.207878 -0.038862 0.338152 -0.225006 0.0357079 0.338152 -0.225006 0.0357079 0.381205 -0.207878 0.110278 0.417704 -0.159103 0.173495 0.417704 -0.159103 0.173495 0.442091 -0.0861059 0.215736 0.450655 -1.00065e-16 0.230568 0.450655 -1.00065e-16 0.230568 0.442091 0.0861059 0.215736 0.417704 0.159103 0.173495 0.417704 0.159103 0.173495 0.381205 0.207878 0.110278 0.338152 0.225006 0.0357079 0.338152 0.225006 0.0357079 0.295099 0.207878 -0.038862 0.258601 0.159103 -0.102079 0.258601 0.159103 -0.102079 0.234213 0.0861059 -0.14432 0.225649 -1.96706e-08 -0.159153 0.206356 2.21796e-08 -0.181153 0.216012 -0.0970886 -0.164428 0.24351 -0.179396 -0.1168 0.24351 -0.179396 -0.1168 0.284664 -0.234393 -0.045519 0.333208 -0.253705 0.0385622 0.333208 -0.253705 0.0385622 0.381753 -0.234393 0.122643 0.422906 -0.179396 0.193924 0.422906 -0.179396 0.193924 0.450405 -0.0970886 0.241552 0.460061 -1.02154e-16 0.258277 0.460061 -1.02154e-16 0.258277 0.450405 0.0970886 0.241552 0.422906 0.179396 0.193924 0.422906 0.179396 0.193924 0.381753 0.234393 0.122643 0.333208 0.253705 0.0385622 0.333208 0.253705 0.0385622 0.284664 0.234393 -0.045519 0.24351 0.179396 -0.1168 0.24351 0.179396 -0.1168 0.216012 0.0970886 -0.164428 0.206356 -2.21796e-08 -0.181153 0.180112 2.43066e-08 -0.194095 0.190694 -0.106399 -0.175766 0.220829 -0.1966 -0.12357 0.220829 -0.1966 -0.12357 0.26593 -0.256871 -0.0454539 0.31913 -0.278035 0.0466906 0.31913 -0.278035 0.0466906 0.372329 -0.256871 0.138835 0.41743 -0.1966 0.216951 0.41743 -0.1966 0.216951 0.447565 -0.106399 0.269147 0.458147 -1.01729e-16 0.287476 0.458147 -1.01729e-16 0.287476 0.447565 0.106399 0.269147 0.41743 0.1966 0.216951 0.41743 0.1966 0.216951 0.372329 0.256871 0.138835 0.31913 0.278035 0.0466906 0.31913 0.278035 0.0466906 0.26593 0.256871 -0.0454539 0.220829 0.1966 -0.12357 0.220829 0.1966 -0.12357 0.190694 0.106399 -0.175766 0.180112 -2.43066e-08 -0.194095 0.150914 2.57278e-08 -0.196008 0.162114 -0.11262 -0.176608 0.194012 -0.208095 -0.12136 0.194012 -0.208095 -0.12136 0.241749 -0.27189 -0.0386766 0.298059 -0.294291 0.0588555 0.298059 -0.294291 0.0588555 0.354369 -0.27189 0.156388 0.402107 -0.208095 0.239071 0.402107 -0.208095 0.239071 0.434004 -0.11262 0.294319 0.445205 -9.88554e-17 0.313719 0.445205 -9.88554e-17 0.313719 0.434004 0.11262 0.294319 0.402107 0.208095 0.239071 0.402107 0.208095 0.239071 0.354369 0.27189 0.156388 0.298059 0.294291 0.0588555 0.298059 0.294291 0.0588555 0.241749 0.27189 -0.0386766 0.194012 0.208095 -0.12136 0.194012 0.208095 -0.12136 0.162114 0.11262 -0.176608 0.150914 -2.57278e-08 -0.196008 0.123205 2.62268e-08 -0.186603 0.134623 -0.114805 -0.166826 0.167139 -0.212132 -0.110507 0.167139 -0.212132 -0.110507 0.215803 -0.277164 -0.026219 0.273205 -0.3 0.0732051 0.273205 -0.3 0.0732051 0.330608 -0.277164 0.172629 0.379271 -0.212132 0.256917 0.379271 -0.212132 0.256917 0.411787 -0.114805 0.313236 0.423205 -9.39704e-17 0.333013 0.423205 -9.39704e-17 0.333013 0.411787 0.114805 0.313236 0.379271 0.212132 0.256917 0.379271 0.212132 0.256917 0.330608 0.277164 0.172629 0.273205 0.3 0.0732051 0.273205 0.3 0.0732051 0.215803 0.277164 -0.026219 0.167139 0.212132 -0.110507 0.167139 0.212132 -0.110507 0.134623 0.114805 -0.166826 0.123205 -2.62268e-08 -0.186603 0.123205 2.62268e-08 -0.186603 0.134623 -0.114805 -0.166826 0.167139 -0.212132 -0.110507 0.167139 -0.212132 -0.110507 0.215803 -0.277164 -0.026219 0.273205 -0.3 0.0732051 0.273205 -0.3 0.0732051 0.330608 -0.277164 0.172629 0.379271 -0.212132 0.256917 0.379271 -0.212132 0.256917 0.411787 -0.114805 0.313236 0.423205 -9.39704e-17 0.333013 0.423205 -9.39704e-17 0.333013 0.411787 0.114805 0.313236 0.379271 0.212132 0.256917 0.379271 0.212132 0.256917 0.330608 0.277164 0.172629 0.273205 0.3 0.0732051 0.273205 0.3 0.0732051 0.215803 0.277164 -0.026219 0.167139 0.212132 -0.110507 0.167139 0.212132 -0.110507 0.134623 0.114805 -0.166826 0.123205 -2.62268e-08 -0.186603 0.101205 2.57278e-08 -0.167309 0.112406 -0.11262 -0.147909 0.144303 -0.208095 -0.0926613 0.144303 -0.208095 -0.0926613 0.192041 -0.27189 -0.00997754 0.248351 -0.294291 0.0875546 0.248351 -0.294291 0.0875546 0.304661 -0.27189 0.185087 0.352399 -0.208095 0.267771 0.352399 -0.208095 0.267771 0.384296 -0.11262 0.323018 0.395497 -8.78179e-17 0.342418 0.395497 -8.78179e-17 0.342418 0.384296 0.11262 0.323018 0.352399 0.208095 0.267771 0.352399 0.208095 0.267771 0.304661 0.27189 0.185087 0.248351 0.294291 0.0875546 0.248351 0.294291 0.0875546 0.192041 0.27189 -0.00997754 0.144303 0.208095 -0.0926613 0.144303 0.208095 -0.0926613 0.112406 0.11262 -0.147909 0.101205 -2.57278e-08 -0.167309 0.0882633 2.43066e-08 -0.141065 0.0988453 -0.106399 -0.122737 0.12898 -0.1966 -0.0705412 0.12898 -0.1966 -0.0705412 0.174081 -0.256871 0.00757512 0.227281 -0.278035 0.0997196 0.227281 -0.278035 0.0997196 0.28048 -0.256871 0.191864 0.325581 -0.1966 0.26998 0.325581 -0.1966 0.26998 0.355716 -0.106399 0.322176 0.366298 -8.13345e-17 0.340505 0.366298 -8.13345e-17 0.340505 0.355716 0.106399 0.322176 0.325581 0.1966 0.26998 0.325581 0.1966 0.26998 0.28048 0.256871 0.191864 0.227281 0.278035 0.0997196 0.227281 0.278035 0.0997196 0.174081 0.256871 0.00757512 0.12898 0.1966 -0.0705412 0.12898 0.1966 -0.0705412 0.0988453 0.106399 -0.122737 0.0882633 -2.43066e-08 -0.141065 0.0863495 2.21796e-08 -0.111867 0.0960055 -0.0970886 -0.095142 0.123504 -0.179396 -0.0475138 0.123504 -0.179396 -0.0475138 0.164658 -0.234393 0.0237668 0.213202 -0.253705 0.107848 0.213202 -0.253705 0.107848 0.261746 -0.234393 0.191929 0.3029 -0.179396 0.26321 0.3029 -0.179396 0.26321 0.330398 -0.0970886 0.310838 0.340054 -7.55072e-17 0.327563 0.340054 -7.55072e-17 0.327563 0.330398 0.0970886 0.310838 0.3029 0.179396 0.26321 0.3029 0.179396 0.26321 0.261746 0.234393 0.191929 0.213202 0.253705 0.107848 0.213202 0.253705 0.107848 0.164658 0.234393 0.0237668 0.123504 0.179396 -0.0475138 0.123504 0.179396 -0.0475138 0.0960055 0.0970886 -0.095142 0.0863495 -2.21796e-08 -0.111867 0.0957552 1.96706e-08 -0.0841583 0.104319 -0.0861059 -0.0693254 0.128707 -0.159103 -0.027085 0.128707 -0.159103 -0.027085 0.165205 -0.207878 0.0361323 0.208258 -0.225006 0.110702 0.208258 -0.225006 0.110702 0.251311 -0.207878 0.185272 0.28781 -0.159103 0.24849 0.28781 -0.159103 0.24849 0.312197 -0.0861059 0.29073 0.320761 -7.12232e-17 0.305563 0.320761 -7.12232e-17 0.305563 0.312197 0.0861059 0.29073 0.28781 0.159103 0.24849 0.28781 0.159103 0.24849 0.251311 0.207878 0.185272 0.208258 0.225006 0.110702 0.208258 0.225006 0.110702 0.165205 0.207878 0.0361323 0.128707 0.159103 -0.027085 0.128707 0.159103 -0.027085 0.104319 0.0861059 -0.0693254 0.0957552 -1.96706e-08 -0.0841583 0.0957552 1.96706e-08 -0.0841583 0.104319 -0.0861059 -0.0693254 0.128707 -0.159103 -0.027085 0.128707 -0.159103 -0.027085 0.165205 -0.207878 0.0361323 0.208258 -0.225006 0.110702 0.208258 -0.225006 0.110702 0.251311 -0.207878 0.185272 0.28781 -0.159103 0.24849 0.28781 -0.159103 0.24849 0.312197 -0.0861059 0.29073 0.320761 -7.12232e-17 0.305563 0.320761 -7.12232e-17 0.305563 0.312197 0.0861059 0.29073 0.28781 0.159103 0.24849 0.28781 0.159103 0.24849 0.251311 0.207878 0.185272 0.208258 0.225006 0.110702 0.208258 0.225006 0.110702 0.165205 0.207878 0.0361323 0.128707 0.159103 -0.027085 0.128707 0.159103 -0.027085 0.104319 0.0861059 -0.0693254 0.0957552 -1.96706e-08 -0.0841583 0.115049 1.71617e-08 -0.0621585 0.12252 -0.0751232 -0.0492175 0.143797 -0.13881 -0.0123647 0.143797 -0.13881 -0.0123647 0.17564 -0.181364 0.0427893 0.213202 -0.196307 0.107848 0.213202 -0.196307 0.107848 0.250763 -0.181364 0.172907 0.282607 -0.13881 0.228061 0.282607 -0.13881 0.228061 0.303884 -0.0751233 0.264913 0.311355 -6.91347e-17 0.277854 0.311355 -6.91347e-17 0.277854 0.303884 0.0751233 0.264913 0.282607 0.13881 0.228061 0.282607 0.13881 0.228061 0.250763 0.181364 0.172907 0.213202 0.196307 0.107848 0.213202 0.196307 0.107848 0.17564 0.181364 0.0427893 0.143797 0.13881 -0.0123647 0.143797 0.13881 -0.0123647 0.12252 0.0751232 -0.0492175 0.115049 -1.71617e-08 -0.0621585 0.141292 1.50347e-08 -0.0492165 0.147838 -0.0658126 -0.0378794 0.166478 -0.121606 -0.00559413 0.166478 -0.121606 -0.00559413 0.194374 -0.158886 0.0427242 0.227281 -0.171977 0.0997196 0.227281 -0.171977 0.0997196 0.260187 -0.158886 0.156715 0.288083 -0.121606 0.205033 0.288083 -0.121606 0.205033 0.306723 -0.0658126 0.237319 0.313269 -6.95597e-17 0.248656 0.313269 -6.95597e-17 0.248656 0.306723 0.0658126 0.237319 0.288083 0.121606 0.205033 0.288083 0.121606 0.205033 0.260187 0.158886 0.156715 0.227281 0.171977 0.0997196 0.227281 0.171977 0.0997196 0.194374 0.158886 0.0427242 0.166478 0.121606 -0.00559413 0.166478 0.121606 -0.00559413 0.147838 0.0658126 -0.0378794 0.141292 -1.50347e-08 -0.0492165 0.170491 1.36135e-08 -0.0473027 0.176418 -0.0595914 -0.0370373 0.193296 -0.110111 -0.00780392 0.193296 -0.110111 -0.00780392 0.218555 -0.143866 0.035947 0.248351 -0.15572 0.0875546 0.248351 -0.15572 0.0875546 0.278147 -0.143866 0.139162 0.303406 -0.110111 0.182913 0.303406 -0.110111 0.182913 0.320284 -0.0595914 0.212147 0.326211 -7.24334e-17 0.222412 0.326211 -7.24334e-17 0.222412 0.320284 0.0595914 0.212147 0.303406 0.110111 0.182913 0.303406 0.110111 0.182913 0.278147 0.143866 0.139162 0.248351 0.15572 0.0875546 0.248351 0.15572 0.0875546 0.218555 0.143866 0.035947 0.193296 0.110111 -0.00780392 0.193296 0.110111 -0.00780392 0.176418 0.0595914 -0.0370373 0.170491 -1.36135e-08 -0.0473027 0.198199 1.31144e-08 -0.0567085 0.203909 -0.0574068 -0.0468194 0.220168 -0.106074 -0.0186577 0.220168 -0.106074 -0.0186577 0.244502 -0.138592 0.0234893 0.273205 -0.150011 0.0732051 0.273205 -0.150011 0.0732051 0.301908 -0.138592 0.122921 0.326242 -0.106074 0.165068 0.326242 -0.106074 0.165068 0.342501 -0.0574068 0.19323 0.348211 -7.73183e-17 0.203119 0.348211 -7.73183e-17 0.203119 0.342501 0.0574068 0.19323 0.326242 0.106074 0.165068 0.326242 0.106074 0.165068 0.301908 0.138592 0.122921 0.273205 0.150011 0.0732051 0.273205 0.150011 0.0732051 0.244502 0.138592 0.0234893 0.220168 0.106074 -0.0186577 0.220168 0.106074 -0.0186577 0.203909 0.0574068 -0.0468194 0.198199 -1.31144e-08 -0.0567085 1 Element0x806bd08 512 3 25 1 26 0 0 26 1 2 0 0 26 2 27 0 0 27 2 3 0 0 28 4 29 0 0 29 4 5 0 0 29 5 30 0 0 30 5 6 0 0 31 7 32 0 0 32 7 8 0 0 32 8 33 0 0 33 8 9 0 0 34 10 35 0 0 35 10 11 0 0 35 11 36 0 0 36 11 12 0 0 37 13 38 0 0 38 13 14 0 0 38 14 39 0 0 39 14 15 0 0 40 16 41 0 0 41 16 17 0 0 41 17 42 0 0 42 17 18 0 0 43 19 44 0 0 44 19 20 0 0 44 20 45 0 0 45 20 21 0 0 46 22 47 0 0 47 22 23 0 0 47 23 48 0 0 48 23 24 0 0 49 25 50 0 0 50 25 26 0 0 50 26 51 0 0 51 26 27 0 0 52 28 53 0 0 53 28 29 0 0 53 29 54 0 0 54 29 30 0 0 55 31 56 0 0 56 31 32 0 0 56 32 57 0 0 57 32 33 0 0 58 34 59 0 0 59 34 35 0 0 59 35 60 0 0 60 35 36 0 0 61 37 62 0 0 62 37 38 0 0 62 38 63 0 0 63 38 39 0 0 64 40 65 0 0 65 40 41 0 0 65 41 66 0 0 66 41 42 0 0 67 43 68 0 0 68 43 44 0 0 68 44 69 0 0 69 44 45 0 0 70 46 71 0 0 71 46 47 0 0 71 47 72 0 0 72 47 48 0 0 73 49 74 0 0 74 49 50 0 0 74 50 75 0 0 75 50 51 0 0 76 52 77 0 0 77 52 53 0 0 77 53 78 0 0 78 53 54 0 0 79 55 80 0 0 80 55 56 0 0 80 56 81 0 0 81 56 57 0 0 82 58 83 0 0 83 58 59 0 0 83 59 84 0 0 84 59 60 0 0 85 61 86 0 0 86 61 62 0 0 86 62 87 0 0 87 62 63 0 0 88 64 89 0 0 89 64 65 0 0 89 65 90 0 0 90 65 66 0 0 91 67 92 0 0 92 67 68 0 0 92 68 93 0 0 93 68 69 0 0 94 70 95 0 0 95 70 71 0 0 95 71 96 0 0 96 71 72 0 0 97 73 98 0 0 98 73 74 0 0 98 74 99 0 0 99 74 75 0 0 100 76 101 0 0 101 76 77 0 0 101 77 102 0 0 102 77 78 0 0 103 79 104 0 0 104 79 80 0 0 104 80 105 0 0 105 80 81 0 0 106 82 107 0 0 107 82 83 0 0 107 83 108 0 0 108 83 84 0 0 109 85 110 0 0 110 85 86 0 0 110 86 111 0 0 111 86 87 0 0 112 88 113 0 0 113 88 89 0 0 113 89 114 0 0 114 89 90 0 0 115 91 116 0 0 116 91 92 0 0 116 92 117 0 0 117 92 93 0 0 118 94 119 0 0 119 94 95 0 0 119 95 120 0 0 120 95 96 0 0 145 121 146 0 0 146 121 122 0 0 146 122 147 0 0 147 122 123 0 0 148 124 149 0 0 149 124 125 0 0 149 125 150 0 0 150 125 126 0 0 151 127 152 0 0 152 127 128 0 0 152 128 153 0 0 153 128 129 0 0 154 130 155 0 0 155 130 131 0 0 155 131 156 0 0 156 131 132 0 0 157 133 158 0 0 158 133 134 0 0 158 134 159 0 0 159 134 135 0 0 160 136 161 0 0 161 136 137 0 0 161 137 162 0 0 162 137 138 0 0 163 139 164 0 0 164 139 140 0 0 164 140 165 0 0 165 140 141 0 0 166 142 167 0 0 167 142 143 0 0 167 143 168 0 0 168 143 144 0 0 169 145 170 0 0 170 145 146 0 0 170 146 171 0 0 171 146 147 0 0 172 148 173 0 0 173 148 149 0 0 173 149 174 0 0 174 149 150 0 0 175 151 176 0 0 176 151 152 0 0 176 152 177 0 0 177 152 153 0 0 178 154 179 0 0 179 154 155 0 0 179 155 180 0 0 180 155 156 0 0 181 157 182 0 0 182 157 158 0 0 182 158 183 0 0 183 158 159 0 0 184 160 185 0 0 185 160 161 0 0 185 161 186 0 0 186 161 162 0 0 187 163 188 0 0 188 163 164 0 0 188 164 189 0 0 189 164 165 0 0 190 166 191 0 0 191 166 167 0 0 191 167 192 0 0 192 167 168 0 0 193 169 194 0 0 194 169 170 0 0 194 170 195 0 0 195 170 171 0 0 196 172 197 0 0 197 172 173 0 0 197 173 198 0 0 198 173 174 0 0 199 175 200 0 0 200 175 176 0 0 200 176 201 0 0 201 176 177 0 0 202 178 203 0 0 203 178 179 0 0 203 179 204 0 0 204 179 180 0 0 205 181 206 0 0 206 181 182 0 0 206 182 207 0 0 207 182 183 0 0 208 184 209 0 0 209 184 185 0 0 209 185 210 0 0 210 185 186 0 0 211 187 212 0 0 212 187 188 0 0 212 188 213 0 0 213 188 189 0 0 214 190 215 0 0 215 190 191 0 0 215 191 216 0 0 216 191 192 0 0 217 193 218 0 0 218 193 194 0 0 218 194 219 0 0 219 194 195 0 0 220 196 221 0 0 221 196 197 0 0 221 197 222 0 0 222 197 198 0 0 223 199 224 0 0 224 199 200 0 0 224 200 225 0 0 225 200 201 0 0 226 202 227 0 0 227 202 203 0 0 227 203 228 0 0 228 203 204 0 0 229 205 230 0 0 230 205 206 0 0 230 206 231 0 0 231 206 207 0 0 232 208 233 0 0 233 208 209 0 0 233 209 234 0 0 234 209 210 0 0 235 211 236 0 0 236 211 212 0 0 236 212 237 0 0 237 212 213 0 0 238 214 239 0 0 239 214 215 0 0 239 215 240 0 0 240 215 216 0 0 265 241 266 0 0 266 241 242 0 0 266 242 267 0 0 267 242 243 0 0 268 244 269 0 0 269 244 245 0 0 269 245 270 0 0 270 245 246 0 0 271 247 272 0 0 272 247 248 0 0 272 248 273 0 0 273 248 249 0 0 274 250 275 0 0 275 250 251 0 0 275 251 276 0 0 276 251 252 0 0 277 253 278 0 0 278 253 254 0 0 278 254 279 0 0 279 254 255 0 0 280 256 281 0 0 281 256 257 0 0 281 257 282 0 0 282 257 258 0 0 283 259 284 0 0 284 259 260 0 0 284 260 285 0 0 285 260 261 0 0 286 262 287 0 0 287 262 263 0 0 287 263 288 0 0 288 263 264 0 0 289 265 290 0 0 290 265 266 0 0 290 266 291 0 0 291 266 267 0 0 292 268 293 0 0 293 268 269 0 0 293 269 294 0 0 294 269 270 0 0 295 271 296 0 0 296 271 272 0 0 296 272 297 0 0 297 272 273 0 0 298 274 299 0 0 299 274 275 0 0 299 275 300 0 0 300 275 276 0 0 301 277 302 0 0 302 277 278 0 0 302 278 303 0 0 303 278 279 0 0 304 280 305 0 0 305 280 281 0 0 305 281 306 0 0 306 281 282 0 0 307 283 308 0 0 308 283 284 0 0 308 284 309 0 0 309 284 285 0 0 310 286 311 0 0 311 286 287 0 0 311 287 312 0 0 312 287 288 0 0 313 289 314 0 0 314 289 290 0 0 314 290 315 0 0 315 290 291 0 0 316 292 317 0 0 317 292 293 0 0 317 293 318 0 0 318 293 294 0 0 319 295 320 0 0 320 295 296 0 0 320 296 321 0 0 321 296 297 0 0 322 298 323 0 0 323 298 299 0 0 323 299 324 0 0 324 299 300 0 0 325 301 326 0 0 326 301 302 0 0 326 302 327 0 0 327 302 303 0 0 328 304 329 0 0 329 304 305 0 0 329 305 330 0 0 330 305 306 0 0 331 307 332 0 0 332 307 308 0 0 332 308 333 0 0 333 308 309 0 0 334 310 335 0 0 335 310 311 0 0 335 311 336 0 0 336 311 312 0 0 337 313 338 0 0 338 313 314 0 0 338 314 339 0 0 339 314 315 0 0 340 316 341 0 0 341 316 317 0 0 341 317 342 0 0 342 317 318 0 0 343 319 344 0 0 344 319 320 0 0 344 320 345 0 0 345 320 321 0 0 346 322 347 0 0 347 322 323 0 0 347 323 348 0 0 348 323 324 0 0 349 325 350 0 0 350 325 326 0 0 350 326 351 0 0 351 326 327 0 0 352 328 353 0 0 353 328 329 0 0 353 329 354 0 0 354 329 330 0 0 355 331 356 0 0 356 331 332 0 0 356 332 357 0 0 357 332 333 0 0 358 334 359 0 0 359 334 335 0 0 359 335 360 0 0 360 335 336 0 0 385 361 386 0 0 386 361 362 0 0 386 362 387 0 0 387 362 363 0 0 388 364 389 0 0 389 364 365 0 0 389 365 390 0 0 390 365 366 0 0 391 367 392 0 0 392 367 368 0 0 392 368 393 0 0 393 368 369 0 0 394 370 395 0 0 395 370 371 0 0 395 371 396 0 0 396 371 372 0 0 397 373 398 0 0 398 373 374 0 0 398 374 399 0 0 399 374 375 0 0 400 376 401 0 0 401 376 377 0 0 401 377 402 0 0 402 377 378 0 0 403 379 404 0 0 404 379 380 0 0 404 380 405 0 0 405 380 381 0 0 406 382 407 0 0 407 382 383 0 0 407 383 408 0 0 408 383 384 0 0 409 385 410 0 0 410 385 386 0 0 410 386 411 0 0 411 386 387 0 0 412 388 413 0 0 413 388 389 0 0 413 389 414 0 0 414 389 390 0 0 415 391 416 0 0 416 391 392 0 0 416 392 417 0 0 417 392 393 0 0 418 394 419 0 0 419 394 395 0 0 419 395 420 0 0 420 395 396 0 0 421 397 422 0 0 422 397 398 0 0 422 398 423 0 0 423 398 399 0 0 424 400 425 0 0 425 400 401 0 0 425 401 426 0 0 426 401 402 0 0 427 403 428 0 0 428 403 404 0 0 428 404 429 0 0 429 404 405 0 0 430 406 431 0 0 431 406 407 0 0 431 407 432 0 0 432 407 408 0 0 433 409 434 0 0 434 409 410 0 0 434 410 435 0 0 435 410 411 0 0 436 412 437 0 0 437 412 413 0 0 437 413 438 0 0 438 413 414 0 0 439 415 440 0 0 440 415 416 0 0 440 416 441 0 0 441 416 417 0 0 442 418 443 0 0 443 418 419 0 0 443 419 444 0 0 444 419 420 0 0 445 421 446 0 0 446 421 422 0 0 446 422 447 0 0 447 422 423 0 0 448 424 449 0 0 449 424 425 0 0 449 425 450 0 0 450 425 426 0 0 451 427 452 0 0 452 427 428 0 0 452 428 453 0 0 453 428 429 0 0 454 430 455 0 0 455 430 431 0 0 455 431 456 0 0 456 431 432 0 0 457 433 458 0 0 458 433 434 0 0 458 434 459 0 0 459 434 435 0 0 460 436 461 0 0 461 436 437 0 0 461 437 462 0 0 462 437 438 0 0 463 439 464 0 0 464 439 440 0 0 464 440 465 0 0 465 440 441 0 0 466 442 467 0 0 467 442 443 0 0 467 443 468 0 0 468 443 444 0 0 469 445 470 0 0 470 445 446 0 0 470 446 471 0 0 471 446 447 0 0 472 448 473 0 0 473 448 449 0 0 473 449 474 0 0 474 449 450 0 0 475 451 476 0 0 476 451 452 0 0 476 452 477 0 0 477 452 453 0 0 478 454 479 0 0 479 454 455 0 0 479 455 480 0 0 480 455 456 0 0 mayavi-4.1.0/mayavi/tests/data/Particles.raw0000644000175100001440000000612011674464502021775 0ustar ischnellusers00000000000000DLgFD JD\ADMSD qDADL5DtD/ADMj$D ODYADLD[aDVADNVtD ?DADND D_ADMDDRADMD M Dk ADMiD kDiADMrD DZADLD (=DADND ?D3ADNO{D IDWADMGDDADMXD 1D`ADND DADMDDhADL(DVD(gADM&D DRADLD .DCADM\yD D*oADNZD ODfADM)DF(D[ADL[D N7DVADM(D 0 DuADLoD fD9ADMuD TD;aADL!D ބD]ADM/D aDyADLD D>ADN4FDDMADNDuDIADM>DDADLmDvDLADN D DADL{D]DADM#D DLADMID֗DnADNHYD K&D"QADMP@D DLADN%D D$ADMl7D U#DLADL*D XDADMڜDD4ADM$@DA"DADM4D DADLD )AD^ADLbKD +DADNVD D8ADMD iDJRADMoD @pD@DN@yD JD@DM9+DD@DLhD DpADNpDyDADLD WDADLbBD {&DADMD ܠD[ADNeD $$DYADND F DADLD /D(ADLڝD DADMڊD D+ADM"D mzDADMBD $JD|ADMnD DMADMD D<]ADNxD D{ADLD E8DLADMD oDLADL*D +D`ADNCD DADNKhD .DADMND DADND ODOADMUD D ADLD D ADMQ;D DADMsD DKOADLD DADLsD g D ADMD ^DADM D nD`ADLaD 7DADMD kDa1ADLMD D7BADND eDADMaD VDlADLgD ;DWADMRD s&DlADLD Dx@DLtD D@DN4D 5D@DM4D %DlADNmD DGADMXDDVADLD D ADM9D 2DoOADN]yD^YD3ADLD D?ADND}DqADMD DADMD DJADMD;DADMoDDVADNrD DlgADLDDNDRADMD PD-ADLpbD_DADN.@D DADLD nDADM:D UDxADMODDADLD DADLD':DADMD 9D9ADLԓD DxADNDzD>ADN8D DADMHD DR6ADLQD yDADM]DQDADNLID^D ADL[DhDyUADN%D DADLD DXADM D DQbADM]D DfADMD uDADLDHD&ADL!D "DΦADN8D DADLD+D˷ADMDDDaSADNm>D D _AQAAAACAAAAUAAAAGQAAAA==eJxjYFBwZGBYAMQfgFjBiYHBwwkAITcDYA==AQAAAACAAAAUAAAAGQAAAA==eJxjYFBwZGBQcGJg6AHiE0DM5AwAGcoC4Q==AQAAAACAAAA8AAAAFgAAAA==eJxjYEAGBxxQ2eh8EHBwgGEAh3wFgQ==AQAAAACAAAA8AAAAFgAAAA==eJxjYEAGBxxQ2eh8EHBwgGEAh3wFgQ==AQAAAACAAAAUAAAAFgAAAA==eJxjYGBgYARiJiBmBmIWIAYAAGQACw==AQAAAACAAAAEAAAADAAAAA==eJxjZWBgAAAAGAAGAQAAAACAAAABAAAACQAAAA==eJzjAwAADwAP mayavi-4.1.0/mayavi/tests/data/tiny.q0000644000175100001440000000202011674464502020474 0ustar ischnellusers00000000000000<<?PGfff?fff?fff?fff?fff?fff?fff?fff?•?•?•?•?•?•?•?•?q=?q=?q=?q=?q=?q=?q=?q=?p}?p}?p}?p}?p}?p}?p}?p}?~@~@~@~@~@~@~@~@?PGfff?fff?fff?fff?fff?fff?fff?fff?•?•?•?•?•?•?•?•?q=?q=?q=?q=?q=?q=?q=?q=?p}?p}?p}?p}?p}?p}?p}?p}?~@~@~@~@~@~@~@~@?PGfff?fff?fff?fff?fff?fff?fff?fff?•?•?•?•?•?•?•?•?q=?q=?q=?q=?q=?q=?q=?q=?p}?p}?p}?p}?p}?p}?p}?p}?~@~@~@~@~@~@~@~@?PGfff?fff?fff?fff?fff?fff?fff?fff?•?•?•?•?•?•?•?•?q=?q=?q=?q=?q=?q=?q=?q=?p}?p}?p}?p}?p}?p}?p}?p}?~@~@~@~@~@~@~@~@?PGfff?fff?fff?fff?fff?fff?fff?fff?•?•?•?•?•?•?•?•?q=?q=?q=?q=?q=?q=?q=?q=?p}?p}?p}?p}?p}?p}?p}?p}?~@~@~@~@~@~@~@~@mayavi-4.1.0/mayavi/tests/test_recorder.py0000644000175100001440000003425511674464502021653 0ustar ischnellusers00000000000000""" Unit tests for the script recorder -- the script recorder has been refactored to move to AppTools however we repeat the tests here with a TVTK object to ensure that the test works with TVTK objects. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. import unittest from traits.api import (HasTraits, Float, Instance, Str, List, Bool) from tvtk.api import tvtk from apptools.scripting.api import (Recorder, recordable, set_recorder) ###################################################################### # Test classes. class Toy(HasTraits): color = Str type = Str ignore = Bool(False, record=False) class Child(HasTraits): name = Str('child') age = Float(10.0) property = Instance(tvtk.Property, (), record=True) toy = Instance(Toy, record=True) friends = List(Str) @recordable def grow(self, x): """Increase age by x years.""" self.age += x self.f(1) @recordable def f(self, args): """Function f.""" return args def not_recordable(self): pass class Parent(HasTraits): children = List(Child, record=True) recorder = Instance(Recorder, record=False) class Test(HasTraits): # This should be set. recorder = Instance(HasTraits) # These should be ignored. _ignore = Bool(False) ignore_ = Bool(False) class TestRecorder(unittest.TestCase): def setUp(self): self.tape = Recorder() set_recorder(self.tape) p = Parent() c = Child() toy = Toy(color='blue', type='bunny') c.toy = toy p.children.append(c) self.p = p return def tearDown(self): self.tape.clear() set_recorder(None) return def test_unique_name(self): "Does the get_unique_id method work." t = tvtk.XMLUnstructuredGridWriter() tape = self.tape self.assertEqual(tape._get_unique_name(t), 'xml_unstructured_grid_writer') self.assertEqual(tape._get_unique_name(t), 'xml_unstructured_grid_writer1') t = Toy() self.assertEqual(tape._get_unique_name(t), 'toy') t = (1, 2) self.assertEqual(tape._get_unique_name(t), 'tuple0') l = [1, 2] self.assertEqual(tape._get_unique_name(l), 'list0') d = {'a': 1} self.assertEqual(tape._get_unique_name(d), 'dict0') self.assertEqual(tape._get_unique_name(1), 'int0') def test_record(self): "Does recording work correctly." tape = self.tape p = self.p c = p.children[0] toy = c.toy # start recording. tape.recording = True tape.register(p) # Test if p's recorder attribute is set. self.assertEqual(tape, p.recorder) # Test script ids and object path. self.assertEqual(tape.get_script_id(p), 'parent') self.assertEqual(tape.get_object_path(p), '') self.assertEqual(tape.get_script_id(c), 'child') self.assertEqual(tape.get_object_path(c), 'parent.children[0]') self.assertEqual(tape.get_script_id(toy), 'child.toy') self.assertEqual(tape.get_object_path(toy), 'parent.children[0].toy') c.name = 'Ram' # The child should first be instantiated. self.assertEqual(tape.lines[-2], "child = parent.children[0]") # Then its trait set. self.assertEqual(tape.lines[-1], "child.name = 'Ram'") c.age = 10.5 self.assertEqual(tape.lines[-1], "child.age = 10.5") c.property.representation = 'w' self.assertEqual(tape.lines[-1], "child.property.representation = 'wireframe'") c.property.color = (1, 0, 0) self.assertEqual(tape.lines[-1], "child.property.color = (1.0, 0.0, 0.0)") toy.color = 'red' self.assertEqual(tape.lines[-1], "child.toy.color = 'red'") toy.type = 'teddy' self.assertEqual(tape.lines[-1], "child.toy.type = 'teddy'") # This trait should be ignored. toy.ignore = True self.assertEqual(tape.lines[-1], "child.toy.type = 'teddy'") # Turn of recording and test. tape.recording = False toy.type = 'rat' self.assertEqual(tape.lines[-1], "child.toy.type = 'teddy'") #print tape.script # Stop recording. n = len(tape.lines) tape.unregister(p) c.property.representation = 'points' toy.type = 'bunny' self.assertEqual(tape.lines[-1], "child.toy.type = 'teddy'") self.assertEqual(n, len(tape.lines)) # Make sure the internal data of the recorder is cleared. self.assertEqual(0, len(tape._registry)) self.assertEqual(0, len(tape._reverse_registry)) self.assertEqual(0, len(tape._known_ids)) def test_recorded_trait_replaced(self): "Does recording work right when a trait is replaced." tape = self.tape p = self.p c = p.children[0] toy = c.toy # start recording. tape.recording = True tape.register(p) # Test the original trait. toy.color = 'red' self.assertEqual(tape.lines[-1], "child.toy.color = 'red'") # Now reassign the toy. t1 = Toy(name='ball') c.toy = t1 t1.color = 'yellow' self.assertEqual(tape.lines[-1], "child.toy.color = 'yellow'") def test_clear(self): "Test the clear method." p = self.p tape = self.tape tape.register(p) tape.clear() # Everything should be unregistered. self.assertEqual(p.recorder, None) # Internal data should be wiped clean. self.assertEqual(0, len(tape._registry)) self.assertEqual(0, len(tape._reverse_registry)) self.assertEqual(0, len(tape._known_ids)) self.assertEqual(0, len(tape._name_map)) def test_create_object(self): "Is the object imported and created if unknown?" tape = self.tape tape.recording = True t = Toy() tape.register(t) t.type = 'computer' # Since the name toy is unknown, there should be a # line to create it. self.assertEqual(tape.lines[-3][-10:], "import Toy") self.assertEqual(tape.lines[-2], "toy = Toy()") self.assertEqual(tape.lines[-1], "toy.type = 'computer'") # Since this one is known, there should be no imports or # anything. t1 = Toy() tape.register(t1, known=True) t1.type = 'ball' self.assertEqual(tape.lines[-2], "toy.type = 'computer'") self.assertEqual(tape.lines[-1], "toy1.type = 'ball'") def test_list_items_changed(self): "Test if a list item is changed does the change get recorded." p = self.p tape = self.tape child = p.children[0] tape.register(p, known=True) tape.recording = True child.friends = ['Krishna', 'Ajay', 'Ali'] self.assertEqual(tape.lines[-1], "child.friends = ['Krishna', 'Ajay', 'Ali']") child.friends[1:] = ['Sam', 'Frodo'] self.assertEqual(tape.lines[-1], "child.friends[1:3] = ['Sam', 'Frodo']") child.friends[1] = 'Hari' self.assertEqual(tape.lines[-1], "child.friends[1:2] = ['Hari']") # What if we change a list where record=True. child1 = Child() tape.register(child1) p.children.append(child1) self.assertEqual(tape.lines[-1], "parent.children[1:1] = [child1]") del p.children[1] self.assertEqual(tape.lines[-1], "parent.children[1:2] = []") p.children[0] = child1 self.assertEqual(tape.lines[-1], "parent.children[0:1] = [child1]") def test_path_change_on_list(self): "Does the object path update when a list has changed?" # Test the case where we have a hierarchy and we change the # list. tape = self.tape p = self.p child1 = Child() p.children.append(child1) tape.register(p) tape.recording = True self.assertEqual(tape.get_object_path(child1), 'parent.children[1]') self.assertEqual(tape.get_script_id(child1), 'child1') del p.children[0] self.assertEqual(tape.get_object_path(child1), 'parent.children[0]') self.assertEqual(tape.get_script_id(child1), 'child1') def test_write_script_id_in_namespace(self): "Test the write_script_id_in_namespace method." tape = self.tape tape.recording = True # This should not cause an error but insert the name 'foo' in the # namespace. tape.write_script_id_in_namespace('foo') def test_recorder_and_ignored(self): "Test if recorder trait is set and private traits are ignored." t = Test() self.assertEqual(t.recorder, None) self.assertEqual(t._ignore, False) self.assertEqual(t.ignore_, False) tape = Recorder() tape.register(t) tape.recording = True self.assertEqual(t.recorder, tape) t._ignore = True t.ignore_ = True self.assertEqual(len(tape.script.strip()), 0) def test_record_function(self): "See if recordable function calls are handled correctly." # Note that the global recorder is set in setUp and removed in # tearDown. tape = self.tape c = self.p.children[0] tape.register(c) tape.recording = True # Setting the age should be recorded. c.age = 11 self.assertEqual(tape.lines[-1], "child.age = 11.0") # This should also work without problems. c.f(c.toy) self.assertEqual(tape.lines[-2], "child.age = 11.0") self.assertEqual(tape.lines[-1], 'child.toy = child.f(child.toy)') # Calling f should be recorded. c.f(1) self.assertEqual(tape.lines[-1], "child.f(1)") # This should not record the call to f or the change to the age # trait inside grow. c.grow(1) self.assertEqual(c.age, 12.0) self.assertEqual(tape.lines[-2], "child.f(1)") self.assertEqual(tape.lines[-1], "child.grow(1)") # Non-recordable functions shouldn't be. c.not_recordable() self.assertEqual(tape.lines[-1], "child.grow(1)") # Test a simple recordable function. @recordable def func(x, y): return x, y result = func(1, 2) self.assertEqual(tape.lines[-1], "tuple0 = func(1, 2)") def test_non_has_traits(self): "Can classes not using traits be handled?" tape = self.tape p = self.p c = p.children[0] class A(object): @recordable def __init__(self, x, y=1): self.x = x self.y = y @recordable def f(self, x, y): return x, y @recordable def g(self, x): return x def not_recordable(self): pass tape.register(p) tape.recording = True # Test if __init__ is recorded correctly. a = A(x=1) # Should record. a.f(1, 'asd') self.assertEqual(tape.lines[-3][-8:], "import A") self.assertEqual(tape.lines[-2], "a = A(x=1)") self.assertEqual(tape.lines[-1], "tuple0 = a.f(1, 'asd')") result = a.f(p, c) # This should instantiate the parent first, get the child from # that and then record the call itself. self.assertEqual(tape.lines[-3], "parent = Parent()") self.assertEqual(tape.lines[-2], "child = parent.children[0]") self.assertEqual(tape.lines[-1], "tuple1 = a.f(parent, child)") # This should simply refer to the child. result = a.g(c) self.assertEqual(tape.lines[-1], "child = a.g(child)") # Should do nothing. a.not_recordable() self.assertEqual(tape.lines[-1], "child = a.g(child)") # When a function is called with unknown args it should attempt # to create the objects. r = a.g(Toy()) self.assertEqual(tape.lines[-3][-10:], "import Toy") self.assertEqual(tape.lines[-2], "toy = Toy()") self.assertEqual(tape.lines[-1], "toy = a.g(toy)") def test_set_script_id(self): "Test if setting script_id at registration time works." tape = self.tape p = self.p c = p.children[0] tape.register(p, script_id='child') tape.recording = True # Ask to be called child. self.assertEqual(tape.get_script_id(p), 'child') # Register another Child. c1 = Child() tape.register(c1) # Will be child2 since child1 is taken. self.assertEqual(tape.get_script_id(c1), 'child2') # Test if recording works correctly with the changed script_id. p.children.append(c1) self.assertEqual(tape.lines[-1], "child.children[1:1] = [child2]") def test_save(self): "Test if saving tape to file works." tape = self.tape p = self.p c = p.children[0] toy = c.toy # Start recording tape.register(p) tape.recording = True toy.type = 'teddy' # Now stop. tape.recording = False tape.unregister(p) import StringIO f = StringIO.StringIO() tape.save(f) # Test if the file is OK. expect = ["child = parent.children[0]\n", "child.toy.type = 'teddy'\n" ] f.seek(0) lines = f.readlines() self.assertEqual(expect, lines) f.close() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_mouse_pick_dispatcher.py0000644000175100001440000000566611674464502024416 0ustar ischnellusers00000000000000""" Test the MousePickDispatcher. """ import unittest from traits.api import HasTraits, Instance from mayavi.core.null_engine import NullEngine, DummyViewer from tvtk.api import tvtk from mayavi.tools.engine_manager import engine_manager from mayavi.core.registry import registry from mayavi.core.mouse_pick_dispatcher import \ MousePickDispatcher from tvtk.pyface.picker import Picker ################################################################################ # class `DummyScene` ################################################################################ class DummyScene(HasTraits): """ Mimics the API of a TVTK scene. """ interactor = Instance(tvtk.GenericRenderWindowInteractor, ()) picker = Instance(Picker, (None, )) ################################################################################ # class `DummyMousePickDispatcher` ################################################################################ class DummyMousePickDispatcher(MousePickDispatcher): """ A MousePickDispatcher that accepts the DummyViewer as a Scene. """ scene = Instance(DummyViewer) ################################################################################ # class `TestMousePickerDispatcher` ################################################################################ class TestMousePickerDispatcher(unittest.TestCase): """ Create a fake figure, to make sure that observers are well registered and removed. """ def setUp(self): e = NullEngine() e.start() registry.register_engine(e) engine_manager.current_engine = e self.e = e self.s = e.new_scene() self.s.scene = DummyScene() def tearDown(self): engine_manager.current_engine = None # Unregistering the engine, to avoid side-effects between tests self.e.stop() registry.unregister_engine(self.e) def test_callback_registering(self): def test(picker): pass dispatcher = DummyMousePickDispatcher(scene=self.s) initial_interactor_callbacks = frozenset([i for i in range(100) if self.s.scene.interactor.has_observer(i) ]) dispatcher.callbacks.append((test, 'point', 'Left')) # Check that VTK observers were established self.assertTrue(dispatcher._mouse_mvt_callback_nb) self.assertTrue('Left' in dispatcher._mouse_press_callback_nbs) self.assertTrue('Left' in dispatcher._mouse_release_callback_nbs) # Check that we are back to no observers dispatcher.callbacks[:] = [] interactor_callbacks = frozenset([i for i in range(100) if self.s.scene.interactor.has_observer(i) ]) self.assertEquals(interactor_callbacks, initial_interactor_callbacks) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_mlab_source_integration.py0000644000175100001440000004032711674464502024741 0ustar ischnellusers00000000000000""" Test for the various mlab source functions. These tests are higher level than the tests testing directly the MlabSource subclasses. They are meant to capture errors in the formatting of the input arguments. """ # Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. import unittest import numpy as np from mayavi.tools import sources ################################################################################ # `BaseTestSource` ################################################################################ class BaseTestSource(unittest.TestCase): def setUp(self): return def tearDown(self): return def all_close(self, a, b): """ Similar to numpy's allclose, but works also for a=None. """ if None in (a, b): self.assert_(a==b) else: self.assert_(np.allclose(a, a)) def check_positions(self, source, x, y, z): """ Check that the position vectors of the source do correspond to the given input positions """ self.assert_(np.allclose(source.mlab_source.x, x)) self.assert_(np.allclose(source.mlab_source.y, y)) self.assert_(np.allclose(source.mlab_source.z, z)) def check_vectors(self, source, u, v, w): """ Check that the vector data corresponds to the given arrays. """ self.all_close(source.mlab_source.u, u) self.all_close(source.mlab_source.v, v) self.all_close(source.mlab_source.w, w) def check_scalars(self, source, s): """ Check that the scalar data corresponds to the given array. """ self.all_close(source.mlab_source.scalars, s) ################################################################################ # `TestScalarScatter` ################################################################################ class TestScalarScatter(BaseTestSource): def test_input_args(self): """ Check that scalar_scatter can take different input arguments """ # Check for a single number as position vectors. ss = sources.scalar_scatter(0, 0, 0, figure=None) self.check_positions(ss, 0, 0, 0) self.check_scalars(ss, None) self.check_vectors(ss, None, None, None) # Check for a single number as scalar data, and no position # vectors. ss = sources.scalar_scatter(0, figure=None) self.check_positions(ss, 0, 0, 0) self.check_scalars(ss, 0) self.check_vectors(ss, None, None, None) # Check for a list as position vectors. ss = sources.scalar_scatter([0, 1], [0, 1], [0, 1], figure=None) self.check_positions(ss, [0, 1], [0, 1], [0, 1]) self.check_scalars(ss, None) self.check_vectors(ss, None, None, None) # Check for a list as scalar data, and no position vectors. ss = sources.scalar_scatter([0, 1], figure=None) self.check_scalars(ss, [0, 1]) self.check_vectors(ss, None, None, None) # Check for a 1D array as position vectors. a = np.array([0, 1]) ss = sources.scalar_scatter(a, a, a, figure=None) self.check_positions(ss, a, a, a) self.check_scalars(ss, None) self.check_vectors(ss, None, None, None) # Check for a 1D array as a scalar data, and no position vectors. ss = sources.scalar_scatter(a, figure=None) self.check_scalars(ss, a) self.check_vectors(ss, None, None, None) # Check for a 2D array as position vectors. a = np.array([[0, 1], [2, 3]]) ss = sources.scalar_scatter(a, a, a, figure=None) self.check_positions(ss, a, a, a) self.check_scalars(ss, None) self.check_vectors(ss, None, None, None) # Check for a 2D array as scalar data, and no position vectors. ss = sources.scalar_scatter(a, figure=None) self.check_scalars(ss, a) self.check_vectors(ss, None, None, None) # Check for a 2D array as scalar data, and no position vectors. ss = sources.scalar_scatter(a, figure=None) self.check_scalars(ss, a) self.check_vectors(ss, None, None, None) ################################################################################ # `TestVectorScatter` ################################################################################ class TestVectorScatter(BaseTestSource): def test_input_args(self): """ Check that vector_scatter can take different input arguments """ # Check for a single number as a position vector. ss = sources.vector_scatter(0, 0, 0, 0, 0, 0, figure=None) self.check_positions(ss, 0, 0, 0) self.check_scalars(ss, None) self.check_vectors(ss, 0, 0, 0) # Check for no position vectors, and single numbers for vector # data. ss = sources.vector_scatter(0, 0, 0, figure=None) self.check_positions(ss, 0, 0, 0) self.check_scalars(ss, None) self.check_vectors(ss, 0, 0, 0) # Check for a list as a position vector. ss = sources.vector_scatter([0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], figure=None) self.check_positions(ss, [0, 1], [0, 1], [0, 1]) self.check_scalars(ss, None) self.check_vectors(ss, [0, 1], [0, 1], [0, 1]) # Check for a lists as a vector data, and no position vectors ss = sources.vector_scatter([0, 1], [0, 1], [0, 1], figure=None) self.check_scalars(ss, None) self.check_vectors(ss, [0, 1], [0, 1], [0, 1]) # Check for a 1D array as a position vector. a = np.array([0, 1]) ss = sources.vector_scatter(a, a, a, a, a, a, figure=None) self.check_positions(ss, a, a, a) self.check_scalars(ss, None) self.check_vectors(ss, a, a, a) # Check for a 1D array as vector data, and no position vectors. ss = sources.vector_scatter(a, a, a, figure=None) self.check_scalars(ss, None) self.check_vectors(ss, a, a, a) # Check for a 2D array as a position vector. a = np.array([[0, 1], [2, 3]]) ss = sources.vector_scatter(a, a, a, a, a, a, figure=None) self.check_positions(ss, a, a, a) self.check_scalars(ss, None) self.check_vectors(ss, a, a, a) # Check for a 2D array as vector data, and no position vectors. ss = sources.vector_scatter(a, a, a, figure=None) self.check_scalars(ss, None) self.check_vectors(ss, a, a, a) # Check for a 3D array as a position vector. x, y, z = np.mgrid[0:3, 0:3, 0:3] ss = sources.vector_scatter(x, y, z, x, y, z, figure=None) self.check_positions(ss, x, y, z) self.check_scalars(ss, None) self.check_vectors(ss, x, y, z) # Check for a 3D array as vector data, and no position vectors. x, y, z = np.mgrid[0:3, 0:3, 0:3] ss = sources.scalar_scatter(z, figure=None) self.check_scalars(ss, z) X, Y, Z = np.indices(z.shape) self.check_positions(ss, X, Y, Z) ################################################################################ # `TestArray2DSource` ################################################################################ class TestArray2DSource(BaseTestSource): def test_input_args(self): """ Check that array2d_source can take different input arguments """ # Check for a single number as data and no position arrays. ss = sources.array2d_source(0, figure=None) self.check_scalars(ss, 0) # Check for a list as data, and no position arrays. ss = sources.array2d_source([0, 1], figure=None) self.check_scalars(ss, [0, 1]) # Check for a 1D array as data, and no position arrays. a = np.array([0, 1]) ss = sources.array2d_source(a, figure=None) self.check_scalars(ss, a) # Check for a 2D array as data, and no position arrays. a = np.array([[0, 1], [2, 3]]) ss = sources.array2d_source(a, figure=None) self.check_scalars(ss, a) # Check for 2 lists as positions vectors, and a 2D list as data x = [0, 1] y = [0, 1] s = [[0, 1], [2, 3]] ss = sources.array2d_source(x, y, s, figure=None) self.check_scalars(ss, s) # Check for an ogrid as position vectors, and a function for the # scalars x, y = np.ogrid[-3:3, -3:3] f = lambda x, y: x**2 + y**2 ss = sources.array2d_source(x, y, f, figure=None) self.check_scalars(ss, f(x, y)) # Check for an mgrid as position vectors, and a 2D array for the # scalars x, y = np.mgrid[-3:3, -3:3] s = np.zeros_like(x) ss = sources.array2d_source(x, y, x, figure=None) self.check_scalars(ss, s) ################################################################################ # `TestScalarField` ################################################################################ class TestScalarField(BaseTestSource): def test_input_args(self): """ Check that scalar_field can take different input arguments """ # Check for 2D arrays as positions vectors, and a function for # the data f = lambda x, y, z: x**2 + y**2 x, y = np.mgrid[-3:3, -3:3] z = np.zeros_like(x) ss = sources.scalar_field(x, y, z, f, figure=None) self.check_positions(ss, x, y, z) s = f(x, y, z) self.check_scalars(ss, s) # Check for a 2D array as data, and no position vectors s = np.random.random((10, 10)) ss = sources.scalar_field(s, figure=None) self.check_scalars(ss, s) # Check for a 3D array as data, and no position vectors s = np.random.random((10, 10, 10)) ss = sources.scalar_field(s, figure=None) self.check_scalars(ss, s) # Check for a 3D array as data, and 3D arrays as position x, y, z = np.mgrid[-3:3, -3:3, -3:3] ss = sources.scalar_field(x, y, z, z, figure=None) self.check_positions(ss, x, y, z) self.check_scalars(ss, z) ################################################################################ # `TestVectorField` ################################################################################ class TestVectorField(BaseTestSource): def test_input_args(self): """ Check that vector_field can take different input arguments """ # Check for 2D arrays as positions vectors, and a function for # the data x, y = np.mgrid[-3:3, -3:3] z = np.zeros_like(x) def f(x, y, z): return y, z, x ss = sources.vector_field(x, y, z, f, figure=None) self.check_scalars(ss, None) self.check_vectors(ss, y, z, x) # Check for a 2D array as data, and no position vectors u = np.random.random((10, 10)) v = np.random.random((10, 10)) w = np.random.random((10, 10)) ss = sources.vector_field(u, v, w, figure=None) self.check_scalars(ss, None) self.check_vectors(ss, u, v, w) # Check for a 3D array as data, and no position vectors u = np.random.random((10, 10, 10)) v = np.random.random((10, 10, 10)) w = np.random.random((10, 10, 10)) ss = sources.vector_field(u, v, w, figure=None) self.check_scalars(ss, None) self.check_vectors(ss, u, v, w) # Check for a 3D array as data, and 3D arrays as position x, y, z = np.mgrid[-3:3, -3:3, -3:3] ss = sources.vector_field(x, y, z, y, z, x, figure=None) self.check_scalars(ss, None) self.check_positions(ss, x, y, z) self.check_vectors(ss, y, z, x) ################################################################################ # `TestLineSource` ################################################################################ class TestLineSource(BaseTestSource): def test_input_args(self): """ Check that vector_field can take different input arguments """ # Check for numbers as position vectors ss = sources.line_source(0, 0, 0, figure=None) self.check_positions(ss, 0, 0, 0) self.check_scalars(ss, None) # Check for lists as position vectors and as data ss = sources.line_source([0, 1], [0, 1], [0, 1], [2, 3], figure=None) self.check_positions(ss, [0, 1], [0, 1], [0, 1]) self.check_scalars(ss, [2, 3]) # Check for arrays as position vectors and a function as data x, y, z = np.random.random((3, 10)) f = lambda x, y, z: x + y + z ss = sources.line_source(x, y, z, f, figure=None) self.check_positions(ss, x, y, z) self.check_scalars(ss, f(x, y, z)) ################################################################################ # `TestVerticalVectorsSource` ################################################################################ class TestVerticalVectorsSource(BaseTestSource): def test_input_args(self): """ Check that vector_field can take different input arguments """ # Check for numbers as position vectors ss = sources.vertical_vectors_source(0, 0, 1, figure=None) self.check_positions(ss, 0, 0, 0) self.check_scalars(ss, 1) self.check_vectors(ss, 0, 0, 1) ss = sources.vertical_vectors_source(0, 0, 1, 1, figure=None) self.check_positions(ss, 0, 0, 1) self.check_scalars(ss, 1) self.check_vectors(ss, 0, 0, 1) # Check for lists as position vectors and as data ss = sources.vertical_vectors_source([0, 1], [0, 1], [0, 1], [2, 3], figure=None) self.check_positions(ss, [0, 1], [0, 1], [0, 1]) self.check_scalars(ss, [2, 3]) self.check_vectors(ss, [0, 0], [0, 0], [2, 3]) # Check for arrays as position vectors and a function as data x, y, z = np.random.random((3, 10)) zeros = np.zeros_like(x) f = lambda x, y, z: x + y + z ss = sources.vertical_vectors_source(x, y, z, f, figure=None) self.check_positions(ss, x, y, z) self.check_scalars(ss, f(x, y, z)) self.check_vectors(ss, zeros, zeros, z) ss = sources.vertical_vectors_source(x, y, z, figure=None) self.check_positions(ss, x, y, zeros) self.check_scalars(ss, z) self.check_vectors(ss, zeros, zeros, z) ################################################################################ # `TestSourceInfinite` ################################################################################ class TestVerticalVectorsSource(unittest.TestCase): def test_infinite(self): """ Check that passing in arrays with infinite values raises errors """ # Some arrays x = np.random.random((10, 3, 4)) y = np.random.random((10, 3, 4)) z = np.random.random((10, 3, 4)) u = np.random.random((10, 3, 4)) v = np.random.random((10, 3, 4)) w = np.random.random((10, 3, 4)) s = np.random.random((10, 3, 4)) # Add a few infinite values: u[2, 2, 1] = np.inf s[0, 0, 0] = -np.inf # Check value errors are raised because of the infinite values self.assertRaises(ValueError, sources.grid_source, x[0], y[0], z[0], scalars=s[0], figure=None) self.assertRaises(ValueError, sources.vertical_vectors_source, x, y, z, s, figure=None) self.assertRaises(ValueError, sources.array2d_source, x[0], y[0], s[0], figure=None) self.assertRaises(ValueError, sources.scalar_field, x, y, z, s, figure=None) self.assertRaises(ValueError, sources.scalar_scatter, x, y, z, s, figure=None) self.assertRaises(ValueError, sources.vector_scatter, x, y, z, u, v, w, figure=None) self.assertRaises(ValueError, sources.vector_field, x, y, z, u, v, w, figure=None) self.assertRaises(ValueError, sources.line_source, x[0, 0], y[0, 0], z[0, 0], s[0, 0], figure=None) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/csv_files/0000755000175100001440000000000011674464502020401 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/tests/csv_files/79.csv0000644000175100001440000000031011674464502021347 0ustar ischnellusers00000000000000319 680 180 690 129 620 762 689 762 318 368 710 720 710 629 168 160 689 716 731 736 729 316 729 729 710 769 290 719 680 318 389 162 289 162 718 729 319 790 680 890 362 319 760 316 729 380 319 728 716 mayavi-4.1.0/mayavi/tests/csv_files/54.csv0000644000175100001440000000233011674464502021344 0ustar ischnellusers000000000000008C TS KC 9H 4S 7D 2S 5D 3S AC 5C AD 5D AC 9C 7C 5H 8D TD KS 3H 7H 6S KC JS QH TD JC 2D 8S TH 8H 5C QS TC 9H 4D JC KS JS 7C 5H KC QH JD AS KH 4C AD 4S 5H KS 9C 7D 9H 8D 3S 5D 5C AH 6H 4H 5C 3H 2H 3S QH 5S 6S AS TD 8C 4H 7C TC KC 4C 3H 7S KS 7C 9C 6D KD 3H 4C QS QC AC KH JC 6S 5H 2H 2D KD 9D 7C AS JS AD QH TH 9D 8H TS 6D 3S AS AC 2H 4S 5C 5S TC KC JD 6C TS 3C QD AS 6H JS 2C 3D 9H KC 4H 8S KD 8S 9S 7C 2S 3S 6D 6S 4H KC 3C 8C 2D 7D 4D 9S 4S QH 4H JD 8C KC 7S TC 2D TS 8H QD AC 5C 3D KH QD 6C 6S AD AS 8H 2H QS 6S 8D 4C 8S 6C QH TC 6D 7D 9D 2S 8D 8C 4C TS 9S 9D 9C AC 3D 3C QS 2S 4H JH 3D 2D TD 8S 9H 5H QS 8S 6D 3C 8C JD AS 7H 7D 6H TD 9D AS JH 6C QC 9S KD JC AH 8S QS 4D TH AC TS 3C 3D 5C 5S 4D JS 3D 8H 6C TS 3S AD 8C 6D 7C 5D 5H 3S 5C JC 2H 5S 3D 5H 6H 2S KS 3D 5D JD 7H JS 8H KH 4H AS JS QS QC TC 6D 7C KS 3D QS TS 2H JS 4D AS 9S JC KD QD 5H 4D 5D KH 7H 3D JS KD 4H 2C 9H 6H 5C 9D 6C JC 2D TH 9S 7D 6D AS QD JH 4D JS 7C QS 5C 3H KH QD AD 8C 8H 3S TH 9D 5S AH 9S 4D 9D 8S 4H JS 3C TC 8D 2C KS 5H QD 3S TS 9H AH AD 8S 5C 7H 5D KD 9H 4D 3D 2D KS AD KS KC 9S 6D 2C QH 9D 9H TS TC 9C 6H 5D QH 4D AD 6D QC JS KH 9S 3H 9D JD 5C 4D 9H AS TC QH 2C 6D JC 9C 3C AD 9S KH 9D 7D KC 9C 7C JC JS KD 3H AS 3C 7D mayavi-4.1.0/mayavi/tests/csv_files/mydata.csv0000644000175100001440000000014611674464502022376 0ustar ischnellusers00000000000000% This is my sample data: % "Sample Name":"Radius":"Speed" Foo:12.5:85.4 Bar:14.1:87.6 Baz:14.3:89.0 mayavi-4.1.0/mayavi/tests/csv_files/webaccess.py0000644000175100001440000000240311674464502022711 0ustar ischnellusers00000000000000{ 'array': array([('2003-09-15', 10713.0), ('2003-10-15', 23482.0), ('2003-11-15', 8522.0), ('2003-12-15', 11776.0), ('2004-01-15', 6048.0), ('2004-02-15', 4196.0), ('2004-03-15', 3406.0), ('2004-04-15', 4475.0), ('2004-05-15', 5304.0), ('2004-06-15', 7551.0), ('2004-07-15', 4898.0), ('2004-08-15', 11499.0), ('2004-09-15', 7922.0), ('2004-10-15', 10261.0), ('2004-11-15', 11866.0), ('2004-12-15', 22312.0), ('2005-01-15', 10421.0), ('2005-02-15', 10564.0), ('2005-03-15', 13774.0), ('2005-04-15', 20336.0), ('2005-05-15', 26889.0), ('2005-06-15', 10080.0), ('2005-07-15', 13330.0), ('2005-08-15', 26420.0), ('2005-09-15', 27044.0), ('2005-10-15', 34789.0), ('2005-11-15', 34332.0), ('2005-12-15', 39317.0), ('2006-01-15', 41423.0), ('2006-02-15', 26403.0), ('2006-03-15', 34117.0), ('2006-04-15', 30342.0), ('2006-05-15', 36894.0), ('2006-06-15', 49460.0), ('2006-07-15', 31944.0)], dtype=[('data', 'S10'), ('number of visits', float)]), 'kwds': { 'comments': '#', 'delimiter': ',', 'dtype': { 'formats': ('S10', float), 'names': ('data', 'number of visits')}, 'skiprows': 2 } } mayavi-4.1.0/mayavi/tests/csv_files/99.csv0000644000175100001440000000563511674464502021370 0ustar ischnellusers00000000000000519432:525806 632382:518061 78864:613712 466580:530130 780495:510032 525895:525320 15991:714883 960290:502358 760018:511029 166800:575487 210884:564478 555151:523163 681146:515199 563395:522587 738250:512126 923525:503780 595148:520429 177108:572629 750923:511482 440902:532446 881418:505504 422489:534197 979858:501616 685893:514935 747477:511661 167214:575367 234140:559696 940238:503122 728969:512609 232083:560102 900971:504694 688801:514772 189664:569402 891022:505104 445689:531996 119570:591871 821453:508118 371084:539600 911745:504251 623655:518600 144361:582486 352442:541775 420726:534367 295298:549387 6530:787777 468397:529976 672336:515696 431861:533289 84228:610150 805376:508857 444409:532117 33833:663511 381850:538396 402931:536157 92901:604930 304825:548004 731917:512452 753734:511344 51894:637373 151578:580103 295075:549421 303590:548183 333594:544123 683952:515042 60090:628880 951420:502692 28335:674991 714940:513349 343858:542826 549279:523586 804571:508887 260653:554881 291399:549966 402342:536213 408889:535550 40328:652524 375856:539061 768907:510590 165993:575715 976327:501755 898500:504795 360404:540830 478714:529095 694144:514472 488726:528258 841380:507226 328012:544839 22389:690868 604053:519852 329514:544641 772965:510390 492798:527927 30125:670983 895603:504906 450785:531539 840237:507276 380711:538522 63577:625673 76801:615157 502694:527123 597706:520257 310484:547206 944468:502959 121283:591152 451131:531507 566499:522367 425373:533918 40240:652665 39130:654392 714926:513355 469219:529903 806929:508783 287970:550487 92189:605332 103841:599094 671839:515725 452048:531421 987837:501323 935192:503321 88585:607450 613883:519216 144551:582413 647359:517155 213902:563816 184120:570789 258126:555322 502546:527130 407655:535678 401528:536306 477490:529193 841085:507237 732831:512408 833000:507595 904694:504542 581435:521348 455545:531110 873558:505829 94916:603796 720176:513068 545034:523891 246348:557409 556452:523079 832015:507634 173663:573564 502634:527125 250732:556611 569786:522139 216919:563178 521815:525623 92304:605270 164446:576167 753413:511364 11410:740712 448845:531712 925072:503725 564888:522477 7062:780812 641155:517535 738878:512100 636204:517828 372540:539436 443162:532237 571192:522042 655350:516680 299741:548735 581914:521307 965471:502156 513441:526277 808682:508700 237589:559034 543300:524025 804712:508889 247511:557192 543486:524008 504383:526992 326529:545039 792493:509458 86033:609017 126554:589005 579379:521481 948026:502823 404777:535969 265767:554022 266876:553840 46631:643714 492397:527958 856106:506581 795757:509305 748946:511584 294694:549480 409781:535463 775887:510253 543747:523991 210592:564536 517119:525990 520253:525751 247926:557124 592141:520626 346580:542492 61083:627945 mayavi-4.1.0/mayavi/tests/csv_files/loc.py0000644000175100001440000001467511674464502021545 0ustar ischnellusers00000000000000{ 'array': array([('2003-02-19', 1436.0, 0.0), ('2003-02-20', 1603.0, 114.0), ('2003-02-21', 2637.0, 401.0), ('2003-02-22', 2921.0, 121.0), ('2003-02-23', 3674.0, 969.0), ('2003-02-24', 3674.0, 969.0), ('2003-02-25', 3808.0, 998.0), ('2003-02-26', 3808.0, 998.0), ('2003-02-27', 3808.0, 998.0), ('2003-02-28', 3847.0, 1054.0), ('2003-03-01', 3854.0, 1056.0), ('2003-03-03', 3865.0, 1114.0), ('2003-03-04', 3865.0, 1116.0), ('2003-03-18', 3865.0, 1116.0), ('2003-04-06', 3865.0, 1116.0), ('2003-04-11', 3865.0, 1116.0), ('2003-04-18', 3892.0, 1221.0), ('2003-04-19', 3943.0, 1273.0), ('2003-05-22', 3943.0, 1273.0), ('2003-05-25', 3943.0, 1273.0), ('2003-05-26', 4239.0, 1414.0), ('2003-05-27', 4714.0, 1668.0), ('2003-05-28', 5603.0, 1847.0), ('2003-05-29', 5735.0, 1883.0), ('2003-05-30', 6515.0, 2380.0), ('2003-05-31', 6927.0, 2393.0), ('2003-06-01', 7144.0, 2396.0), ('2003-06-02', 7152.0, 2400.0), ('2003-06-05', 7195.0, 2400.0), ('2003-06-06', 7212.0, 2400.0), ('2003-06-07', 7215.0, 2400.0), ('2003-06-08', 7228.0, 2390.0), ('2003-06-09', 7230.0, 2390.0), ('2003-06-13', 7356.0, 2387.0), ('2003-06-14', 7436.0, 2387.0), ('2003-06-15', 7436.0, 2387.0), ('2003-06-16', 7558.0, 2411.0), ('2003-06-17', 7568.0, 2411.0), ('2003-06-18', 7708.0, 2465.0), ('2003-06-19', 7796.0, 2460.0), ('2003-06-20', 7794.0, 2467.0), ('2003-06-21', 7823.0, 2479.0), ('2003-06-22', 9536.0, 2608.0), ('2003-06-23', 10196.0, 3153.0), ('2003-06-24', 10675.0, 3428.0), ('2003-06-28', 11165.0, 3571.0), ('2003-06-29', 11173.0, 3579.0), ('2003-06-30', 11173.0, 3579.0), ('2003-07-01', 11336.0, 3597.0), ('2003-07-02', 11344.0, 3597.0), ('2003-07-03', 11344.0, 3597.0), ('2003-07-04', 11344.0, 3597.0), ('2003-07-06', 11354.0, 3597.0), ('2003-07-07', 11354.0, 3597.0), ('2003-07-08', 11544.0, 3621.0), ('2003-07-10', 11544.0, 3621.0), ('2003-07-12', 11544.0, 3621.0), ('2003-07-13', 11544.0, 3621.0), ('2003-07-14', 11617.0, 3625.0), ('2003-07-15', 11617.0, 3625.0), ('2003-07-16', 11617.0, 3625.0), ('2003-07-17', 11617.0, 3625.0), ('2003-07-18', 11617.0, 3625.0), ('2003-07-19', 11617.0, 3625.0), ('2003-07-24', 11617.0, 3625.0), ('2003-07-26', 11617.0, 3625.0), ('2003-07-27', 11617.0, 3625.0), ('2003-07-28', 11617.0, 3625.0), ('2003-07-29', 11617.0, 3625.0), ('2003-07-30', 11617.0, 3625.0), ('2003-07-31', 11617.0, 3625.0), ('2003-08-01', 11617.0, 3625.0), ('2003-08-02', 11617.0, 3625.0), ('2003-08-06', 11617.0, 3625.0), ('2003-08-07', 11617.0, 3625.0), ('2003-08-09', 11617.0, 3625.0), ('2003-08-10', 11617.0, 3625.0), ('2003-08-17', 11617.0, 3625.0), ('2003-08-24', 11617.0, 3625.0), ('2003-08-25', 11617.0, 3625.0), ('2003-09-02', 11617.0, 3625.0), ('2003-09-07', 11617.0, 3625.0), ('2003-09-08', 11617.0, 3625.0), ('2003-09-09', 11617.0, 3625.0), ('2003-09-10', 11617.0, 3625.0), ('2003-09-12', 11617.0, 3625.0), ('2003-09-13', 11617.0, 3625.0), ('2003-09-14', 11617.0, 3625.0), ('2003-09-15', 11617.0, 3625.0), ('2003-09-16', 11617.0, 3625.0), ('2003-09-17', 11617.0, 3625.0), ('2003-09-18', 11954.0, 3536.0), ('2003-09-19', 11976.0, 3541.0), ('2003-09-20', 11976.0, 3541.0), ('2003-09-21', 11976.0, 3541.0), ('2003-09-22', 11976.0, 3541.0), ('2003-09-23', 12024.0, 3671.0), ('2003-09-25', 12024.0, 3671.0), ('2003-09-26', 12024.0, 3671.0), ('2003-09-28', 12024.0, 3671.0), ('2003-09-29', 12024.0, 3677.0), ('2003-09-30', 12393.0, 3760.0), ('2003-10-01', 12900.0, 3965.0), ('2003-10-02', 13241.0, 4310.0), ('2003-10-03', 13241.0, 4310.0), ('2003-10-04', 13310.0, 4356.0), ('2003-10-05', 12443.0, 4223.0), ('2003-10-06', 12415.0, 4223.0), ('2003-10-07', 12415.0, 4223.0), ('2003-10-08', 12417.0, 4223.0), ('2003-10-09', 12417.0, 4223.0), ('2003-10-10', 12875.0, 4431.0), ('2003-10-11', 12884.0, 4452.0), ('2003-10-12', 12970.0, 4492.0), ('2003-10-13', 12984.0, 4492.0), ('2003-10-14', 12984.0, 4492.0), ('2003-10-15', 12974.0, 4492.0), ('2003-10-16', 13051.0, 4492.0), ('2003-10-17', 13094.0, 4521.0), ('2003-10-18', 13131.0, 4561.0), ('2003-10-20', 13131.0, 4561.0), ('2003-10-21', 13131.0, 4561.0), ('2003-10-23', 13147.0, 4570.0), ('2003-10-24', 13268.0, 4413.0), ('2003-10-25', 13276.0, 4416.0), ('2003-10-26', 13372.0, 4409.0), ('2003-10-27', 13641.0, 4403.0), ('2003-10-28', 13699.0, 4409.0), ('2003-10-29', 13850.0, 4419.0), ('2003-10-30', 13848.0, 4431.0), ('2003-10-31', 13854.0, 4431.0), ('2003-11-03', 13704.0, 4437.0), ('2003-11-04', 13704.0, 4437.0), ('2003-11-05', 13711.0, 4437.0), ('2003-11-10', 13711.0, 4437.0), ('2003-11-11', 13872.0, 4634.0), ('2003-11-13', 13872.0, 4634.0), ('2003-11-14', 13872.0, 4634.0), ('2003-11-17', 13872.0, 4634.0), ('2003-11-18', 13850.0, 4682.0), ('2003-11-19', 13954.0, 4508.0), ('2003-11-20', 14086.0, 4555.0), ('2003-11-21', 14226.0, 4565.0), ('2003-11-22', 14231.0, 4565.0), ('2003-11-24', 14170.0, 4620.0), ('2003-11-25', 14170.0, 4620.0), ('2003-11-26', 14120.0, 4624.0), ('2003-11-27', 14543.0, 4673.0), ('2003-11-28', 14546.0, 4673.0), ('2003-11-29', 14546.0, 4673.0), ('2003-11-30', 15350.0, 4695.0), ('2003-12-01', 15350.0, 4695.0), ('2003-12-02', 15350.0, 4695.0), ('2003-12-03', 15350.0, 4695.0), ('2003-12-04', 15350.0, 4695.0), ('2003-12-08', 15350.0, 4695.0), ('2003-12-09', 15352.0, 4695.0), ('2003-12-10', 15352.0, 4695.0), ('2003-12-11', 15352.0, 4695.0), ('2003-12-12', 15352.0, 4695.0), ('2003-12-13', 15352.0, 4695.0), ('2003-12-15', 15474.0, 4695.0), ('2003-12-16', 16321.0, 4701.0), ('2003-12-17', 17469.0, 4878.0), ('2003-12-18', 17793.0, 5121.0), ('2003-12-19', 19327.0, 5002.0), ('2003-12-20', 19795.0, 5058.0), ('2003-12-21', 20048.0, 5121.0)], dtype=[('date', 'S10'), ('lines of code', float), ('lines of testcode', float)]), 'kwds': { 'comments': '#', 'delimiter': ',', 'dtype': { 'formats': ( 'S10', float, float), 'names': ( 'date', 'lines of code', 'lines of testcode')}, 'skiprows': 2 } } mayavi-4.1.0/mayavi/tests/csv_files/1col.py0000644000175100001440000000057711674464502021622 0ustar ischnellusers00000000000000{ 'array': array([(5.552,), (5.963,), (6.135,), (6.313,), (6.713,)], dtype=[('x-values', ' # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. The files in this directory are test cases for test_csv_sniff.py ---------------------------------------------------------------- For each xxx.csv and xxx.py the test involves these steps: 1. sniff the xxx.csv; detecting the keyword arguments for numpy.loadtxt 2. load the array using numpy.loadtxt with the detected keywords 3. load the xxx.py [using eval(open('xxx.py').read())] 4. compare the two numpy arrays obtained in step 2 and 3 mayavi-4.1.0/mayavi/tests/csv_files/82.py0000644000175100001440000004407211674464502021213 0ustar ischnellusers00000000000000{ 'array': array([ (4445.0, 2697.0, 5115.0, 718.0, 2209.0, 2212.0, 654.0, 4348.0, 3079.0, 6821.0, 7668.0, 3276.0, 8874.0, 4190.0, 3785.0, 2752.0, 9473.0, 7817.0, 9137.0, 496.0, 7338.0, 3434.0, 7152.0, 4355.0, 4552.0, 7917.0, 7827.0, 2460.0, 2350.0, 691.0, 3514.0, 5880.0, 3145.0, 7633.0, 7199.0, 3783.0, 5066.0, 7487.0, 3285.0, 1084.0, 8985.0, 760.0, 872.0, 8609.0, 8051.0, 1134.0, 9536.0, 5750.0, 9716.0, 9371.0, 7619.0, 5617.0, 275.0, 9721.0, 2997.0, 2698.0, 1887.0, 8825.0, 6372.0, 3014.0, 2113.0, 7122.0, 7050.0, 6775.0, 5948.0, 2758.0, 1219.0, 3539.0, 348.0, 7989.0, 2735.0, 9862.0, 1263.0, 8089.0, 6401.0, 9462.0, 3168.0, 2758.0, 3748.0, 5870.0), (1096.0, 20.0, 1318.0, 7586.0, 5167.0, 2642.0, 1443.0, 5741.0, 7621.0, 7030.0, 5526.0, 4244.0, 2348.0, 4641.0, 9827.0, 2448.0, 6918.0, 5883.0, 3737.0, 300.0, 7116.0, 6531.0, 567.0, 5997.0, 3971.0, 6623.0, 820.0, 6148.0, 3287.0, 1874.0, 7981.0, 8424.0, 7672.0, 7575.0, 6797.0, 6717.0, 1078.0, 5008.0, 4051.0, 8795.0, 5820.0, 346.0, 1851.0, 6463.0, 2117.0, 6058.0, 3407.0, 8211.0, 117.0, 4822.0, 1317.0, 4377.0, 4434.0, 5925.0, 8341.0, 4800.0, 1175.0, 4173.0, 690.0, 8978.0, 7470.0, 1295.0, 3799.0, 8724.0, 3509.0, 9849.0, 618.0, 3320.0, 7068.0, 9633.0, 2384.0, 7175.0, 544.0, 6583.0, 1908.0, 9983.0, 481.0, 4187.0, 9353.0, 9377.0), (9607.0, 7385.0, 521.0, 6084.0, 1364.0, 8983.0, 7623.0, 1585.0, 6935.0, 8551.0, 2574.0, 8267.0, 4781.0, 3834.0, 2764.0, 2084.0, 2669.0, 4656.0, 9343.0, 7709.0, 2203.0, 9328.0, 8004.0, 6192.0, 5856.0, 3555.0, 2260.0, 5118.0, 6504.0, 1839.0, 9227.0, 1259.0, 9451.0, 1388.0, 7909.0, 5733.0, 6968.0, 8519.0, 9973.0, 1663.0, 5315.0, 7571.0, 3035.0, 4325.0, 4283.0, 2304.0, 6438.0, 3815.0, 9213.0, 9806.0, 9536.0, 196.0, 5542.0, 6907.0, 2475.0, 1159.0, 5820.0, 9075.0, 9470.0, 2179.0, 9248.0, 1828.0, 4592.0, 9167.0, 3713.0, 4640.0, 47.0, 3637.0, 309.0, 7344.0, 6955.0, 346.0, 378.0, 9044.0, 8635.0, 7466.0, 5036.0, 9515.0, 6385.0, 9230.0), (7206.0, 3114.0, 7760.0, 1094.0, 6150.0, 5182.0, 7358.0, 7387.0, 4497.0, 955.0, 101.0, 1478.0, 7777.0, 6966.0, 7010.0, 8417.0, 6453.0, 4955.0, 3496.0, 107.0, 449.0, 8271.0, 131.0, 2948.0, 6185.0, 784.0, 5937.0, 8001.0, 6104.0, 8282.0, 4165.0, 3642.0, 710.0, 2390.0, 575.0, 715.0, 3089.0, 6964.0, 4217.0, 192.0, 5949.0, 7006.0, 715.0, 3328.0, 1152.0, 66.0, 8044.0, 4319.0, 1735.0, 146.0, 4818.0, 5456.0, 6451.0, 4113.0, 1063.0, 4781.0, 6799.0, 602.0, 1504.0, 6245.0, 6550.0, 1417.0, 1343.0, 2363.0, 3785.0, 5448.0, 4545.0, 9371.0, 5420.0, 5068.0, 4613.0, 4882.0, 4241.0, 5043.0, 7873.0, 8042.0, 8434.0, 3939.0, 9256.0, 2187.0), (3620.0, 8024.0, 577.0, 9997.0, 7377.0, 7682.0, 1314.0, 1158.0, 6282.0, 6310.0, 1896.0, 2509.0, 5436.0, 1732.0, 9480.0, 706.0, 496.0, 101.0, 6232.0, 7375.0, 2207.0, 2306.0, 110.0, 6772.0, 3433.0, 2878.0, 8140.0, 5933.0, 8688.0, 1399.0, 2210.0, 7332.0, 6172.0, 6403.0, 7333.0, 4044.0, 2291.0, 1790.0, 2446.0, 7390.0, 8698.0, 5723.0, 3678.0, 7104.0, 1825.0, 2040.0, 140.0, 3982.0, 4905.0, 4160.0, 2200.0, 5041.0, 2512.0, 1488.0, 2268.0, 1175.0, 7588.0, 8321.0, 8078.0, 7312.0, 977.0, 5257.0, 8465.0, 5068.0, 3453.0, 3096.0, 1651.0, 7906.0, 253.0, 9250.0, 6021.0, 8791.0, 8109.0, 6651.0, 3412.0, 345.0, 4778.0, 5152.0, 4883.0, 7505.0), (1074.0, 5438.0, 9008.0, 2679.0, 5397.0, 5429.0, 2652.0, 3403.0, 770.0, 9188.0, 4248.0, 2493.0, 4361.0, 8327.0, 9587.0, 707.0, 9525.0, 5913.0, 93.0, 1899.0, 328.0, 2876.0, 3604.0, 673.0, 8576.0, 6908.0, 7659.0, 2544.0, 3359.0, 3883.0, 5273.0, 6587.0, 3065.0, 1749.0, 3223.0, 604.0, 9925.0, 6941.0, 2823.0, 8767.0, 7039.0, 3290.0, 3214.0, 1787.0, 7904.0, 3421.0, 7137.0, 9560.0, 8451.0, 2669.0, 9219.0, 6332.0, 1576.0, 5477.0, 6755.0, 8348.0, 4164.0, 4307.0, 2984.0, 4012.0, 6629.0, 1044.0, 2874.0, 6541.0, 4942.0, 903.0, 1404.0, 9125.0, 5160.0, 8836.0, 4345.0, 2581.0, 460.0, 8438.0, 1538.0, 5507.0, 668.0, 3352.0, 2678.0, 6942.0), (4295.0, 1176.0, 5596.0, 1521.0, 3061.0, 9868.0, 7037.0, 7129.0, 8933.0, 6659.0, 5947.0, 5063.0, 3653.0, 9447.0, 9245.0, 2679.0, 767.0, 714.0, 116.0, 8558.0, 163.0, 3927.0, 8779.0, 158.0, 5093.0, 2447.0, 5782.0, 3967.0, 1716.0, 931.0, 7772.0, 8164.0, 1117.0, 9244.0, 5783.0, 7776.0, 3846.0, 8862.0, 6014.0, 2330.0, 6947.0, 1777.0, 3112.0, 6008.0, 3491.0, 1906.0, 5952.0, 314.0, 4602.0, 8994.0, 5919.0, 9214.0, 3995.0, 5026.0, 7688.0, 6809.0, 5003.0, 3128.0, 2509.0, 7477.0, 110.0, 8971.0, 3982.0, 8539.0, 2980.0, 4689.0, 6343.0, 5411.0, 2992.0, 5270.0, 5247.0, 9260.0, 2269.0, 7474.0, 1042.0, 7162.0, 5206.0, 1232.0, 4556.0, 4757.0), (510.0, 3556.0, 5377.0, 1406.0, 5721.0, 4946.0, 2635.0, 7847.0, 4251.0, 8293.0, 8281.0, 6351.0, 4912.0, 287.0, 2870.0, 3380.0, 3948.0, 5322.0, 3840.0, 4738.0, 9563.0, 1906.0, 6298.0, 3234.0, 8959.0, 1562.0, 6297.0, 8835.0, 7861.0, 239.0, 6618.0, 1322.0, 2553.0, 2213.0, 5053.0, 5446.0, 4402.0, 6500.0, 5182.0, 8585.0, 6900.0, 5756.0, 9661.0, 903.0, 5186.0, 7687.0, 5998.0, 7997.0, 8081.0, 8955.0, 4835.0, 6069.0, 2621.0, 1581.0, 732.0, 9564.0, 1082.0, 1853.0, 5442.0, 1342.0, 520.0, 1737.0, 3703.0, 5321.0, 4793.0, 2776.0, 1508.0, 1647.0, 9101.0, 2499.0, 6891.0, 4336.0, 7012.0, 3329.0, 3212.0, 1442.0, 9993.0, 3988.0, 4930.0, 7706.0), (9444.0, 3401.0, 5891.0, 9716.0, 1228.0, 7107.0, 109.0, 3563.0, 2700.0, 6161.0, 5039.0, 4992.0, 2242.0, 8541.0, 7372.0, 2067.0, 1294.0, 3058.0, 1306.0, 320.0, 8881.0, 5756.0, 9326.0, 411.0, 8650.0, 8824.0, 5495.0, 8282.0, 8397.0, 2000.0, 1228.0, 7817.0, 2099.0, 6473.0, 3571.0, 5994.0, 4447.0, 1299.0, 5991.0, 543.0, 7874.0, 2297.0, 1651.0, 101.0, 2093.0, 3463.0, 9189.0, 6872.0, 6118.0, 872.0, 1008.0, 1779.0, 2805.0, 9084.0, 4048.0, 2123.0, 5877.0, 55.0, 3075.0, 1737.0, 9459.0, 4535.0, 6453.0, 3644.0, 108.0, 5982.0, 4437.0, 5213.0, 1340.0, 6967.0, 9943.0, 5815.0, 669.0, 8074.0, 1838.0, 6979.0, 9132.0, 9315.0, 715.0, 5048.0), (3327.0, 4030.0, 7177.0, 6336.0, 9933.0, 5296.0, 2621.0, 4785.0, 2755.0, 4832.0, 2512.0, 2118.0, 2244.0, 4407.0, 2170.0, 499.0, 7532.0, 9742.0, 5051.0, 7687.0, 970.0, 6924.0, 3527.0, 4694.0, 5145.0, 1306.0, 2165.0, 5940.0, 2425.0, 8910.0, 3513.0, 1909.0, 6983.0, 346.0, 6377.0, 4304.0, 9330.0, 7203.0, 6605.0, 3709.0, 3346.0, 970.0, 369.0, 9737.0, 5811.0, 4427.0, 9939.0, 3693.0, 8436.0, 5566.0, 1977.0, 3728.0, 2399.0, 3985.0, 8303.0, 2492.0, 5366.0, 9802.0, 9193.0, 7296.0, 1033.0, 5060.0, 9144.0, 2766.0, 1151.0, 7629.0, 5169.0, 5995.0, 58.0, 7619.0, 7565.0, 4208.0, 1713.0, 6279.0, 3209.0, 4908.0, 9224.0, 7409.0, 1325.0, 8540.0), (6882.0, 1265.0, 1775.0, 3648.0, 4690.0, 959.0, 5837.0, 4520.0, 5394.0, 1378.0, 9485.0, 1360.0, 4018.0, 578.0, 9174.0, 2932.0, 9890.0, 3696.0, 116.0, 1723.0, 1178.0, 9355.0, 7063.0, 1594.0, 1918.0, 8574.0, 7594.0, 7942.0, 1547.0, 6166.0, 7888.0, 354.0, 6932.0, 4651.0, 1010.0, 7759.0, 6905.0, 661.0, 7689.0, 6092.0, 9292.0, 3845.0, 9605.0, 8443.0, 443.0, 8275.0, 5163.0, 7720.0, 7265.0, 6356.0, 7779.0, 1798.0, 1754.0, 5225.0, 6661.0, 1180.0, 8024.0, 5666.0, 88.0, 9153.0, 1840.0, 3508.0, 1193.0, 4445.0, 2648.0, 3538.0, 6243.0, 6375.0, 8107.0, 5902.0, 5423.0, 2520.0, 1122.0, 5015.0, 6113.0, 8859.0, 9370.0, 966.0, 8673.0, 2442.0), (7338.0, 3423.0, 4723.0, 6533.0, 848.0, 8041.0, 7921.0, 8277.0, 4094.0, 5368.0, 7252.0, 8852.0, 9166.0, 2250.0, 2801.0, 6125.0, 8093.0, 5738.0, 4038.0, 9808.0, 7359.0, 9494.0, 601.0, 9116.0, 4946.0, 2702.0, 5573.0, 2921.0, 9862.0, 1462.0, 1269.0, 2410.0, 4171.0, 2709.0, 7508.0, 6241.0, 7522.0, 615.0, 2407.0, 8200.0, 4189.0, 5492.0, 5649.0, 7353.0, 2590.0, 5203.0, 4274.0, 710.0, 7329.0, 9063.0, 956.0, 8371.0, 3722.0, 4253.0, 4785.0, 1194.0, 4828.0, 4717.0, 4548.0, 940.0, 983.0, 2575.0, 4511.0, 2938.0, 1827.0, 2027.0, 2700.0, 1236.0, 841.0, 5760.0, 1680.0, 6260.0, 2373.0, 3851.0, 1841.0, 4968.0, 1172.0, 5179.0, 7175.0, 3509.0), (4420.0, 1327.0, 3560.0, 2376.0, 6260.0, 2988.0, 9537.0, 4064.0, 4829.0, 8872.0, 9598.0, 3228.0, 1792.0, 7118.0, 9962.0, 9336.0, 4368.0, 9189.0, 6857.0, 1829.0, 9863.0, 6287.0, 7303.0, 7769.0, 2707.0, 8257.0, 2391.0, 2009.0, 3975.0, 4993.0, 3068.0, 9835.0, 3427.0, 341.0, 8412.0, 2134.0, 4034.0, 8511.0, 6421.0, 3041.0, 9012.0, 2983.0, 7289.0, 100.0, 1355.0, 7904.0, 9186.0, 6920.0, 5856.0, 2008.0, 6545.0, 8331.0, 3655.0, 5011.0, 839.0, 8041.0, 9255.0, 6524.0, 3862.0, 8788.0, 62.0, 7455.0, 3513.0, 5003.0, 8413.0, 3918.0, 2076.0, 7960.0, 6108.0, 3638.0, 6999.0, 3436.0, 1441.0, 4858.0, 4181.0, 1866.0, 8731.0, 7745.0, 3744.0, 1000.0), (356.0, 8296.0, 8325.0, 1058.0, 1277.0, 4743.0, 3850.0, 2388.0, 6079.0, 6462.0, 2815.0, 5620.0, 8495.0, 5378.0, 75.0, 4324.0, 3441.0, 9870.0, 1113.0, 165.0, 1544.0, 1179.0, 2834.0, 562.0, 6176.0, 2313.0, 6836.0, 8839.0, 2986.0, 9454.0, 5199.0, 6888.0, 1927.0, 5866.0, 8760.0, 320.0, 1792.0, 8296.0, 7898.0, 6121.0, 7241.0, 5886.0, 5814.0, 2815.0, 8336.0, 1576.0, 4314.0, 3109.0, 2572.0, 6011.0, 2086.0, 9061.0, 9403.0, 3947.0, 5487.0, 9731.0, 7281.0, 3159.0, 1819.0, 1334.0, 3181.0, 5844.0, 5114.0, 9898.0, 4634.0, 2531.0, 4412.0, 6430.0, 4262.0, 8482.0, 4546.0, 4555.0, 6804.0, 2607.0, 9421.0, 686.0, 8649.0, 8860.0, 7794.0, 6672.0), (9870.0, 152.0, 1558.0, 4963.0, 8750.0, 4754.0, 6521.0, 6256.0, 8818.0, 5208.0, 5691.0, 9659.0, 8377.0, 9725.0, 5050.0, 5343.0, 2539.0, 6101.0, 1844.0, 9700.0, 7750.0, 8114.0, 5357.0, 3001.0, 8830.0, 4438.0, 199.0, 9545.0, 8496.0, 43.0, 2078.0, 327.0, 9397.0, 106.0, 6090.0, 8181.0, 8646.0, 6414.0, 7499.0, 5450.0, 4850.0, 6273.0, 5014.0, 4131.0, 7639.0, 3913.0, 6571.0, 8534.0, 9703.0, 4391.0, 7618.0, 445.0, 1320.0, 5.0, 1894.0, 6771.0, 7383.0, 9191.0, 4708.0, 9706.0, 6939.0, 7937.0, 8726.0, 9382.0, 5216.0, 3685.0, 2247.0, 9029.0, 8154.0, 1738.0, 9984.0, 2626.0, 9438.0, 4167.0, 6351.0, 5060.0, 29.0, 1218.0, 1239.0, 4785.0), (192.0, 5213.0, 8297.0, 8974.0, 4032.0, 6966.0, 5717.0, 1179.0, 6523.0, 4679.0, 9513.0, 1481.0, 3041.0, 5355.0, 9303.0, 9154.0, 1389.0, 8702.0, 6589.0, 7818.0, 6336.0, 3539.0, 5538.0, 3094.0, 6646.0, 6702.0, 6266.0, 2759.0, 4608.0, 4452.0, 617.0, 9406.0, 8064.0, 6379.0, 444.0, 5602.0, 4950.0, 1810.0, 8391.0, 1536.0, 316.0, 8714.0, 1178.0, 5182.0, 5863.0, 5110.0, 5372.0, 4954.0, 1978.0, 2971.0, 5680.0, 4863.0, 2255.0, 4630.0, 5723.0, 2168.0, 538.0, 1692.0, 1319.0, 7540.0, 440.0, 6430.0, 6266.0, 7712.0, 7385.0, 5702.0, 620.0, 641.0, 3136.0, 7350.0, 1478.0, 3155.0, 2820.0, 9109.0, 6261.0, 1122.0, 4470.0, 14.0, 8493.0, 2095.0), (1046.0, 4301.0, 6082.0, 474.0, 4974.0, 7822.0, 2102.0, 5161.0, 5172.0, 6946.0, 8074.0, 9716.0, 6586.0, 9962.0, 9749.0, 5015.0, 2217.0, 995.0, 5388.0, 4402.0, 7652.0, 6399.0, 6539.0, 1349.0, 8101.0, 3677.0, 1328.0, 9612.0, 7922.0, 2879.0, 231.0, 5887.0, 2655.0, 508.0, 4357.0, 4964.0, 3554.0, 5930.0, 6236.0, 7384.0, 4614.0, 280.0, 3093.0, 9600.0, 2110.0, 7863.0, 2631.0, 6626.0, 6620.0, 68.0, 1311.0, 7198.0, 7561.0, 1768.0, 5139.0, 1431.0, 221.0, 230.0, 2940.0, 968.0, 5283.0, 6517.0, 2146.0, 1646.0, 869.0, 9402.0, 7068.0, 8645.0, 7058.0, 1765.0, 9690.0, 4152.0, 2926.0, 9504.0, 2939.0, 7504.0, 6074.0, 2944.0, 6470.0, 7859.0), (4659.0, 736.0, 4951.0, 9344.0, 1927.0, 6271.0, 8837.0, 8711.0, 3241.0, 6579.0, 7660.0, 5499.0, 5616.0, 3743.0, 5801.0, 4682.0, 9748.0, 8796.0, 779.0, 1833.0, 4549.0, 8138.0, 4026.0, 775.0, 4170.0, 2432.0, 4174.0, 3741.0, 7540.0, 8017.0, 2833.0, 4027.0, 396.0, 811.0, 2871.0, 1150.0, 9809.0, 2719.0, 9199.0, 8504.0, 1224.0, 540.0, 2051.0, 3519.0, 7982.0, 7367.0, 2761.0, 308.0, 3358.0, 6505.0, 2050.0, 4836.0, 5090.0, 7864.0, 805.0, 2566.0, 2409.0, 6876.0, 3361.0, 8622.0, 5572.0, 5895.0, 3280.0, 441.0, 7893.0, 8105.0, 1634.0, 2929.0, 274.0, 3926.0, 7786.0, 6123.0, 8233.0, 9921.0, 2674.0, 5340.0, 1445.0, 203.0, 4585.0, 3837.0), (5759.0, 338.0, 7444.0, 7968.0, 7742.0, 3755.0, 1591.0, 4839.0, 1705.0, 650.0, 7061.0, 2461.0, 9230.0, 9391.0, 9373.0, 2413.0, 1213.0, 431.0, 7801.0, 4994.0, 2380.0, 2703.0, 6161.0, 6878.0, 8331.0, 2538.0, 6093.0, 1275.0, 5065.0, 5062.0, 2839.0, 582.0, 1014.0, 8109.0, 3525.0, 1544.0, 1569.0, 8622.0, 7944.0, 2905.0, 6120.0, 1564.0, 1839.0, 5570.0, 7579.0, 1318.0, 2677.0, 5257.0, 4418.0, 5601.0, 7935.0, 7656.0, 5192.0, 1864.0, 5886.0, 6083.0, 5580.0, 6202.0, 8869.0, 1636.0, 7907.0, 4759.0, 9082.0, 5854.0, 3185.0, 7631.0, 6854.0, 5872.0, 5632.0, 5280.0, 1431.0, 2077.0, 9717.0, 7431.0, 4256.0, 8261.0, 9680.0, 4487.0, 4752.0, 4286.0), (1571.0, 1428.0, 8599.0, 1230.0, 7772.0, 4221.0, 8523.0, 9049.0, 4042.0, 8726.0, 7567.0, 6736.0, 9033.0, 2104.0, 4879.0, 4967.0, 6334.0, 6716.0, 3994.0, 1269.0, 8995.0, 6539.0, 3610.0, 7667.0, 6560.0, 6065.0, 874.0, 848.0, 4597.0, 1711.0, 7161.0, 4811.0, 6734.0, 5723.0, 6356.0, 6026.0, 9183.0, 2586.0, 5636.0, 1092.0, 7779.0, 7923.0, 8747.0, 6887.0, 7505.0, 9909.0, 1792.0, 3233.0, 4526.0, 3176.0, 1508.0, 8043.0, 720.0, 5212.0, 6046.0, 4988.0, 709.0, 5277.0, 8256.0, 3642.0, 1391.0, 5803.0, 1468.0, 2145.0, 3970.0, 6301.0, 7767.0, 2359.0, 8487.0, 9771.0, 8785.0, 7520.0, 856.0, 1605.0, 8972.0, 2402.0, 2386.0, 991.0, 1383.0, 5963.0)], dtype=[('Column 1', float), ('Column 2', float), ('Column 3', float), ('Column 4', float), ('Column 5', float), ('Column 6', float), ('Column 7', float), ('Column 8', float), ('Column 9', float), ('Column 10', float), ('Column 11', float), ('Column 12', float), ('Column 13', float), ('Column 14', float), ('Column 15', float), ('Column 16', float), ('Column 17', float), ('Column 18', float), ('Column 19', float), ('Column 20', float), ('Column 21', float), ('Column 22', float), ('Column 23', float), ('Column 24', float), ('Column 25', float), ('Column 26', float), ('Column 27', float), ('Column 28', float), ('Column 29', float), ('Column 30', float), ('Column 31', float), ('Column 32', float), ('Column 33', float), ('Column 34', float), ('Column 35', float), ('Column 36', float), ('Column 37', float), ('Column 38', float), ('Column 39', float), ('Column 40', float), ('Column 41', float), ('Column 42', float), ('Column 43', float), ('Column 44', float), ('Column 45', float), ('Column 46', float), ('Column 47', float), ('Column 48', float), ('Column 49', float), ('Column 50', float), ('Column 51', float), ('Column 52', float), ('Column 53', float), ('Column 54', float), ('Column 55', float), ('Column 56', float), ('Column 57', float), ('Column 58', float), ('Column 59', float), ('Column 60', float), ('Column 61', float), ('Column 62', float), ('Column 63', float), ('Column 64', float), ('Column 65', float), ('Column 66', float), ('Column 67', float), ('Column 68', float), ('Column 69', float), ('Column 70', float), ('Column 71', float), ('Column 72', float), ('Column 73', float), ('Column 74', float), ('Column 75', float), ('Column 76', float), ('Column 77', float), ('Column 78', float), ('Column 79', float), ('Column 80', float)]), 'kwds': { 'comments': '#', 'delimiter': ',', 'dtype': { 'formats': 80 * (float,), 'names': ( 'Column 1', 'Column 2', 'Column 3', 'Column 4', 'Column 5', 'Column 6', 'Column 7', 'Column 8', 'Column 9', 'Column 10', 'Column 11', 'Column 12', 'Column 13', 'Column 14', 'Column 15', 'Column 16', 'Column 17', 'Column 18', 'Column 19', 'Column 20', 'Column 21', 'Column 22', 'Column 23', 'Column 24', 'Column 25', 'Column 26', 'Column 27', 'Column 28', 'Column 29', 'Column 30', 'Column 31', 'Column 32', 'Column 33', 'Column 34', 'Column 35', 'Column 36', 'Column 37', 'Column 38', 'Column 39', 'Column 40', 'Column 41', 'Column 42', 'Column 43', 'Column 44', 'Column 45', 'Column 46', 'Column 47', 'Column 48', 'Column 49', 'Column 50', 'Column 51', 'Column 52', 'Column 53', 'Column 54', 'Column 55', 'Column 56', 'Column 57', 'Column 58', 'Column 59', 'Column 60', 'Column 61', 'Column 62', 'Column 63', 'Column 64', 'Column 65', 'Column 66', 'Column 67', 'Column 68', 'Column 69', 'Column 70', 'Column 71', 'Column 72', 'Column 73', 'Column 74', 'Column 75', 'Column 76', 'Column 77', 'Column 78', 'Column 79', 'Column 80')}, 'skiprows': 0 } } mayavi-4.1.0/mayavi/tests/csv_files/99.py0000644000175100001440000001200511674464502021212 0ustar ischnellusers00000000000000{ 'array': array([(519432.0, 525806.0), (632382.0, 518061.0), (78864.0, 613712.0), (466580.0, 530130.0), (780495.0, 510032.0), (525895.0, 525320.0), (15991.0, 714883.0), (960290.0, 502358.0), (760018.0, 511029.0), (166800.0, 575487.0), (210884.0, 564478.0), (555151.0, 523163.0), (681146.0, 515199.0), (563395.0, 522587.0), (738250.0, 512126.0), (923525.0, 503780.0), (595148.0, 520429.0), (177108.0, 572629.0), (750923.0, 511482.0), (440902.0, 532446.0), (881418.0, 505504.0), (422489.0, 534197.0), (979858.0, 501616.0), (685893.0, 514935.0), (747477.0, 511661.0), (167214.0, 575367.0), (234140.0, 559696.0), (940238.0, 503122.0), (728969.0, 512609.0), (232083.0, 560102.0), (900971.0, 504694.0), (688801.0, 514772.0), (189664.0, 569402.0), (891022.0, 505104.0), (445689.0, 531996.0), (119570.0, 591871.0), (821453.0, 508118.0), (371084.0, 539600.0), (911745.0, 504251.0), (623655.0, 518600.0), (144361.0, 582486.0), (352442.0, 541775.0), (420726.0, 534367.0), (295298.0, 549387.0), (6530.0, 787777.0), (468397.0, 529976.0), (672336.0, 515696.0), (431861.0, 533289.0), (84228.0, 610150.0), (805376.0, 508857.0), (444409.0, 532117.0), (33833.0, 663511.0), (381850.0, 538396.0), (402931.0, 536157.0), (92901.0, 604930.0), (304825.0, 548004.0), (731917.0, 512452.0), (753734.0, 511344.0), (51894.0, 637373.0), (151578.0, 580103.0), (295075.0, 549421.0), (303590.0, 548183.0), (333594.0, 544123.0), (683952.0, 515042.0), (60090.0, 628880.0), (951420.0, 502692.0), (28335.0, 674991.0), (714940.0, 513349.0), (343858.0, 542826.0), (549279.0, 523586.0), (804571.0, 508887.0), (260653.0, 554881.0), (291399.0, 549966.0), (402342.0, 536213.0), (408889.0, 535550.0), (40328.0, 652524.0), (375856.0, 539061.0), (768907.0, 510590.0), (165993.0, 575715.0), (976327.0, 501755.0), (898500.0, 504795.0), (360404.0, 540830.0), (478714.0, 529095.0), (694144.0, 514472.0), (488726.0, 528258.0), (841380.0, 507226.0), (328012.0, 544839.0), (22389.0, 690868.0), (604053.0, 519852.0), (329514.0, 544641.0), (772965.0, 510390.0), (492798.0, 527927.0), (30125.0, 670983.0), (895603.0, 504906.0), (450785.0, 531539.0), (840237.0, 507276.0), (380711.0, 538522.0), (63577.0, 625673.0), (76801.0, 615157.0), (502694.0, 527123.0), (597706.0, 520257.0), (310484.0, 547206.0), (944468.0, 502959.0), (121283.0, 591152.0), (451131.0, 531507.0), (566499.0, 522367.0), (425373.0, 533918.0), (40240.0, 652665.0), (39130.0, 654392.0), (714926.0, 513355.0), (469219.0, 529903.0), (806929.0, 508783.0), (287970.0, 550487.0), (92189.0, 605332.0), (103841.0, 599094.0), (671839.0, 515725.0), (452048.0, 531421.0), (987837.0, 501323.0), (935192.0, 503321.0), (88585.0, 607450.0), (613883.0, 519216.0), (144551.0, 582413.0), (647359.0, 517155.0), (213902.0, 563816.0), (184120.0, 570789.0), (258126.0, 555322.0), (502546.0, 527130.0), (407655.0, 535678.0), (401528.0, 536306.0), (477490.0, 529193.0), (841085.0, 507237.0), (732831.0, 512408.0), (833000.0, 507595.0), (904694.0, 504542.0), (581435.0, 521348.0), (455545.0, 531110.0), (873558.0, 505829.0), (94916.0, 603796.0), (720176.0, 513068.0), (545034.0, 523891.0), (246348.0, 557409.0), (556452.0, 523079.0), (832015.0, 507634.0), (173663.0, 573564.0), (502634.0, 527125.0), (250732.0, 556611.0), (569786.0, 522139.0), (216919.0, 563178.0), (521815.0, 525623.0), (92304.0, 605270.0), (164446.0, 576167.0), (753413.0, 511364.0), (11410.0, 740712.0), (448845.0, 531712.0), (925072.0, 503725.0), (564888.0, 522477.0), (7062.0, 780812.0), (641155.0, 517535.0), (738878.0, 512100.0), (636204.0, 517828.0), (372540.0, 539436.0), (443162.0, 532237.0), (571192.0, 522042.0), (655350.0, 516680.0), (299741.0, 548735.0), (581914.0, 521307.0), (965471.0, 502156.0), (513441.0, 526277.0), (808682.0, 508700.0), (237589.0, 559034.0), (543300.0, 524025.0), (804712.0, 508889.0), (247511.0, 557192.0), (543486.0, 524008.0), (504383.0, 526992.0), (326529.0, 545039.0), (792493.0, 509458.0), (86033.0, 609017.0), (126554.0, 589005.0), (579379.0, 521481.0), (948026.0, 502823.0), (404777.0, 535969.0), (265767.0, 554022.0), (266876.0, 553840.0), (46631.0, 643714.0), (492397.0, 527958.0), (856106.0, 506581.0), (795757.0, 509305.0), (748946.0, 511584.0), (294694.0, 549480.0), (409781.0, 535463.0), (775887.0, 510253.0), (543747.0, 523991.0), (210592.0, 564536.0), (517119.0, 525990.0), (520253.0, 525751.0), (247926.0, 557124.0), (592141.0, 520626.0), (346580.0, 542492.0), (61083.0, 627945.0)], dtype=[('Column 1', float), ('Column 2', float)]), 'kwds': { 'comments': '#', 'delimiter': ':', 'dtype': { 'formats': (float, float), 'names': ('Column 1', 'Column 2')}, 'skiprows': 0 } } mayavi-4.1.0/mayavi/tests/csv_files/multi-col.py0000644000175100001440000000202111674464502022653 0ustar ischnellusers00000000000000{ 'array': array([ (5.552, 1.942, 1.761, 2.6, 6.8), (5.963, 1.885, 1.775, 2.7, 6.9), (6.135, 1.791, 1.792, 2.5, 6.6), (6.313, 1.774, 1.884, 2.6, 6.6), (6.713, 1.762, 1.943, 2.5, 6.7)], dtype=[('Column 1', float), ('Column 2', float), ('Column 3', float), ('Column 4', float), ('Column 5', float)]), 'kwds': { 'comments': '#', 'delimiter': None, 'dtype': { 'formats': ( float, float, float, float, float), 'names': ( 'Column 1', 'Column 2', 'Column 3', 'Column 4', 'Column 5')}, 'skiprows': 0 } } mayavi-4.1.0/mayavi/tests/csv_files/mydata.py0000644000175100001440000000062111674464502022231 0ustar ischnellusers00000000000000{ 'array': array([('Foo', 12.5, 85.4), ('Bar', 14.1, 87.6), ('Baz', 14.3, 89.0)], dtype=[('Sample Name', 'S3'), ('Radius', float), ('Speed', float)]), 'kwds': { 'comments': '%', 'delimiter': ':', 'dtype': { 'formats': ('S3', float, float), 'names': ('Sample Name', 'Radius', 'Speed')}, 'skiprows': 3 } } mayavi-4.1.0/mayavi/tests/csv_files/11.py0000644000175100001440000000776411674464502021212 0ustar ischnellusers00000000000000{ 'array': array([ (8.0, 2.0, 22.0, 97.0, 38.0, 15.0, 0.0, 40.0, 0.0, 75.0, 4.0, 5.0, 7.0, 78.0, 52.0, 12.0, 50.0, 77.0, 91.0, 8.0), (49.0, 49.0, 99.0, 40.0, 17.0, 81.0, 18.0, 57.0, 60.0, 87.0, 17.0, 40.0, 98.0, 43.0, 69.0, 48.0, 4.0, 56.0, 62.0, 0.0), (81.0, 49.0, 31.0, 73.0, 55.0, 79.0, 14.0, 29.0, 93.0, 71.0, 40.0, 67.0, 53.0, 88.0, 30.0, 3.0, 49.0, 13.0, 36.0, 65.0), (52.0, 70.0, 95.0, 23.0, 4.0, 60.0, 11.0, 42.0, 69.0, 24.0, 68.0, 56.0, 1.0, 32.0, 56.0, 71.0, 37.0, 2.0, 36.0, 91.0), (22.0, 31.0, 16.0, 71.0, 51.0, 67.0, 63.0, 89.0, 41.0, 92.0, 36.0, 54.0, 22.0, 40.0, 40.0, 28.0, 66.0, 33.0, 13.0, 80.0), (24.0, 47.0, 32.0, 60.0, 99.0, 3.0, 45.0, 2.0, 44.0, 75.0, 33.0, 53.0, 78.0, 36.0, 84.0, 20.0, 35.0, 17.0, 12.0, 50.0), (32.0, 98.0, 81.0, 28.0, 64.0, 23.0, 67.0, 10.0, 26.0, 38.0, 40.0, 67.0, 59.0, 54.0, 70.0, 66.0, 18.0, 38.0, 64.0, 70.0), (67.0, 26.0, 20.0, 68.0, 2.0, 62.0, 12.0, 20.0, 95.0, 63.0, 94.0, 39.0, 63.0, 8.0, 40.0, 91.0, 66.0, 49.0, 94.0, 21.0), (24.0, 55.0, 58.0, 5.0, 66.0, 73.0, 99.0, 26.0, 97.0, 17.0, 78.0, 78.0, 96.0, 83.0, 14.0, 88.0, 34.0, 89.0, 63.0, 72.0), (21.0, 36.0, 23.0, 9.0, 75.0, 0.0, 76.0, 44.0, 20.0, 45.0, 35.0, 14.0, 0.0, 61.0, 33.0, 97.0, 34.0, 31.0, 33.0, 95.0), (78.0, 17.0, 53.0, 28.0, 22.0, 75.0, 31.0, 67.0, 15.0, 94.0, 3.0, 80.0, 4.0, 62.0, 16.0, 14.0, 9.0, 53.0, 56.0, 92.0), (16.0, 39.0, 5.0, 42.0, 96.0, 35.0, 31.0, 47.0, 55.0, 58.0, 88.0, 24.0, 0.0, 17.0, 54.0, 24.0, 36.0, 29.0, 85.0, 57.0), (86.0, 56.0, 0.0, 48.0, 35.0, 71.0, 89.0, 7.0, 5.0, 44.0, 44.0, 37.0, 44.0, 60.0, 21.0, 58.0, 51.0, 54.0, 17.0, 58.0), (19.0, 80.0, 81.0, 68.0, 5.0, 94.0, 47.0, 69.0, 28.0, 73.0, 92.0, 13.0, 86.0, 52.0, 17.0, 77.0, 4.0, 89.0, 55.0, 40.0), (4.0, 52.0, 8.0, 83.0, 97.0, 35.0, 99.0, 16.0, 7.0, 97.0, 57.0, 32.0, 16.0, 26.0, 26.0, 79.0, 33.0, 27.0, 98.0, 66.0), (88.0, 36.0, 68.0, 87.0, 57.0, 62.0, 20.0, 72.0, 3.0, 46.0, 33.0, 67.0, 46.0, 55.0, 12.0, 32.0, 63.0, 93.0, 53.0, 69.0), (4.0, 42.0, 16.0, 73.0, 38.0, 25.0, 39.0, 11.0, 24.0, 94.0, 72.0, 18.0, 8.0, 46.0, 29.0, 32.0, 40.0, 62.0, 76.0, 36.0), (20.0, 69.0, 36.0, 41.0, 72.0, 30.0, 23.0, 88.0, 34.0, 62.0, 99.0, 69.0, 82.0, 67.0, 59.0, 85.0, 74.0, 4.0, 36.0, 16.0), (20.0, 73.0, 35.0, 29.0, 78.0, 31.0, 90.0, 1.0, 74.0, 31.0, 49.0, 71.0, 48.0, 86.0, 81.0, 16.0, 23.0, 57.0, 5.0, 54.0), (1.0, 70.0, 54.0, 71.0, 83.0, 51.0, 54.0, 69.0, 16.0, 92.0, 33.0, 48.0, 61.0, 43.0, 52.0, 1.0, 89.0, 19.0, 67.0, 48.0)], dtype=[('Column 1', float), ('Column 2', float), ('Column 3', float), ('Column 4', float), ('Column 5', float), ('Column 6', float), ('Column 7', float), ('Column 8', float), ('Column 9', float), ('Column 10', float), ('Column 11', float), ('Column 12', float), ('Column 13', float), ('Column 14', float), ('Column 15', float), ('Column 16', float), ('Column 17', float), ('Column 18', float), ('Column 19', float), ('Column 20', float)]), 'kwds': { 'comments': '#', 'delimiter': None, 'dtype': { 'formats': 20 * (float,), 'names': ( 'Column 1', 'Column 2', 'Column 3', 'Column 4', 'Column 5', 'Column 6', 'Column 7', 'Column 8', 'Column 9', 'Column 10', 'Column 11', 'Column 12', 'Column 13', 'Column 14', 'Column 15', 'Column 16', 'Column 17', 'Column 18', 'Column 19', 'Column 20')}, 'skiprows': 0 } } mayavi-4.1.0/mayavi/tests/csv_files/OObeta3.py0000644000175100001440000000111011674464502022200 0ustar ischnellusers00000000000000{ 'array': array([('05/23/93', 25.0, 12.43), ('04/19/08', 18.0, 13.49)], dtype=[('Date', 'S8'), ('Temp.', float), ('p (Gpa)', float)]), 'kwds': { 'comments': '#', 'delimiter': ',', 'dtype': { 'formats': ( 'S8', float, float), 'names': ( 'Date', 'Temp.', 'p (Gpa)')}, 'skiprows': 1 } } mayavi-4.1.0/mayavi/tests/csv_files/hp11c.csv0000644000175100001440000000032411674464502022031 0ustar ischnellusers00000000000000 x y #Beispiel aus Handbuch des HP 11C: # xmean = 6.1352 # ymean = 1.831 # sx = 0.4287309179 # sy = 0.07891451071 # r = 0.9207849308 5.552 1.761 5.963 1.775 6.135 1.792 6.313 1.884 6.713 1.943 mayavi-4.1.0/mayavi/tests/csv_files/csv_2_py0000755000175100001440000000076211674464502022060 0ustar ischnellusers00000000000000#!/usr/bin/env python # Small tool to read .csv files and print the .py files for the tests import sys import pprint from mayavi.tools.wizards.csv_sniff import Sniff filename = sys.argv[1] s = Sniff(filename) d = {'kwds': s.kwds(), 'array': s.loadtxt()} pp = pprint.PrettyPrinter(indent=2, width=40) out = pp.pformat(d) out = out.replace("", 'float') out = out.replace("' # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import unittest # Local imports. from common import get_example_data # Enthought library imports from mayavi.core.null_engine import NullEngine from mayavi.filters.optional import Optional from mayavi.filters.user_defined import UserDefined from mayavi.filters.api import (CellToPointData, ExtractVectorNorm, ExtractVectorComponents) from mayavi.modules.api import ScalarCutPlane from mayavi.sources.vtk_xml_file_reader import VTKXMLFileReader from tvtk.api import tvtk class TestUserDefined(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() e.new_scene() self.e=e # Read a VTK (old style) data file. r = VTKXMLFileReader() r.initialize(get_example_data('pyramid_ug.vtu')) e.add_source(r) # Create the filters. # CellDerivatives cd = tvtk.CellDerivatives() ud = UserDefined(filter=cd) e.add_filter(ud) ctp = CellToPointData() ctp.filter.pass_cell_data = False e.add_filter(ctp) evn = ExtractVectorNorm() e.add_filter(evn) evc = ExtractVectorComponents(component='y-component') o = Optional(filter=evc) e.add_filter(o) e.add_module(ScalarCutPlane()) self.scene = e.current_scene s = self.scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self): """Do the actual testing.""" scene = self.scene src = scene.children[0] ud = src.children[0] o = ud.children[0].children[0].children[0] mm = o.children[0] assert src.outputs[0].point_data.scalars.name == 'temperature' assert src.outputs[0].point_data.vectors.name == 'velocity' expect = ['ScalarGradient', 'Vorticity'] expect1 = [x +'-y' for x in expect] expect2 = [x + ' magnitude' for x in expect] o.enabled = True assert o.outputs[0].point_data.scalars.name in expect1 assert o.outputs[0].point_data.vectors.name in expect assert mm.scalar_lut_manager.data_name in expect1 # Turn of extraction. o.enabled = False assert o.outputs[0].point_data.scalars.name in expect2 assert o.outputs[0].point_data.vectors.name in expect assert mm.scalar_lut_manager.data_name in expect2 # Compute the vorticity ud.filter.vector_mode = 'compute_vorticity' assert o.outputs[0].point_data.scalars.name == 'Vorticity magnitude' assert o.outputs[0].point_data.vectors.name == 'Vorticity' assert mm.scalar_lut_manager.data_name == 'Vorticity magnitude' # Turn on extraction. o.enabled = True assert o.outputs[0].point_data.scalars.name == 'Vorticity-y' assert o.outputs[0].point_data.vectors.name == 'Vorticity' assert mm.scalar_lut_manager.data_name == 'Vorticity-y' # Turn off extraction. o.enabled = False def test_user_defined(self): "Test if the test fixture works" #Now test. s = self.scene self.check() #from mayavi.tools.show import show #show() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene s = self.scene self.check() def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) cp = source.children[0].children[-1] s = self.scene self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 s = self.scene self.check() #from mayavi.tools.show import show #show() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_mlab_scene_model.py0000644000175100001440000000263711674464502023315 0ustar ischnellusers00000000000000""" Testing the MlabSceneModel """ import unittest import numpy as np from traits.api import HasTraits, Instance from mayavi.tools.mlab_scene_model import MlabSceneModel from mayavi import mlab from test_mlab_integration import TestMlabNullEngine ############################################################################### # class `TestMlabSceneModel` ############################################################################### class TestMlabSceneModel(TestMlabNullEngine): """ Testing the MlabSceneModel, in particular the magic mlab attribute. """ def test_several_scene_models(self): """ Check that plotting to scene attributes using their mlab attribute does create objects as children, and does not unset the current scene """ class TestObject(HasTraits): scene1 = Instance(MlabSceneModel, ()) scene2 = Instance(MlabSceneModel, ()) test_object = TestObject() x, y, z = np.random.random((3, 10)) plt = mlab.plot3d(x, y, z, figure=test_object.scene1.mayavi_scene) pts = mlab.points3d(x, y, z, figure=test_object.scene2.mayavi_scene) # Check that each figure got the module it should have self.assertEqual(plt.scene, test_object.scene1) self.assertEqual(pts.scene, test_object.scene2) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_unstructured_data_reader.py0000644000175100001440000000716711674464502025132 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Standard library imports. import unittest # Local imports. from common import get_example_data # Enthought library imports from mayavi.sources.unstructured_grid_reader import UnstructuredGridReader from mayavi.tests.data_reader_test_base import DataReaderTestBase class TestAVSUCDReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a AVSUCD data file. r = UnstructuredGridReader() r.initialize(get_example_data('cellsnd.ascii.inp')) self.e.add_source(r) self.bounds =(-2.0, 2.0, -2.0, 2.0, 0.0, 0.0) def test_avsucd_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestExodusReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a Exodus data file. r = UnstructuredGridReader() r.initialize(get_example_data('edgeFaceElem.exii')) self.e.add_source(r) self.bounds =(-3.0, 3.0, -3.0, 3.0, -3.0, 3.0) def test_exodus_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) class TestGambitReader(DataReaderTestBase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ # Read a Gambit data file. r = UnstructuredGridReader() r.initialize(get_example_data('prism.neu')) self.e.add_source(r) self.bounds = (-1.0, 1.0, -1.0, 1.0, 0.0, 1.0) def test_gambit_data_reader(self): "Test if the test fixture works" #Now test. self.check(self.scene, self.bounds) def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" self.check_saving(self.e, self.scene, self.bounds) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. self.check_deepcopying(self.scene, self.bounds) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_image_plane_widget.py0000644000175100001440000001063511674464502023646 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy import unittest # Enthought library imports from mayavi.core.engine import Engine from mayavi.core.null_engine import NullEngine from mayavi.sources.array_source import ArraySource from mayavi.modules.outline import Outline from mayavi.modules.image_plane_widget import ImagePlaneWidget class TestImagePlaneWidget(unittest.TestCase): def make_data(self): """Creates suitable data for the test.""" dims = numpy.array((64, 64, 64), 'i') # Create some scalars to render. dx, dy, dz = 10.0/(dims - 1) x = numpy.reshape(numpy.arange(-5.0, 5.0+dx*0.5, dx, 'f'), (dims[0], 1, 1)) y = numpy.reshape(numpy.arange(-5.0, 5.0+dy*0.5, dy, 'f'), (1, dims[1], 1)) z = numpy.reshape(numpy.arange(-5.0, 5.0+dz*0.5, dz, 'f'), (1, 1, dims[0])) scalars = numpy.sin(x*y*z)/(x*y*z) return scalars def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() s=e.new_scene() self.e=e self.s=s ############################################################ # Create a new scene and set up the visualization. d = ArraySource() sc = self.make_data() d.scalar_data = sc e.add_source(d) # Create an outline for the data. o = Outline() e.add_module(o) # ImagePlaneWidgets for the scalars ipw = ImagePlaneWidget() e.add_module(ipw) ipw_y = ImagePlaneWidget() e.add_module(ipw_y) ipw_y.ipw.plane_orientation = 'y_axes' ipw_z = ImagePlaneWidget() e.add_module(ipw_z) ipw_z.ipw.plane_orientation = 'z_axes' self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self): """Do the actual testing.""" s=self.scene src = s.children[0] i1, i2, i3 = src.children[0].children[1:] self.assertEqual(i1.ipw.plane_orientation,'x_axes') self.assertEqual(numpy.allclose(i1.ipw.center, (0, 31.5, 31.5)),True) self.assertEqual( i2.ipw.plane_orientation,'y_axes') self.assertEqual(numpy.allclose(i2.ipw.center, (31.5, 0, 31.5)),True) self.assertEqual(i3.ipw.plane_orientation,'z_axes') self.assertEqual( numpy.allclose(i3.ipw.center, (31.5, 31.5, 0)),True) def test_image_plane_widget(self): "Test if the test fixture works" self.check() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene sources = s.children s.children = [] # Add it back to see if that works without error. s.children.extend(sources) self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. sources1 = copy.deepcopy(sources) s.children[:] = sources1 self.check() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_streamline.py0000644000175100001440000001375111674464502022207 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy import unittest # Enthought library imports from mayavi.core.null_engine import NullEngine from mayavi.sources.array_source import ArraySource from mayavi.modules.outline import Outline from mayavi.modules.streamline import Streamline class TestStreamline(unittest.TestCase): def make_data(self): """Trivial data -- creates an elementatry scalar field and a constant vector field along the 'x' axis.""" s = numpy.arange(0.0, 10.0, 0.01) s = numpy.reshape(s, (10,10,10)) s = numpy.transpose(s) v = numpy.zeros(3000, 'd') v[1::3] = 1.0 v = numpy.reshape(v, (10,10,10,3)) return s, v def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() s=e.new_scene() self.e=e self.s=s ############################################################ # Create a new scene and set up the visualization. d = ArraySource() sc, vec = self.make_data() d.origin = (-5, -5, -5) d.scalar_data = sc d.vector_data = vec e.add_source(d) # Create an outline for the data. o = Outline() e.add_module(o) # View the data. st = Streamline() e.add_module(st) widget = st.seed.widget widget.set(radius=1.0, center=(-4.0, -4.0, -4.0), theta_resolution=4, phi_resolution=4) st = Streamline(streamline_type='ribbon') seed = st.seed seed.widget = seed.widget_list[1] e.add_module(st) seed.widget.set(point1=(-5.0, -4.5, -4.0), point2=(-5.0, -4.5, 4.0)) st.ribbon_filter.width = 0.25 st = Streamline(streamline_type='tube') seed = st.seed seed.widget = seed.widget_list[2] e.add_module(st) seed.widget.set(center=(-5.0, 1.5, -2.5)) st.tube_filter.radius = 0.15 st = Streamline(streamline_type='tube') seed = st.seed seed.widget = seed.widget_list[3] e.add_module(st) seed.widget.position=(-5.0, 3.75, 3.75) st.tube_filter.radius = 0.2 self.st = st self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self): """Do the actual testing.""" s=self.scene src = s.children[0] st = src.children[0].children[2] self.assertEqual(st.streamline_type,'ribbon') self.assertEqual(st.ribbon_filter.width,0.25) self.assertEqual(st.seed.widget,st.seed.widget_list[1]) self.assertEqual(numpy.allclose(st.seed.widget.point1, (-5.0, -4.5, -4.0)),True) self.assertEqual(numpy.allclose(st.seed.widget.point2, (-5.0, -4.5, 4.0)),True) st = src.children[0].children[3] self.assertEqual(st.streamline_type,'tube') self.assertEqual(st.tube_filter.radius,0.15) self.assertEqual(st.seed.widget,st.seed.widget_list[2]) self.assertEqual(numpy.allclose(st.seed.widget.center, (-5.0, 1.5, -2.5)),True) st = src.children[0].children[4] self.assertEqual(st.streamline_type,'tube') self.assertEqual(st.tube_filter.radius,0.2) self.assertEqual(st.seed.widget,st.seed.widget_list[3]) self.assertEqual(numpy.allclose(st.seed.widget.position, (-5.0, 3.75, 3.75)),True) def test_streamline(self): "Test if the test fixture works" self.check() def test_components_changed(self): """Test if the modules respond correctly when the components are changed.""" st = self.st tf = st.tube_filter st.tube_filter = tf.__class__() st.tube_filter = tf st.ribbon_filter = st.ribbon_filter.__class__() seed = st.seed st.seed = seed.__class__() st.seed = seed st.actor = st.actor.__class__() tracer = st.stream_tracer st.stream_tracer = tracer.__class__() st.stream_tracer = tracer self.check() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene sources = s.children s.children = [] # Add it back to see if that works without error. s.children.extend(sources) self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. sources1 = copy.deepcopy(sources) s.children[:] = sources1 self.check() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/README.txt0000644000175100001440000000241411674464502020123 0ustar ischnellusers00000000000000======================= Notes on testing Mayavi ======================= This directory contains unit tests for mayavi. Running the tests ================= The best way to run the tests in this directory is to do:: $ ./runtests.py . Try ``runtests.py --help`` to see all help options. You may also run each test individually. For example:: $ python test_contour.py You can also use nosetests but nosetests runs everything in the same process often tripping up on valid tests. Debugging using on-screen rendering =================================== Many of these unit tests run off screen and make use of TestEngine. TestEngine can be replaced by Engine to allow for scene creation which may be useful in debugging. This can be easily done by uncommenting the following line from the setUp() functions:: e = Engine() # This is commented by default It must be followed by the commenting of : e = TestEngine() # This is uncommented by default Debugging using an IPython Shell =================================== The IPython Shell can be embedded anywhere in the program. You need to import the `IPython` module and then add the following lines wherver you want to embed the shell:: embedshell = IPython.Shell.IPShellEmbed() embedshell() mayavi-4.1.0/mayavi/tests/test_grid_plane.py0000644000175100001440000001017111674464502022141 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import unittest import datasets # Local imports. from mayavi.core.engine import Engine from mayavi.core.null_engine import NullEngine # Enthought library imports from mayavi.sources.vtk_data_source import VTKDataSource from mayavi.modules.outline import Outline from mayavi.modules.grid_plane import GridPlane class TestGridPlane(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() e.new_scene() self.e=e sgrid = datasets.generateStructuredGrid() src = VTKDataSource(data = sgrid) e.add_source(src) # Create an outline for the data. o = Outline() e.add_module(o) # Create three simple grid plane modules. # First normal to 'x' axis. gp1 = GridPlane() e.add_module(gp1) # Second normal to 'y' axis. gp2 = GridPlane() # We'll test how robust things are by setting attributes gp2.grid_plane.axis = 'y' gp2.grid_plane.position = 16 e.add_module(gp2) # Third normal to 'z' axis. gp3 = GridPlane() e.add_module(gp3) gp3.grid_plane.axis = 'z' gp3.grid_plane.position = 6 for gp in (gp1, gp2, gp3): gp.actor.property.set(ambient=1.0) self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self): """Do the actual testing.""" s = self.scene mm = s.children[0].children[0] gp1, gp2, gp3 = mm.children[1:] self.assertEqual(gp1.grid_plane.axis,'x') self.assertEqual(gp1.grid_plane.position,0) self.assertEqual(gp1.actor.property.ambient,1.0) self.assertEqual(gp2.grid_plane.axis,'y') self.assertEqual(gp2.grid_plane.position,16) self.assertEqual(gp2.actor.property.ambient,1.0) self.assertEqual(gp3.grid_plane.axis,'z') self.assertEqual(gp3.grid_plane.position,6) self.assertEqual(gp3.actor.property.ambient,1.0) def test_grid_plane(self): "Test if the test fixture works" self.check() #from mayavi.tools.show import show #show() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 self.check() #from mayavi.tools.show import show #show() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_mlab_null_engine.py0000644000175100001440000000403411674464502023330 0ustar ischnellusers00000000000000""" Test the mlab null engine. """ import unittest from mayavi import mlab from mayavi.core.engine import Engine from mayavi.tools.engine_manager import engine_manager from mayavi.core.registry import registry from mayavi.tests.common import get_example_data ################################################################################ # class `TestMlabNullEngineBase` ################################################################################ class TestMlabNullEngineBase(unittest.TestCase): """ Base class to test mlab with the null engine """ def setUp(self): e = Engine() e.start() self._non_null_engine = e mlab.set_engine(e) def tearDown(self): # Check that the NullEngine was not set as the default mlab engine. if not mlab.get_engine() is self._non_null_engine: raise AssertionError, \ "The NullEngine has overridden the default one" engine_manager.current_engine = None # Unregistering all unused engines. registry.unregister_engine(self._non_null_engine) for engine in registry.engines.keys(): registry.unregister_engine(engine) ################################################################################ # class `TestRealMlabNullEngine` ################################################################################ class TestRealMlabNullEngine(unittest.TestCase): """Tests if the mlab settings via the options.backend and offscreen options work correctly.""" def setUp(self): self.backend = mlab.options.backend def tearDown(self): mlab.options.backend = self.backend for engine in registry.engines.keys(): registry.unregister_engine(engine) def test_test_backend(self): """Test if setting the backend to 'test' works.""" mlab.options.backend = 'test' mlab.test_contour3d() mlab.clf() mlab.pipeline.open(get_example_data('cube.vti')) mlab.clf() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_ipw_multiple_scalars.py0000644000175100001440000000510511674464502024260 0ustar ischnellusers00000000000000import unittest from numpy import zeros, random from tvtk.api import tvtk from mayavi.sources.vtk_data_source import VTKDataSource from mayavi.core.null_engine import NullEngine from mayavi.modules.image_plane_widget import ImagePlaneWidget class TestIPWMultipleScalars(unittest.TestCase): def setUp(self): # Create dataset with multiple scalars. arr1 = zeros(27, 'f') for n in range(27): arr1[n] = (1+float(n))/10.0 arr2 = (arr1 + 1).astype('d') arr3 = arr1 + 2.0*(0.5 - random.random(27)) arr3 = arr3.astype('f') p = tvtk.ImageData(dimensions=[3,3,3],spacing=[1,1,1], scalar_type='int') p.point_data.scalars = arr1 p.point_data.scalars.name = 'first' j2 = p.point_data.add_array(arr2) p.point_data.get_array(j2).name='second' j3 = p.point_data.add_array(arr3) p.point_data.get_array(j3).name='third' p.update() self.img = p self.first = arr1 self.second = arr2 self.third = arr3 # Setup the mayavi pipeline. e = NullEngine() e.start() e.new_scene() self.e = e src = VTKDataSource(data=p) e.add_source(src) self.src = src ipw = ImagePlaneWidget() e.add_module(ipw) self.ipw = ipw def tearDown(self): self.e.stop() return def test_ipw(self): """Test the image plane widget.""" arr1, arr2, arr3 = self.first, self.second, self.third ipw = self.ipw.ipw scalars = ipw.input.point_data.scalars r = scalars.range expect = min(arr1), max(arr1) self.assertEqual(r, expect) o = self.src.outputs[0] o.update_traits() st = ipw.input.scalar_type self.assertEqual(scalars.data_type, 10) self.assertEqual(st, 'float') self.src.point_scalars_name = 'second' scalars = ipw.input.point_data.scalars r = scalars.range expect = min(arr2), max(arr2) self.assertEqual(r, expect) o.update_traits() st = ipw.input.scalar_type self.assertEqual(scalars.data_type, 11) self.assertEqual(st, 'double') self.src.point_scalars_name = 'third' scalars = ipw.input.point_data.scalars r = scalars.range expect = min(arr3), max(arr3) self.assertEqual(r, expect) o.update_traits() st = ipw.input.scalar_type self.assertEqual(scalars.data_type, 10) self.assertEqual(st, 'float') if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_glyph.py0000644000175100001440000001341211674464502021161 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy import unittest # Local imports. from mayavi.core.null_engine import NullEngine # Enthought library imports from mayavi.sources.array_source import ArraySource from mayavi.modules.outline import Outline from mayavi.modules.glyph import Glyph from mayavi.modules.vector_cut_plane import VectorCutPlane class TestGlyph(unittest.TestCase): def make_data(self): """Trivial data -- creates an elementatry scalar field and a constant vector field along the 'x' axis.""" s = numpy.arange(0.0, 10.0, 0.01) s = numpy.reshape(s, (10,10,10)) s = numpy.transpose(s) v = numpy.zeros(3000, 'd') v[1::3] = 1.0 v = numpy.reshape(v, (10,10,10,3)) return s, v def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() s=e.new_scene() self.e=e self.s=s ############################################################ # Create a new scene and set up the visualization. d = ArraySource() sc, vec = self.make_data() d.origin = (-5, -5, -5) d.scalar_data = sc d.vector_data = vec e.add_source(d) # Create an outline for the data. o = Outline() e.add_module(o) # Glyphs for the scalars g = Glyph() e.add_module(g) g.glyph.glyph_source.glyph_position = 'center' g.glyph.glyph.vector_mode = 'use_normal' g.glyph.glyph.scale_factor = 0.5 g.actor.property.line_width = 1.0 v = VectorCutPlane() glyph = v.glyph gs = glyph.glyph_source gs.glyph_position = 'tail' gs.glyph_source = gs.glyph_list[1] e.add_module(v) v.implicit_plane.set(normal=(0, 1, 0), origin=(0, 3, 0)) v = VectorCutPlane() glyph = v.glyph gs = glyph.glyph_source gs.glyph_source = gs.glyph_list[2] gs.glyph_position = 'head' e.add_module(v) v.implicit_plane.set(normal=(0, 1, 0), origin=(0, -2, 0)) self.g=g self.v=v self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self): """Do the actual testing.""" s=self.scene src = s.children[0] g = src.children[0].children[1] self.assertEqual(g.glyph.glyph_source.glyph_position,'center') self.assertEqual(g.glyph.glyph.vector_mode,'use_normal') self.assertEqual(g.glyph.glyph.scale_factor,0.5) self.assertEqual(g.actor.property.line_width,1.0) v = src.children[0].children[2] glyph = v.glyph gs = glyph.glyph_source self.assertEqual(gs.glyph_position,'tail') self.assertEqual(gs.glyph_source,gs.glyph_list[1]) self.assertEqual(numpy.allclose(v.implicit_plane.normal, (0., 1., 0.)),True) v = src.children[0].children[3] glyph = v.glyph gs = glyph.glyph_source self.assertEqual(gs.glyph_source,gs.glyph_list[2]) self.assertEqual(gs.glyph_position,'head') self.assertEqual(numpy.allclose(v.implicit_plane.normal, (0., 1., 0.)),True) def test_glyph(self): "Test if the test fixture works" self.check() def test_components_changed(self): """"Test if the modules respond correctly when the components are changed.""" g=self.g v=self.v g.actor = g.actor.__class__() glyph = g.glyph g.glyph = glyph.__class__() g.glyph = glyph glyph = v.glyph v.glyph = glyph.__class__() v.glyph = glyph v.actor = v.actor.__class__() v.cutter = v.cutter.__class__() ip = v.implicit_plane v.implicit_plane = ip.__class__() v.implicit_plane = ip self.check() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene sources = s.children s.children = [] # Add it back to see if that works without error. s.children.extend(sources) self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. sources1 = copy.deepcopy(sources) s.children[:] = sources1 self.check() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/data_reader_test_base.py0000644000175100001440000000565111674464502023271 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import unittest import numpy from mayavi.core.null_engine import NullEngine from mayavi.modules.outline import Outline class DataReaderTestBase(unittest.TestCase): def setup_reader(self): """"Setup the reader in here. This is called after the engine has been created and started. The engine is available as self.e. This method is called by setUp(). """ raise NotImplementedError() def setup_viz(self): """Setup the visualization.""" # Create an outline for the data. o = Outline() self.e.add_module(o) def setUp(self): e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() self.e = e s = e.new_scene() self.scene = e.current_scene self.setup_reader() self.setup_viz() return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self, scene, bounds, error = 1.01e-02): """Do the actual testing.""" src = scene.children[0] ot = src.children[0].children[0] ot.render() # Flush the pipeline. # Check the outline bounds self.assertEqual(numpy.allclose(ot.outline_filter.output.bounds,bounds, atol=error), True) def check_saving(self, engine, scene, bounds, error = 1.01e-02): # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check(scene,bounds,error) def check_deepcopying(self, scene, bounds, error = 1.01e-02): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. source = scene.children.pop() # Add it back to see if that works without error. scene.children.append(source) self.check(scene, bounds, error) # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) scene.children[0] = source1 self.check(scene, bounds, error) mayavi-4.1.0/mayavi/tests/test_mayavi_traits.py0000644000175100001440000000643611674464502022722 0ustar ischnellusers00000000000000""" Tests for traits defined in mayavi.core.traits """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran Enthought, Inc. # License: BSD Style. import unittest import numpy from traits.api import (HasTraits, Either, Array, Any, TraitError, Float, Int) from mayavi.core.traits import ShadowProperty ArrayOrNone = Either(None, Array) class DataNotSmart(HasTraits): x = ShadowProperty(ArrayOrNone, smart_notify=False) # Test attribute. _test = Any def _x_changed(self, value): self._test = value.copy() class DataSmart(HasTraits): x = ShadowProperty(ArrayOrNone, smart_notify=True) # Test attribute. _test = Any def _x_changed(self, value): self._test = value.copy() class Simple(HasTraits): x = ShadowProperty(Float) # Test attribute. _test = Int(0) def _x_changed(self, value): self._test += 1 class TestShadowProperty(unittest.TestCase): def test_simple_case(self): "Test a simple trait wrapped as a shadow property." s = Simple(x=10.0) self.assertEqual(s._test, 1) self.assertEqual(s.x, 10.0) s.x = 10.0 self.assertEqual(s._test, 1) self.assertEqual(s.x, 10.0) s.x = 20.0 self.assertEqual(s._test, 2) self.assertEqual(s.x, 20.0) self.assertRaises(TraitError, s.__setattr__, 'x', 'hey') def test_shadow_property_smart(self): "Test if the shadow property trait type works correctly." x = numpy.linspace(0, 1) d = DataSmart(x=x) self.assertEqual(numpy.all(d.x == x), True) self.assertEqual(numpy.all(d._x == x), True) self.assertEqual(numpy.all(d._test == x), True) old = x.copy() x *= 2 d.x = x self.assertEqual(numpy.all(d.x == x), True) self.assertEqual(numpy.all(d._x == x), True) # Notifier shouldn't be called. self.assertEqual(numpy.all(d._test == old), True) def test_shadow_property_not_smart(self): "Test if the shadow property trait type works correctly." x = numpy.linspace(0, 1) d = DataNotSmart(x=x) self.assertEqual(numpy.all(d.x == x), True) self.assertEqual(numpy.all(d._x == x), True) self.assertEqual(numpy.all(d._test == x), True) x *= 2 d.x = x self.assertEqual(numpy.all(d.x == x), True) self.assertEqual(numpy.all(d._x == x), True) self.assertEqual(numpy.all(d._test == x), True) def test_type_checking(self): "Test if the validation works correctly." x = numpy.linspace(0, 1) d = DataNotSmart(x=x) self.assertRaises(TraitError, d.__setattr__, 'x', 'hey') def test_set_trait_change_notify(self): "If trait_change_notify is honored." s = Simple() trait_names = s.trait_names() s.x = 10.0 self.assertEqual(s._test, 1) self.assertEqual(s.x, 10.0) s.set(x=20.0, trait_change_notify=False) self.assertEqual(s._test, 1) self.assertEqual(s.x, 20.0) # Assert that no new traits were added and no new notifiers were # added. self.assertEqual(s.trait_names(), trait_names) self.assertEqual(s._notifiers(False), None) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_dataset_manager.py0000644000175100001440000001631311674464502023160 0ustar ischnellusers00000000000000""" Test for the dataset_manager.py module. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. import unittest import numpy as N from tvtk.api import tvtk from mayavi.core.dataset_manager import DatasetManager def make_data(): points = N.array([[0,0,0], [1,0,0], [0,1,0], [0,0,1], # tets [1,0,0], [2,0,0], [1,1,0], [1,0,1], [2,0,0], [3,0,0], [2,1,0], [2,0,1], ], 'f') tets = N.array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]) tet_type = tvtk.Tetra().cell_type ug = tvtk.UnstructuredGrid(points=points) ug.set_cells(tet_type, tets) # Setup the point attributes. temp = N.random.random(12) v = N.random.randn(12, 3) ten = N.random.randn(12, 9) a = tvtk.FloatArray(name='p') a.from_array(N.random.randn(12)) ug.point_data.add_array(a) ug.point_data.scalars = temp ug.point_data.scalars.name = 't' ug.point_data.vectors = v ug.point_data.vectors.name = 'v' ug.point_data.tensors = ten ug.point_data.tensors.name = 'ten' # Setup the cell attributes. temp = N.random.random(3) v = N.random.randn(3, 3) ten = N.random.randn(3, 9) ug.cell_data.scalars = temp ug.cell_data.scalars.name = 't' ug.cell_data.vectors = v ug.cell_data.vectors.name = 'v' ug.cell_data.tensors = ten ug.cell_data.tensors.name = 'ten' return ug class TestDataSetManager(unittest.TestCase): def setUp(self): self.data = make_data() self.dm = DatasetManager(dataset=self.data) return def tearDown(self): return def test_point_arrays(self): "Are the point data arrays of the manager setup right?" dm = self.dm sc = dm.point_scalars.keys() sc.sort() self.assertEqual(sc, ['p', 't']) vec = dm.point_vectors.keys() self.assertEqual(vec, ['v']) ten = dm.point_tensors.keys() self.assertEqual(ten, ['ten']) def test_point_array_view(self): "Are the manager's point arrays views of the VTK data?" # Check that the array we have is really a view of the VTK data. dm = self.dm data = self.data t = dm.point_scalars['t'] t[0] += 1.0 self.assertEqual(t[0], data.point_data.scalars[0]) v = dm.point_vectors['v'] v[0][0] += 1.0 self.assertEqual(v[0][0], data.point_data.vectors[0][0]) ten = dm.point_tensors['ten'] ten[0][0] += 1.0 self.assertEqual(ten[0][0], data.point_data.tensors[0][0]) def test_cell_arrays(self): "Are the cell arrays of the manager setup right?" dm = self.dm sc = dm.cell_scalars.keys() self.assertEqual(sc, ['t']) vec = dm.cell_vectors.keys() self.assertEqual(vec, ['v']) ten = dm.cell_tensors.keys() self.assertEqual(ten, ['ten']) def test_cell_array_view(self): "Are the manager's cell arrays views of the VTK data?" # Check that the array we have is really a view of the VTK data. dm = self.dm data = self.data t = dm.cell_scalars['t'] t[0] += 1.0 self.assertEqual(t[0], data.cell_data.scalars[0]) v = dm.cell_vectors['v'] v[0][0] += 1.0 self.assertEqual(v[0][0], data.cell_data.vectors[0][0]) ten = dm.cell_tensors['ten'] ten[0][0] += 1.0 self.assertEqual(ten[0][0], data.cell_data.tensors[0][0]) def test_remove_array(self): "Does the remove_array method work correctly." dm = self.dm data = self.data dm.remove_array('t', 'point') self.assertEqual(len(dm.point_scalars), 1) self.assertEqual(dm.point_scalars.keys(), ['p']) dm.remove_array('ten', 'point') self.assertEqual(len(dm.point_tensors), 0) # Make sure the default category is point. dm.remove_array('v') self.assertEqual(len(dm.point_vectors), 0) # Cell arrays. dm.remove_array('t', 'cell') self.assertEqual(len(dm.cell_scalars), 0) dm.remove_array('ten', 'cell') self.assertEqual(len(dm.cell_tensors), 0) dm.remove_array('v', 'cell') self.assertEqual(len(dm.cell_vectors), 0) def test_rename_array(self): "Does array renaming work." dm = self.dm data = self.data dm.rename_array('ten', 'ten1', 'point') self.assertEqual(dm.point_tensors.keys(), ['ten1']) pd = data.point_data arrs = [pd.get_array_name(x) for x in range(pd.number_of_arrays)] arrs.sort() self.assertEqual(arrs, ['p', 't', 'ten1', 'v']) dm.rename_array('t', 'temp', 'cell') self.assertEqual(dm.cell_scalars.keys(), ['temp']) cd = data.cell_data arrs = [cd.get_array_name(x) for x in range(cd.number_of_arrays)] arrs.sort() self.assertEqual(arrs, ['temp', 'ten', 'v']) def test_add_array(self): "Does the add_array method work." dm = self.dm data = self.data pd = data.point_data cd = data.cell_data # Point data. s = N.random.randn(12) v = N.random.randn(12,3) t = N.random.randn(12, 9) dm.add_array(s, 'scalar') sc = self.dm.point_scalars.keys() sc.sort() self.assertEqual(sc, ['p', 'scalar', 't']) x = pd.get_array('scalar') self.assertNotEqual(x, None) dm.add_array(v, 'vector') vc = self.dm.point_vectors.keys() vc.sort() self.assertEqual(vc, ['v', 'vector']) x = pd.get_array('vector') self.assertNotEqual(x, None) dm.add_array(t, 'tensor') vc = self.dm.point_tensors.keys() vc.sort() self.assertEqual(vc, ['ten', 'tensor']) x = pd.get_array('tensor') self.assertNotEqual(x, None) # cell data. s = N.random.randn(3) v = N.random.randn(3, 3) t = N.random.randn(3, 9) dm.add_array(s, 'scalar', 'cell') sc = self.dm.cell_scalars.keys() sc.sort() self.assertEqual(sc, ['scalar', 't']) x = cd.get_array('scalar') self.assertNotEqual(x, None) dm.add_array(v, 'vector', 'cell') vc = self.dm.cell_vectors.keys() vc.sort() self.assertEqual(vc, ['v', 'vector']) x = cd.get_array('vector') self.assertNotEqual(x, None) dm.add_array(t, 'tensor', 'cell') vc = self.dm.cell_tensors.keys() vc.sort() self.assertEqual(vc, ['ten', 'tensor']) x = cd.get_array('tensor') self.assertNotEqual(x, None) def test_activate(self): "Does activating a particular array work." dm = self.dm data = self.dm.output dm.activate('p') self.assertEqual(data.point_data.scalars.name, 'p') dm.activate('t') self.assertEqual(data.point_data.scalars.name, 't') s = N.random.randn(3) dm.add_array(s, 'foo', 'cell') dm.activate('foo', 'cell') self.assertEqual(data.cell_data.scalars.name, 'foo') dm.activate('t', 'cell') self.assertEqual(data.cell_data.scalars.name, 't') if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_script_recording.py0000644000175100001440000000721711674464502023404 0ustar ischnellusers00000000000000""" A simple test for script recording in Mayavi. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. import unittest from apptools.scripting.api import Recorder, set_recorder from mayavi.sources.parametric_surface import \ ParametricSurface from mayavi.modules.outline import Outline from mayavi.modules.surface import Surface from mayavi.core.null_engine import NullEngine class TestScriptRecording(unittest.TestCase): def setUp(self): tape = Recorder() # Set the global recorder. set_recorder(tape) self.tape = tape def tearDown(self): self.tape.clear() set_recorder(None) def test_script_recording(self): "Does script recording work correctly." # Create a mayavi pipeline and record it. tape = self.tape e = NullEngine() e.start() # Start recording. tape.recording = True tape.register(e, known=True, script_id='engine') e.new_scene() #print tape.script self.assertEqual(tape.lines[-1], "dummy_viewer = engine.new_scene()") src = ParametricSurface() e.add_source(src) expect = 'from mayavi.sources.parametric_surface '\ 'import ParametricSurface' self.assertEqual(tape.lines[-3], expect) self.assertEqual(tape.lines[-2], "parametric_surface = ParametricSurface()") self.assertEqual(tape.lines[-1], "engine.add_source(parametric_surface)") src.function = 'dini' self.assertEqual(tape.lines[-1], "parametric_surface.function = 'dini'") o = Outline() e.add_module(o) expect = 'from mayavi.modules.outline import Outline' self.assertEqual(tape.lines[-3], expect) self.assertEqual(tape.lines[-2], "outline = Outline()") self.assertEqual(tape.lines[-1], "engine.add_module(outline)") o.actor.property.color = (1,0,0) self.assertEqual(tape.lines[-1], "outline.actor.property.color = (1.0, 0.0, 0.0)") s = Surface() e.add_module(s) expect = 'from mayavi.modules.surface import Surface' self.assertEqual(tape.lines[-3], expect) self.assertEqual(tape.lines[-2], "surface = Surface()") self.assertEqual(tape.lines[-1], "engine.add_module(surface)") s.actor.property.representation = 'wireframe' self.assertEqual(tape.lines[-1], "surface.actor.property.representation = 'wireframe'") o.actor.property.representation = 'wireframe' self.assertEqual(tape.lines[-1], "outline.actor.property.representation = 'wireframe'") s.actor.property.opacity = 0.5 self.assertEqual(tape.lines[-1], "surface.actor.property.opacity = 0.5") s.actor.mapper.scalar_visibility = False self.assertEqual(tape.lines[-1], "surface.actor.mapper.scalar_visibility = False") #print tape.script # Stop recording and test. tape.unregister(e) tape.record('#end') # Placeholder o.actor.property.opacity = 0.5 self.assertEqual(tape.lines[-1], '#end') s.actor.property.color = (1,0,0) self.assertEqual(tape.lines[-1], '#end') s.enable_contours = True self.assertEqual(tape.lines[-1], '#end') src.function = 'klein' self.assertEqual(tape.lines[-1], '#end') if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_csv_sniff.py0000644000175100001440000001363111674464502022021 0ustar ischnellusers00000000000000""" Tests for the CSV file sniffer """ # Author: Ilan Schnell # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. import glob import os import os.path import sys import unittest from test.test_support import TESTFN, TestFailed import nose from numpy import array, ndarray from mayavi.tools.data_wizards.csv_sniff import \ Sniff, loadtxt, loadtxt_unknown, array2dict class Util(object): def assertNamedClose(self, x, y): self.assertEqual(x.shape, y.shape) self.assertEqual(x.dtype.names, y.dtype.names) for name in x.dtype.names: self.assertAllClose(x[name], y[name]) def assertAllClose(self, x, y): self.assertEqual(len(x), len(y)) for a, b in zip(x, y): self.assertClose(a, b) def assertClose(self, a, b): if isinstance(a, (int, float)): if repr(a) == 'nan': self.assert_(repr(b) == 'nan') else: self.assert_(abs(a - b) < 1e-6 * max(1, abs(a)), '%r != %r %r' % (a, b, abs(a - b))) elif isinstance(a, str): self.assertEqual(a, b) else: raise TestFailed("Hmm, did not expect: %r" % a) class Test(unittest.TestCase, Util): def test_API(self): fo = open(TESTFN, 'wb') fo.write(''' "A", "B", "C" 1, 2, 3.2 7, 4, 1.87''') fo.close() s = Sniff(TESTFN) self.assertEqual(s.comments(), '#') self.assertEqual(s.delimiter(), ',') self.assertEqual(s.skiprows(), 1) self.assertEqual(s.dtype(), {'names': ('A', 'B', 'C'), 'formats': (float, float, float)}) x = s.loadtxt() y = array([(1.0, 2.0, 3.20), (7.0, 4.0, 1.87)], dtype=[('A', float), ('B', float), ('C', float)]) self.assertNamedClose(x, y) y = loadtxt(TESTFN, **s.kwds()) self.assertNamedClose(x, y) y = loadtxt_unknown(TESTFN) self.assertNamedClose(x, y) d = array2dict(y) self.assertEqual(type(d), type({})) self.assertAllClose(x['A'], [1, 7]) self.assertAllClose(x['B'], [2, 4]) self.assertAllClose(x['C'], [3.2, 1.87]) def test_comment(self): fo = open(TESTFN, 'wb') fo.write(''' % "A" "B" "C" 1 2 4.2 % comment''') fo.close() s = Sniff(TESTFN) self.assertEqual(s.kwds(), {'dtype': {'names': ('A', 'B', 'C'), 'formats': (float, float, float)}, 'delimiter': None, 'skiprows': 0, # FIXME 'comments': '%'}) def test_tabs(self): fo = open(TESTFN, 'wb') fo.write('''54\t87\n21\t32''') fo.close() s = Sniff(TESTFN) self.assertEqual(s.delimiter(), None) self.assertEqual(s.skiprows(), 0) def test_nohead(self): fo = open(TESTFN, 'wb') fo.write('''Hello;54;87\nWorld;42;86.5''') fo.close() s = Sniff(TESTFN) self.assertEqual(s.kwds(), {'comments': '#', 'delimiter': ';', 'skiprows': 0, 'dtype': {'names': ('Column 1', 'Column 2', 'Column 3'), 'formats': ('S5', float, float)}}) def test_empty_file(self): fo = open(TESTFN, 'wb') fo.write('') fo.close() self.assertRaises(IndexError, Sniff, TESTFN) def tearDown(self): os.unlink(TESTFN) class Test_csv_py_files(unittest.TestCase, Util): """ These tests require files in csv_files/ """ def check(self, name, skip_if_win=False): """ Check if the output array from csv_files/.csv (which is of unkown format) is the same as the array in csv_files/.py """ if skip_if_win and sys.platform.startswith('win'): raise nose.SkipTest # Note: The files needed for the test are currently accessed directly. # This assumes that the files are present, and not in a zipped # egg. traits.util.resource as supposed to always work, but # when it ran the striped the tests in the EPD installer tests, # it was broken. Since we define zip_safe = False in setup.py # it is safe to assume the files are always present. s = Sniff(os.path.join(os.path.dirname(__file__), 'csv_files', name + '.csv')) f_py = os.path.join(os.path.dirname(__file__), 'csv_files', name + '.py') if not sys.platform.startswith('win'): nan = float('nan') # must be in namespace for some .py files d = eval(open(f_py).read()) self.assertEqual(d['kwds'], s.kwds()) self.assertNamedClose(d['array'], s.loadtxt()) def test_11(self): self.check('11') def test_1col(self): self.check('1col') def test_54(self): self.check('54') def test_79(self): self.check('79') def test_82(self): self.check('82') def test_89(self): self.check('89') def test_99(self): self.check('99') def test_colors(self): self.check('colors') def test_example1(self): # this test need to be skipped on Windows because the data file # has some floats nan, and float('nan') fails on on Windows. self.check('example1', skip_if_win=True) def test_hp11c(self): self.check('hp11c') def test_loc(self): self.check('loc') def test_multi_col(self): self.check('multi-col') def test_mydata(self): self.check('mydata') def test_OObeta3(self): self.check('OObeta3') def test_post(self): self.check('post') def test_webaccess(self): self.check('webaccess') if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/__init__.py0000644000175100001440000000000011674464502020523 0ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/tests/test_preferences_mirror.py0000644000175100001440000000552311674464502023735 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought Inc. # License: BSD Style. import unittest from pkg_resources import resource_filename from traits.api import Str, Int, Bool from apptools.preferences.api import set_default_preferences from apptools.preferences.api import Preferences, PreferencesHelper from mayavi.tools.preferences_mirror import PreferencesMirror class TestPreference(PreferencesHelper): """A simple test preference helper.""" preferences_path = "test" bg = Str width = Int show = Bool class ClassNameTest(unittest.TestCase): def setUp(self): """Called before each test is run""" self.preferences = set_default_preferences(Preferences()) # The filename of the example preferences file. pref_file = resource_filename('mayavi.tests', 'test_preference.ini') self.preferences.load(pref_file) self.pref = TestPreference() self.mirror = PreferencesMirror() self.mirror.preferences = self.pref def test_mirroring(self): """Are the traits properly mirrored?""" pref = self.pref mirror = self.mirror self.assertEqual(pref.bg, mirror.bg) self.assertEqual(pref.width, mirror.width) self.assertEqual(pref.show, mirror.show) def test_sync(self): """Does the mirror listen for changes on original preference.""" pref = self.pref mirror = self.mirror # Save original state. saved = pref.get() pref.set(bg = 'white', width=20, show=True) self.assertEqual(pref.bg, mirror.bg) self.assertEqual(pref.width, mirror.width) self.assertEqual(pref.show, mirror.show) # Reset preferences back to defaults. pref.set(saved) def test_no_reverse_sync(self): """mirror must not sync changes back to the original preferences.""" pref = self.pref mirror = self.mirror saved = pref.get() mirror.set(bg = 'white', width=20, show=True) self.assertNotEqual(pref.bg, mirror.bg) self.assertNotEqual(pref.width, mirror.width) self.assertNotEqual(pref.show, mirror.show) self.assertEqual(pref.bg, saved['bg']) self.assertEqual(pref.width, saved['width']) self.assertEqual(pref.show, saved['show']) def test_save(self): """Are Mirror's preferences saved correctly""" pref = self.pref mirror = self.mirror saved = pref.get() mirror.set(bg = 'white', width=20, show=True) mirror.save() self.assertEqual(pref.bg, mirror.bg) self.assertEqual(pref.width, mirror.width) self.assertEqual(pref.show, mirror.show) # Reset preferences back to defaults. pref.set(saved) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_set_active_attribute.py0000644000175100001440000000774711674464502024265 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import unittest # Local imports. from common import get_example_data # Enthought library imports from mayavi.core.null_engine import NullEngine from mayavi.sources.api import VTKXMLFileReader from mayavi.filters.contour import Contour from mayavi.filters.api import PolyDataNormals from mayavi.filters.set_active_attribute import SetActiveAttribute from mayavi.modules.api import Surface, Outline class TestSetActiveAttribute(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() e.new_scene() self.e=e r = VTKXMLFileReader() r.initialize(get_example_data('pyramid_ug.vtu')) e.add_source(r) r.point_scalars_name = 'temperature' o = Outline() e.add_module(o) c = Contour() e.add_filter(c) n = PolyDataNormals() e.add_filter(n) aa = SetActiveAttribute() e.add_filter(aa) aa.point_scalars_name = 'pressure' s = Surface() e.add_module(s) self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self): """Do the actual testing""" scene = self.scene src = scene.children[0] self.assertEqual(src.point_scalars_name,'temperature') c = src.children[1] sc = c.outputs[0].point_data.scalars self.assertEqual(sc.name,'temperature') # It is an iso-contour! self.assertEqual(sc.range[0],sc.range[1]) aa = c.children[0].children[0] self.assertEqual(aa.point_scalars_name,'pressure') sc = aa.outputs[0].point_data.scalars self.assertEqual(sc.name, 'pressure') self.assertEqual((abs(sc.range[0] - 70) < 1.0),True) self.assertEqual((abs(sc.range[1] - 70) < 1.0),True) s = aa.children[0].children[0] def test_set_active_attribute(self): "Test if the test fixture works" #Now test. self.check() #from mayavi.tools.show import show #show() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 self.check() #from mayavi.tools.show import show #show() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_contour.py0000644000175100001440000001366411674464502021540 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import numpy import unittest import datasets # Local imports. from mayavi.core.null_engine import NullEngine # Enthought library imports from mayavi.sources.vtk_data_source import VTKDataSource from mayavi.modules.outline import Outline from mayavi.modules.iso_surface import IsoSurface from mayavi.modules.contour_grid_plane import ContourGridPlane from mayavi.modules.scalar_cut_plane import ScalarCutPlane class TestContour(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() e.new_scene() self.e=e sgrid=datasets.generateStructuredGrid() src = VTKDataSource(data = sgrid) e.add_source(src) # Create an outline for the data. o = Outline() e.add_module(o) # Create one ContourGridPlane normal to the 'x' axis. cgp1 = ContourGridPlane() e.add_module(cgp1) # Set the position to the middle of the data. cgp1.grid_plane.position = 15 # Another with filled contours normal to 'y' axis. cgp2 = ContourGridPlane() cgp2.contour.filled_contours = True # Set the axis and position to the middle of the data. cgp2.grid_plane.axis = 'y' cgp2.grid_plane.position = 15 e.add_module(cgp2) # An isosurface module. iso = IsoSurface(compute_normals=True) e.add_module(iso) iso.contour.contours = [5] # An interactive scalar cut plane. cp = ScalarCutPlane() e.add_module(cp) ip = cp.implicit_plane ip.normal = 0,0,1 ip.origin = 0.5, 0.5, 1.0 # Since this is running offscreen this seems necessary. ip.widget.origin = 0.5, 0.5, 1.0 ip.widget.enabled = False self.scene = e.current_scene self.cgp2=cgp2 self.iso=iso self.cp=cp return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self): """Do the actual testing.""" scene = self.scene src = scene.children[0] mm = src.children[0] cgp1 = mm.children[1] self.assertEqual(cgp1.grid_plane.position,15) cgp2 = mm.children[2] self.assertEqual(cgp2.contour.filled_contours,True) self.assertEqual(cgp2.grid_plane.axis, 'y') self.assertEqual(cgp2.grid_plane.position,15) iso = mm.children[3] ctr = iso.contour.contours self.assertEqual(iso.compute_normals,True) self.assertEqual(ctr, [5.0]) rng = iso.actor.mapper.input.point_data.scalars.range self.assertEqual(rng[0],5.0) self.assertEqual(rng[1],5.0) cp = mm.children[4] ip = cp.implicit_plane self.assertAlmostEqual(numpy.sum(ip.normal - (0,0,1)) , 1e-16) self.assertAlmostEqual(numpy.sum(ip.origin - (0.5, 0.5, 1.0)), 0.0) self.assertEqual(ip.widget.enabled,False) def test_contour(self): "Test if the test fixture works" #Now test. self.check() #from mayavi.tools.show import show #show() def test_components_changed(self): """Test if the modules respond correctly when the components are changed.""" cgp2=self.cgp2 cp =self.cp iso =self.iso ctr = cgp2.contour cgp2.contour = ctr.__class__() cgp2.contour = ctr cgp2.actor = cgp2.actor.__class__() iso.contour = iso.contour.__class__() iso.contour.contours = [5.0] iso.actor = iso.actor.__class__() iso.normals = iso.normals.__class__() ip = cp.implicit_plane cp.implicit_plane = cp.implicit_plane.__class__() cp.implicit_plane = ip ip.widget.enabled = False cp.contour = cp.contour.__class__() cp.cutter = cp.cutter.__class__() cp.actor = cp.actor.__class__() # Now check. self.check() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) cp = source.children[0].children[-1] cp.implicit_plane.widget.enabled = False self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 cp = source1.children[0].children[-1] cp.implicit_plane.widget.enabled = False self.check() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_preference.ini0000644000175100001440000000005411674464502022301 0ustar ischnellusers00000000000000[test] bg = 'black' width = 10 show = False mayavi-4.1.0/mayavi/tests/test_extract_grid_filter.py0000644000175100001440000001521111674464502024061 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Standard library imports. import unittest from numpy import random, allclose, arange # Local imports. from mayavi.core.null_engine import NullEngine # Enthought library imports from mayavi.sources.vtk_data_source import VTKDataSource from mayavi.modules.grid_plane import GridPlane from mayavi.modules.axes import Axes from mayavi.filters.extract_grid import ExtractGrid from tvtk.api import tvtk class TestExtractGridFilter(unittest.TestCase): def make_scatter(self): pd = tvtk.PolyData() pd.points = 100 + 100*random.random((1000, 3)) verts = arange(0, 1000, 1) verts.shape = (1000, 1) pd.verts = verts pd.point_data.scalars = random.random(1000) pd.point_data.scalars.name = 'scalars' return pd def make_grid4scatter(self): src = VTKDataSource() xmin, xmax, dx = 100, 200, 2 nx = int((xmax-xmin)/dx)+1 ymin, ymax, dy = 100, 200, 2 ny = int((ymax-ymin)/dy)+1 zmin, zmax, dz = 100, 200, 2 nz = int((zmax-zmin)/dz)+1 image_data = tvtk.ImageData(origin=(xmin, ymin, zmin), spacing=(dx, dy, dz), extent=(0, nx-1, 0, ny-1, 0, nz-1)) image_data.whole_extent = image_data.extent src.data = image_data return src def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() s=e.new_scene() self.e=e self.s=s ############################################################ # Create a new scene and set up the visualization. #Make the grid grid = self.make_grid4scatter() e.add_source(grid) eg = ExtractGrid() e.add_filter(eg) nb_ticks = 6 eg.x_ratio = eg.y_ratio = eg.z_ratio = 100/(nb_ticks-1)/2 gpx = GridPlane() e.add_module(gpx) gpx.grid_plane.axis = 'x' gpy = GridPlane() e.add_module(gpy) gpy.grid_plane.axis = 'y' gpz = GridPlane() e.add_module(gpz) gpz.grid_plane.axis = 'z' #Add the scatter d = VTKDataSource() d.data = self.make_scatter() e.add_source(d) a = Axes() e.add_module(a) a.axes.number_of_labels = nb_ticks self.eg = eg self.gpx = gpx self.gpy = gpy self.gpz = gpz self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def test_extract_grid_filter_sample(self): import sys if sys.platform != "darwin": from nose import SkipTest raise SkipTest("actor.bounds returns incorrect values") "Test if the sample rate works." eg = self.eg gpx = self.gpx gpy = self.gpy gpz = self.gpz self.assertEqual(allclose(gpx.actor.actor.bounds, (100.0, 100.0, 100.0, 200.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpy.actor.actor.bounds, (100.0, 200.0, 100.0, 100.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpz.actor.actor.bounds, (100.0, 200.0, 100.0, 200.0, 100.0, 100.0)), True) eg.x_ratio = eg.y_ratio = eg.z_ratio = 25 self.assertEqual(allclose(gpx.actor.actor.bounds, (100.0, 100.0, 100.0, 200.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpy.actor.actor.bounds, (100.0, 200.0, 100.0, 100.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpz.actor.actor.bounds, (100.0, 200.0, 100.0, 200.0, 100.0, 100.0)), True) eg.x_ratio = eg.y_ratio = eg.z_ratio = 5 self.assertEqual(allclose(gpx.actor.actor.bounds, (100.0, 100.0, 100.0, 200.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpy.actor.actor.bounds, (100.0, 200.0, 100.0, 100.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpz.actor.actor.bounds, (100.0, 200.0, 100.0, 200.0, 100.0, 100.0)), True) return def test_voi(self): import sys if sys.platform != "darwin": from nose import SkipTest raise SkipTest("actor.bounds returns incorrect values") "Test if setting the VOI works correctly." eg = self.eg gpx = self.gpx gpy = self.gpy gpz = self.gpz self.assertEqual(allclose(gpx.actor.actor.bounds, (100.0, 100.0, 100.0, 200.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpy.actor.actor.bounds, (100.0, 200.0, 100.0, 100.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpz.actor.actor.bounds, (100.0, 200.0, 100.0, 200.0, 100.0, 100.0)), True) eg.x_ratio = eg.y_ratio = eg.z_ratio = 10 # Now changing the VOI and then setting the ratio used to # show a stupid bug in the grid plane so we test that here. eg.set(x_min=10, x_max=40) eg.x_ratio = 5 self.assertEqual(allclose(gpx.actor.actor.bounds, (120.0, 120.0, 100.0, 200.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpy.actor.actor.bounds, (120.0, 180.0, 100.0, 100.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpz.actor.actor.bounds, (120.0, 180.0, 100.0, 200.0, 100.0, 100.0)), True) # Set some smaller VOI. eg.set(y_min=20, y_max=40, z_min=10, z_max=30) eg.set(x_ratio = 1, y_ratio=1, z_ratio=1) # Reset it and it should go right back. eg.set(x_min=0, x_max=50, y_min=0, y_max=50, z_min=0, z_max=50) self.assertEqual(allclose(gpx.actor.actor.bounds, (100.0, 100.0, 100.0, 200.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpy.actor.actor.bounds, (100.0, 200.0, 100.0, 100.0, 100.0, 200.0)), True) self.assertEqual(allclose(gpz.actor.actor.bounds, (100.0, 200.0, 100.0, 200.0, 100.0, 100.0)), True) #from mayavi.tools.show import show #show() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/test_image_data_probe.py0000644000175100001440000001000011674464502023266 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import unittest # Local imports. from common import get_example_data # Enthought library imports from mayavi.core.null_engine import NullEngine from mayavi.sources.vtk_xml_file_reader import VTKXMLFileReader from mayavi.modules.api import ContourGridPlane from mayavi.filters.image_data_probe import ImageDataProbe class TestImageDataProbe(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() e.new_scene() self.e=e # Read a VTK (old style) data file. r = VTKXMLFileReader() r.initialize(get_example_data('pyramid_ug.vtu')) e.add_source(r) # Create the filters. idp = ImageDataProbe() idp.rescale_scalars = True e.add_filter(idp) cgp = ContourGridPlane(enable_contours=False) e.add_module(cgp) cgp.grid_plane.axis = 'z' cgp.grid_plane.position = 1 self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def check(self): """Do the actual testing""" scene = self.scene src = scene.children[0] idp = src.children[0] mm = idp.children[0] self.assertEqual(src.outputs[0].is_a('vtkUnstructuredGrid'),True) self.assertEqual(idp.outputs[0].is_a('vtkImageData'),True) sc = idp.outputs[0].point_data.scalars vc = idp.outputs[0].point_data.vectors self.assertEqual(sc.name,idp.rescaled_scalar_name) self.assertEqual(vc.name,'velocity') self.assertEqual(mm.scalar_lut_manager.data_name, idp.rescaled_scalar_name) self.assertEqual((abs(sc.range[0]) < 1e-2),True) self.assertEqual( abs(sc.range[1] - 65535.0) < 1.e-2,True) self.assertEqual((idp.outputs[0].dimensions == (3, 3, 2)).all(),True) def test_image_data_probe(self): "Test if the test fixture works" #Now test. self.check() #from mayavi.tools.show import show #show() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene self.check() # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) self.scene = engine.current_scene self.check() def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Test if the MayaVi2 visualization can be deep-copied. # Pop the source object. s = self.scene source = s.children.pop() # Add it back to see if that works without error. s.children.append(source) self.check() # Now deepcopy the source and replace the existing one with # the copy. This basically simulates cutting/copying the # object from the UI via the right-click menu on the tree # view, and pasting the copy back. source1 = copy.deepcopy(source) s.children[0] = source1 self.check() #from mayavi.tools.show import show #show() if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/tests/runtests.py0000644000175100001440000001472111674464502020672 0ustar ischnellusers00000000000000#!/usr/bin/env python """Script to run all the tests each in its own subprocess to ensure maximal correctness and minimal pain. This does make running the tests quite slow but the tests will be run correctly -- which is the important thing. """ # Author: Prabhu Ramachandran # Copyright (c) 2009, Enthought Inc. # License: BSD Style. import sys from os.path import dirname, join, isfile, isdir from glob import glob import subprocess import time import re import optparse # Use nosetests when it is available USE_NOSE = False def get_test_runner(): """Get a test runner for the tests. Uses nose if available.""" result = [sys.executable] if USE_NOSE: try: import nose except ImportError: result = [sys.executable] else: result = ['nosetests'] return result def get_tests_in_dir(pth): """Get all tests in given directory `pth`.""" files = [] files.extend(glob(join(pth, 'test*.py'))) files.extend(glob(join(pth, '*_test_case.py'))) files.extend(glob(join(pth, 'test', 'test*.py'))) files.extend(glob(join(pth, 'test', '*_test_case.py'))) files.extend(glob(join(pth, 'tests', 'test*.py'))) files.extend(glob(join(pth, 'tests', '*_test_case.py'))) return files def find_tests(tests): """Find test files given list of arguments which may be files, directories or modules.""" files = [] for test in tests: if isfile(test): files.append(test) elif isdir(test): files.extend(get_tests_in_dir(test)) else: # A module. try: # Import the module components = test.split('.') modname = '.'.join(components[:-1]) symbol = components[-1] mod = __import__(modname, globals(), locals(), [symbol]) s = getattr(mod, symbol) d = dirname(s.__file__) files.extend(get_tests_in_dir(d)) except ImportError: msg = 'Warning: %s is neither a file/directory or '\ 'module. Ignoring.'%test print msg return files def run(tests, verbose=1): """Run the given tests. Each test file is run as a unittest in a subprocess. **Parameters** :tests: List of test file paths to run as tests. These tests are best written as unittests. :verbose: An integer in (0, 1, 2). 0 specifies no output. 1 specifies moderate output and 2 very verbose output. **Returns** The error code is returned. If there are errors this is 1 if everything ran fine it returns 0. """ PIPE = subprocess.PIPE errors = [] total = 0 tot_err = 0 tot_fail = 0 total_time = 0.0 cmd_base = get_test_runner() has_nose = 'nosetests' in cmd_base for test in tests: if verbose > 1 and has_nose: cmd = cmd_base + ['-v', test] else: cmd = cmd_base + [test] # Run the test in a subprocess. if verbose > 1: print 'Running:', ' '.join(cmd) t1 = time.time() pipe = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True) # Get the status, stdout and stderr. st = pipe.wait() t2 = time.time() out, err = pipe.communicate() # Calculate number of tests run, errors and failures based on output. res = re.search('Ran\s*(\d*)\s*test', err) nt = 1 if res: nt = int(res.group(1)) total += nt res = re.search('failures=(\d*)', err) nfail = 0 if res: nfail = int(res.group(1)) nerr = 0 res = re.search('errors=(\d*)', err) if res: nerr = int(res.group(1)) tot_err += nerr tot_fail += nfail # Print output catching any errors. if verbose > 0: nsuccess = nt - nerr - nfail res = '.'*nsuccess + 'F'*nfail + 'E'*nerr sys.stdout.write(res) if st != 0: errors.append([test, st, out, err, t2 - t1]) if verbose > 1: print out print err sys.stdout.flush() total_time += t2 - t1 print '\n' + '-'*70 print "Ran %d tests in %.4g seconds\n"%(total, total_time) errorcode = 0 if len(errors) > 0: print 'FAILED: there were %d failures and %d errors'\ %(tot_fail, tot_err) for err in errors: test, st, out, err, t = err print 'File:', test print out print err errorcode = 1 else: print 'OK' return errorcode def m2_tests(verbose=1): """Run all the TVTK and mayavi tests. """ if verbose > 0: print "-"*70 print "Running TVTK tests." tests = find_tests(['tvtk']) err = run(tests, verbose) if verbose > 0: print "-"*70 print "Running Mayavi tests." tests = find_tests(['mayavi']) err += run(tests, verbose) return err def main(): usage = """%prog [options] directories/files/modules This program runs any tests in the given files and directories and also scans a module's directory for directories called 'tests' or 'test' which contain 'test_*.py' files and runs them each in a separate subprocess. """ parser = optparse.OptionParser(usage) parser.add_option("-v", "--verbose", action="store_true", default=False, dest="verbose", help="run tests in verbose mode") parser.add_option("-q", "--quiet", action="store_true", default=False, dest="quiet", help="run tests in quiet mode") parser.add_option("-n", "--nose", action="store_true", default=False, dest="nose", help="run tests using nose if it is available") options, args = parser.parse_args() verbose = 1 global USE_NOSE if options.nose: USE_NOSE = True if options.verbose: verbose = 2 if options.quiet: verbose = 0 status = 0 if len(args) == 0: status = m2_tests(verbose) else: tests = find_tests(args) if len(tests) > 0: status = run(tests, verbose=verbose) sys.exit(status) if __name__ == "__main__": main() mayavi-4.1.0/mayavi/tests/test_plot3d_mb_reader.py0000644000175100001440000000673111674464502023251 0ustar ischnellusers00000000000000# Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import abspath from StringIO import StringIO import copy import unittest # Local imports. from common import get_example_data # Enthought library imports from mayavi.core.null_engine import NullEngine from mayavi.sources.plot3d_reader import PLOT3DReader from mayavi.filters.select_output import SelectOutput from mayavi.modules.outline import Outline class TestPlot3dMbReader(unittest.TestCase): def setUp(self): """Initial setting up of test fixture, automatically called by TestCase before any other test method is invoked""" e = NullEngine() # Uncomment to see visualization for debugging etc. #e = Engine() e.start() e.new_scene() self.e=e # Read the multi-block plot3d file. r = PLOT3DReader() r.reader.set(has_byte_count=True, multi_grid=True, byte_order='little_endian') r.initialize(get_example_data('tiny.xyz'), get_example_data('tiny.q'), configure=False) e.add_source(r) # Add the filter. f = SelectOutput() e.add_filter(f) # Create an outline for the data. o = Outline() e.add_module(o) o.render() self.o=o self.r=r self.e=e self.scene = e.current_scene return def tearDown(self): """For necessary clean up, automatically called by TestCase after the test methods have been invoked""" self.e.stop() return def test_plot3d_mb_reader(self): "Test if the test fixture works" s=self.scene o=self.o #Check the bounds of the outline. self.assertEqual(o.outline_filter.output.bounds, (1.0, 2.0, 1.0, 2.0, 1.0, 2.0)) #from mayavi.tools.show import show #show() def test_save_and_restore(self): """Test if saving a visualization and restoring it works.""" engine = self.e scene = self.scene # Save visualization. f = StringIO() f.name = abspath('test.mv2') # We simulate a file. engine.save_visualization(f) f.seek(0) # So we can read this saved data. # Remove existing scene. engine.close_scene(scene) # Load visualization engine.load_visualization(f) s=self.scene = engine.current_scene o = s.children[0].children[0].children[0].children[0] self.assertEqual(o.outline_filter.output.bounds, (1.0, 2.0, 1.0, 2.0, 1.0, 2.0)) def test_deepcopied(self): """Test if the MayaVi2 visualization can be deep-copied.""" ############################################################ # Copy the reader to see if it does not pop up the UI. # Pop the source object. r=self.r e=self.e r1 = copy.deepcopy(r) e.add_source(r1) o1 = r1.children[0].children[0].children[0] self.assertEqual(o1.outline_filter.output.bounds, (1.0, 2.0, 1.0, 2.0, 1.0, 2.0)) r1.children[0].output_index = 1 self.assertEqual(o1.outline_filter.output.bounds, (2.0, 3.0, 1.0, 2.0, 1.0, 2.0)) if __name__ == '__main__': unittest.main() mayavi-4.1.0/mayavi/images/0000755000175100001440000000000011674464665016541 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/images/m2.ico0000644000175100001440000005024611674464502017550 0ustar ischnellusers00000000000000 vhv  00%)H^O(  @c|Ve^psTnlBkqlt]YC]cVr>:3/IƼYYYP:PSM[jdW\TaZRvg`E^WO4 #KICf~v^ncV/"kX3rnLiƃD0'QRJ}qqtD_[m~WhnǺ?x~tnƬiL^dn]*/$j_m]UD8jatWucZhHKdHpKW?fa9xl`]oLh{FheCXxQ[~V| x?(  LW=i& )+egzW e[OKT9}azp]+iY:\lLaWgq@%$OkF\[AL2(?(, AQ9,d~N_|Wjh|Woc-jie{cjsLrMmδoվp`zAbaAe0lstub97*Y$_h[w|nZG;LxrUQGEN=3T=;5,F9 W]U¹v9E@gy6 . &!+9)"E:1LA8TKBc]Uxjcc?'+ # (1 9 AIQaF/o^C²Gws, %"Yb\ye\L>c0uOjuBf}UǾACXR0/ *C:3uqlr^(gzE]c`Vk{q3 1 xztpl˰lxFbR]c]cmku}RPF9 C*!}urnҺlhzF]b]c^[CRF5oԾspC@j{uroԾols@bX_bbYfomδp`~oHM0%orqonniy_dG_iGRfG& btjŦmɯU*Ob[QazgfiZ \D457&;a{emLgJ&C;+!JS7^ki~Kq_)~t[MW:__f~IgoLEW9j\`hzZ??(0 b{Rd{PdlO?SfGlk6mjjĥ_p`\iFtUlmжoԼl{SU;nm?hnqrsp;cC)Z6(tvy{u\^PCOmr@MKBKM0%sN-"@[f]sWTK< 5 QQIlƽź_vC(1 *#"(0 <L8.TH>`XMxiaBSN9s/ ( "+ 5 9@GNW gT5r_DAnNJE80)#"jzn`pb]5 l>q\&fMjxSs]ld, )X_XwsoԽpsHnf0cO]cdSCXQDzK@71 0 }uqnѹlir=_[]c]cgu}{:7 SSJwspmͲkoeK]c]c]]isv^zn?= y~xtqnҹmiu@_[]d_Z9E-@OBIn϶psL&DTSH~|xtqlhxmxPjyMc[IaDpcjkȫnзfLK e_WbmhkƨZ>.R^N@`sehs`( ^;&93)01!1`uc}qY'lI}p`GO4~]gfRpb+kgGIQ5^`dMbpH>K1F\_a{W???( @ `|Td{KXmImOdEhwBmi3hfcnR?\pKnf0qn(U\[Mux{}~~e?[L?QM ufHF=MITH=yS9.E@h~ƼURNFB= : }o--(J.%: 5 3 HB;\ler}^vpKb]ou7 2 , '# !%*0 5 C(OA8WOEa[RφvqrޏND<0 *%%*0 5 ; AFLQ \B0eTA멒/ *$!- TXPXaYV[RUUKUPEM&MS\ h7s\)hd?{iUunTXO*&#"PRKyk`hXa7l>tUlk5dN^XSoq, )'7$xtqnиnnDq\&ir=bS]c]_`&1-y^nf/ - , p}vspnзlnh3gzE_\]c]ce{JQvmxD/&3 1 8ytroֿmͳj¢kn9cO]c]c]c^Xhu{u: 7 6 czq{usqnӺlʮk~gyC`Y]c]c]cdxV+4.oоsv{XbW=< ?|vtroֿmδmjs=cO]c]c]`iz[Pn`prtrFB@a}s~zvtrpnp_ml7fJ`ad^@R8VdnѷpqbHFI lqqpnmkjifzbjQkkI[eDRaD7B0"hlʮnзoֿU9+LJ\UK:F7_hjäkɬfRPU7* _q[fgi`H1WVg^S]y[dehie1a) bUA\_a{d}rT!nBgW5 \d_pj}Jq[%oa.83 %]d`\it>ko:xr\]c`XbPjoRYX\Z?O4B????(0` $boW_~R^rFZhGmpcw1>+LdPgxCko9hzeXrX}^]wThwBkm7og2hhidfo\byOlk6oa+ptCi¢jĥkƨkȫkçXoYdsJp`*sVoTkǪlʮḻmδmжnѸ[shoGtUrJmlFmͲmжnҹoԼoֿpp\zCcf@sJl>gS2nҺoվppqqrrYuBSS8m?f3`+nɳqqrssttt^((8g8`( Y dorstuuuvvv\{bF,Z UV/"tuvxyz{{{z^ti^P?UROjxz|~wIE;SOLUH<}lD U0$MJGf[{RNCKGDC {ź`xt771QI EB?J4*~jSE:C@< 9 YaWsEHJAA=: 7 4 ]kce UQG< 8 5 1 . 1ORKWc\_rkfzmty\lgG^X< 6 3 / , (%#!"#&), 0 3 8E,#MB8SMEXUNygcoasj5 1 - *&# "%(, 0 3 7 ; >BFJ S4'UC6ZPD|v#!;0 , (%!!%(, 0 4 7 ; ?BFJNQU^)cP3d\D{rdQpjl/ + ($ "&)477 8 < @CGKNRW_& g6pEsZ%flBrl]q=( + ($!!' Zd]znbwZOBTY a*i9rIsX!mg1hxB^yOmra)305w, )&#""$z~yusoϻfleA"l>tNq\&lk5gzEbT]d`nLb„- *(&%&dun}xusqoֿmγnyRuRoa+jp:e~I`Y]c]c]yPlŻ/ , *))1|wtsqoվmϵkɬqrAnf0hu@cO^^]c]c]cdsJ'409zm1 . - , , dwpzvtrpoӻmͳkǪlvll6gzEaU]c]c]c]c]_\|z~RSJ3 1 0 / 2}xusqpnѹl̰jƧj~ir=dL_[]c]c]c]c]cx_gw{95 4 3 2 XaX~zvtrqoվmϵlʭjĥjggyCbR]a]c]c]c]c\_'3-= =|ɾ~{xutrqpnҹlͱlmwEit>dL_[]c]c\_WrP &1)@nϸprstuYdXCB@@X`V}zwutsqpnӻnqSod/ko9f|Ga[]cYZ6G1`\zlnзoվprsrIFDDD u~~|zwtqonȺkid_~fbkKjjChqLXgENaC2?,Z`ḻnѷoվpqcJHGGTLB,;7U'41G'%9,  fkȫḻnзoԼpU9+LKJN$33,@-8-fhjäkǪl˰mϵfQONM]VMVkXghiãkƨlʮZ>.SRQV;.Zz`fghig~Z WUV"?:0p\jdeghdF'`( ]# [! YPC !axcdei}Ui9g4d0`P82:(`va{bfusNpEm@gT0 EU:_n`taymo;sVuQqX'xknSiH]g^ldZnf0pa*p`*gcJUhG]d_^gyCjq;ll6]cAShH^abTdLf}HZhCE_?]c^^_X[hJ. WWZV:J0^????( ;L68lWh|d69)`cT~rXKAw~k23"W[RVPEmaBX/7/rB$snm9aYgvcfZ[OnlhzZ`aDT87]sPmi;IB/7V}SkxY??mayavi-4.1.0/mayavi/images/m2_about.jpg0000644000175100001440000032271611674464665020766 0ustar ischnellusers00000000000000JFIFyyExifMM*CC"   !1AQ aq" #2RSTU$34568BWrstu%CVx'9DEYbw&(cdg)7FfvyG b !1A"Qa2q#BRb$3Ce%567FSTfrs 'EGWcu&UVdt4Dv ?ƍ4`P~Cg'rGXX1=!AG#˂iD"ŻZ,_r2oMVv$?G .*{s\i{jS9"qONv~#xEo*d] &rBmڽ^ә}@*'Dqe0㉶!T~[q7oߞ$'FV 4hF0`ѣF4hѣ #Bx9)iӉa%ݬF*W#S=ls$evuJdnHBᥟ^cM>&Z=< >٫TtjKUs!OLn7 IqZ@͹̵4)R_S6q6ilm{~y?*/KK'$zt3x.lnl0Y,JsA)W:q(lz-wM(]їq)N2*˜tp8*eEUk`ʀ?I>{r^=eH]"r)0X"O\&+"=bAGW*+qS*v2 ZS8bH"]hѪ4`ll${ycY:t((9:m2h܊tunYeB)ncn$3#bΖ3Ƽ*bTEy2 <7j>dvdL;>)-?]5RK$dr&[aH+j&du"SP\ˊLⴡޒPErieʛ*L%2i%Ҥzp{7*[t4hѯ>0`ѣF4hѣ 4hF0`ѣF4hѣ 4hF0`ѣF4hѣ 4hF0`ѣF4hѣ 4hFq,hʪr&uQCiNsФ!@Ls@(@: Ѩ#ӝCe3#N^[ Tr UF6~* JP23]Ut̩LbamK  s4E^PmȷXf7jpTw0,UZ䫩lщn6C|MKYL*#xbm喸ժƒv ZeFxse\\̬Gʹ[=ҷr2Ryruʘ8W+;,d$ ;WcYEUKp_w\=bhK*tIըΓ|-0/pKWn$흳ɘQ/ bvRYKC;tYQ!AaiZa% SANEТᥰk|)5)]hKRS HaV] ܐgZC(S%OLqwұ4D-ϛZt.'F5b`ת/庱^,,,X Z^30-z8|oqeec$r  O/N3j: u!6Keݵ~_?x{ȕ,GīE"9??#YYN&gnJ{&JFZ\6fܲ.k qճ ],/%AX]q/Rm'ie5\ݾ2Ͷ#o;15G óTV)/&RUI"ҝH%AHUV\R- 'ڈ#@6P!Qפ'?ݿk1《.+]~(lk5+V/P|{n^6]MBu_iM8.ԏlߺ7P*phѣZ 4hF0`jT$񳦋)FfxnPQk!/!U)vXlP#rEd5uDHN^8:FU0tr(8špE9SzIR3lM-JkiJ|5zRj-ӗ{hip^[6M^q|XSɿ\t9k>B*QLg:5lthѯEs~Xg<\TBSeSQHQeO@$]؏,[XPӼ3O:|I;<0.ҖVXHwk\(P-Rwv}|SDSbQUWvHcEA; H  UhQfS%%o-:ҮBZ Í5L4g]BV/k4;ƾƱpejtߙ%}1LQ_8\.9AF,R8ڼWf8p#ҷYZuiHUb)@T$B9R( ?~UY7K>xFE ެw,Lu\.P1cpB͟0KXRq=*HK5N{EIfXtEHi] \"-oqΛZӵ,?0sf! a) 4E`1o!J&0@DVoßO)l~?aZyjvw%zLpiצ+Mtxb*ĭRQ{T!!/hѯ qkphѣpF 5LP1C!1 phѣF>ѣF4hѣ 4hF0`ѣF4hѸx ;PpˏFF0`ѣF4hѣ 4hMËvVYzdaD ^L.+pI &)G>f) 6N|Ӧƚt3rzV M@ ֤qqi6R8R51{qmoo|h'*2>՛UޤiIý+tt@rnJd! %e,ž ̒A(@|4s ]@JbfZQg+Xk6^9e_5: nCמ@ש%'3sZ(haAҩ)U~$ݥhǭ%zZL`J̼1͵# Ԫ9L!jZqo)q-:vRp& lC=(]7g4gQ v[nJ]l{ krĎN^EXSI3Hw%\ԝ&'3]S7|qjX403ms&;؛3gZ-"WXH-g粴/J\i)7p2 C*-'&hCAF)(RmUBӢ<=1rRJ&+dZ}\ld#p@18̪{ɕRJ(M%ZɄ,hҜpUTYB9'AQGl0GrH!S2QC}dsB(n6ÑPjԕr84hѭكF0`})߈薸>JL#8ճo[O\tn("0/l2srkNGjH%O䊘ItUD=/["ͩ3ؑ14{츶,d2/.q2 GcOkpd!DZ "M2B Jg&+j=5](r-T+T #Jb5Qz-hܸTWݎѩa/M-SQگwPH1grZ(U,hv.g뉪 9E'!1 tUkv"`Z+׉V'ZSw/ ͔"^6Ed!(m/ld Ke6tэ3m j8|W.2MȪ(e9g‘BcDLإt$% W)}ƥMBN٥d? E]*/FcUBv˅ѣ]QL]II8n=W^vjͣT.WPA4[LJD)8J#H%)JT)A)JET`sKda3>94w7 )n*9% &Z61uب)qud8/)eK, -vꀐ#鐄N4;'j)Hxq2몡pG<mIL QhDOKHTaW]KQ 5ۑ%i8v`鷴]wc}PWÄZZ5[sXrGbǮfH58/NtUr&n|V6b9nkìv 8xˤE0\1-ie]U\8UE]C(eVYe &UuPDꬡu0qs9_2>iGq]:nn)m ۶U6$|ʧIL[c]ʺeʢW.2q"3*PnU[7f&!e/IiuJL$W"%n(Ul]aKqЖ !E:RS(P:9YW;]ueW]w+D,eUC21100Üaq1Zƙɪ(jɧtM+'IAFl"d]$*Gٸݲv-,E ;jhU61rg-&Ak?D/2J1"ۋǮ²nLgvs9EHG7 gNv/G-̧+4dC+>)ςL@%8m pqtoŌB3:7)lޔN#qNTeI+"эMzAAwoZg?q85y_x{b,xoCŎSN;Ǔq6S,tg;HK1_UzrƠ̶\|0-)+9"ji8XK2dWfMw-!ET!r rج3v$fgX&T{-96ii7jt~Μ!eN$!%!ZToEN)Y%A:ej5UezU<R !+Ai)#Ci&<|K5&\2N<}i㩍n`);V$e21r1ۄ@D=X1R"E 移czGa8Otz-݈,qS;^$ $n<]P0(m xpw˔ ` %8!N`߿nzfY糇뙣+ŭVZu{eHFBXfKL#\HBTldo=J.ˉahmEN-NJq <,efqoO^-9_oC]tΘa71rDJnbuOvh$HH+{]R`#[30yCxL`((JB %) "%!ǥk[7orIvP-!\k JFsW.U)C7IAH37s[DR ݙd*[l"Mj4hRe*VRqռ!ә!Kl/Z-{)\8[n (Ns c_͎8b}qllVǓv-U~*]ؠ9"+9'tjEmo'+"D[Lg9Gi_]aǩVdUԀ H*5~ݹ$f "epE]9QVÇɨ̑QE5|טڄ[([eE*-+MMlJRNj+PmwTf kHPQ!$r6D ӹ9'xAkgN. QQUNe%(@1~R|yr\hO|8B$DIV"面$gγ/YE'U2(e}\JYБ i.TT4/k-of M• [( xb0Y%>>,0Uq*dH$%d8v5i,\ȷH a83MSkݱz?i+2NPRSԪ1J2tE! ٰnP i˹ ^> %|F]fқ\XuNXOo^igsCH7R2Q|6E[{DIU9aeIQq_sxi,vʷiN؟D8|o٪ҥ*PnhǪ!zk6$dQtw2OỖ8a%"pQXo;H58D!ȡJb.w+>CN64o1{'"eSI!QOHg8#4NQ *!P$ʆNTMԖ$Udp^[Jm23iCTqiJq0Zөi_M*ei2%^vs13H5L8XMeU(NAԺEt$h7%[ϓGX"_aK$h{I4Y;bXIM^4pvTAAKN?EMDn+S\>j'ݺk6&&PC""6T⭃yN8vC( ˌ] $1Ϸ\8$;nMyz!vk8j~U6Nj)%#Ydm\j[ZS%rIME:{z3u !9O;ٞI.\0ycOC!3L]-qU $WbWղ2$Bp{2^ㄒcKU(zaůqѪZ{/TBnop.+^/ sj~$!ITG2K;Gl\CuQ^]3IԞWi\Zʿ4g$j*^)x͡7uiEnVah9e4n8ņ/b.M^̄h)(7mM,IPKz*.R@!d8=ඁ_R(rp[mH5-@2_eeӤP m֜V-.)%8jJ,IiHFaUF[hP8c{`N?n=]ZT"3 5JX(2V85Z6 Y0ɘ86ֶi{čv5olN꤮C[|K7 @DJѪX!,EHVeE觶PG~EENBvQsV a@E`,'fj|\6(hoG+'-%2w.Ĵa`p3hw4Mm1 SPeP< $Ĩ \ļ"/HqN-A[hpB {$aaFjJ3E4;(jɹb加*7A\IرbTkٹo\[ZCqHb݋_',l iW"bw+lR܍ j)D(EƭJlDgq/enUͥehii՟,ʳN{j&]M0t-.תVH]+zd gUƚ̪AU>cֻ)%kKUw) wW2Smt㬔JzuĔVr6'0kYK$\VjhʪX>I("R3 y4u EMRpA52+vi7wRS;k'"fNAN%zCMXf S/nA($ >*lڷcSdS#+\A:]-΂2q“'뱪lY[4ʡ.VKZ5xRخSl}Q~cޱcpGd7=**1,)W mwȦɵ]Fv۪r$[ȧ)nGL3r鬃"0cG C$T%IRRBX4hѣK?Lx{A隤|ٵ;o FΡTՎ9JR&,^Tͺn][B֮*$]MPl熎#v"yw9LTXpes©F;E _vB.þd6\yF2tcH*&ab9AVQ r1UDH)9YNퟱr0i$Qn2*t זҥ ! 6_yYJ=72礖ܿ[zN)0w 6(8 RXxXi&ɦ,jڲE=QYșnRX\koڠblR)sOX!'00G,\CmT '/Cy "wt)dp 2R;M"( oRaS~*$氺@c1o4Nُ"#0DSBPS:L]ivzjS2% &c%Xyu#"=Vq^&bLn9|zZ8UBS4s<0$ M׳|'-Vw8n[;&Rn[Lx>i;Lb&H9'۪nTyLnXױDDFŃO4LR)$11.Ѻ&Q4RE) =j4q=+;8ڞ2^2ݔMVe\E=2Hb,lK3Y>Tfk>R j)h=÷\'OdF`/+u2 d~3GicP<`y -kF0^l1: b:^7+J-'l@1ڱV x"T=vBqR#*L>vPrqI=,tsEuћFE;W*tL-,H9&PHPƒqr~xpn.ҋWvˣ8 ;k,9(4!P3д].6D6Rd;s-&$w%>e4BXZ}&ؐ""d Xe=t& HeѰIw,Z7ILAǼ8+MQM K*zxyqKU޲f)/0Pq!-\vhYWӨ1T.KnieηM"tud<*"\D-,CJۼ4nHJI%V'Vƍ4h%ġkx(-Mf8%AdEJؠJ8E(䣈Ww"wWC|+gvj{܏X)VAR(SE]Y㢐r8bRGSIsa;Tp6yI/31MBgRr :gۘijYjiXʼn*]l6FLq;1ZRcz8{@˯G:C8[!Gkl0}b͕.ֱmr*AbJjPHEd]N&nP$(Pb#5HtL"PL0B(QHS6!@)(=_G~᷋alU/SmX\~\.eIY]yT?&fnsΝ'fMޒViZ(Ǟi@l˒^I|mqN+p6J}T71{Ed!THQ3MBC0t1 Q)"Su.=6 |iyIJ!öyY0PY0TUJ2r*@HdJ` yj ?ݵFjT4,B8}?yq3S.,[NppdԴhۦ6)Lg)j:v$'Jd-oC^JlPB=ҪUf+p~hyѠZ߆|K?[\㜉ëJ2y$ݳ:brGhMfs*鬙1 bB}in20btaou`1[hM{NQ!>t6j&"c"໦a 7);V/Lgse^q.p8=ŜZt m֤챾C-)rt[RN?n*PQX[Qt e26 T첓>s:RuRn1j@$eH\3 *Q^HB.l*1q{e)UJ鮾I dZYM&"^  6p4rL"룺2jVza1' ?dEʑqA}a)[f{˥`$(ws֢A_$4zL (V0)er2-n`{²qI*:&ǒ/g#&s`FH98Lߖ,Wl˄ bVζ(bm ҹ4؛oGMo rqfK;19IR BE]hA7P776ӨB")KTK3_߮D%7WHZ8R*Q̓k T<옳cbͭa ~p<10 8҂EU@zq2>;1W`ˎ*t%s…TE5ĕH>rF̤YYsl KTkfr{fRFv&fGqsmEhɔYf+]xlV؂mA͌Ri)^!"n!q#lE'QfAnSԇB.h\㜻fBzFO[nWX L$-![Je#*[iϨRU$ My=Uki5^p/zN%LJviܣX8rV#]-V `Unm?徢"z!cY2z5VVT`fm*XA)X' H#`LAz%RwӗCZk̷.=95q"TvRPw$1ҡwHMKҲ쉪6h,RPP栥)iEH0sI.]1V YK?+jLSpR=+$ W:mZ2vNLqoJ SdW+uBps} C!]96﫷m}3Ni^--ft`0h=$it;7 b03+4H+e\"5 &9D sv@H*|7zTs Py+n25' E\1q \10T>TP!%"m{)/p}|w) 沋ú02q* 5z^=)3]9o ;tBdPEo0ʪf*JI[~CL ֞n4uڙi% (iԬ<OS^LXm \`7R@mqc\9%G4xnEsGQT*˷|1e7P+O(jm*T3¶#VbʹEl+brT^n.~ɓID"}jvݒN5r eCpݐyG cv2tw E[\9CX w].SkӦS>"DrQl"zR L8P^ fRd0jK1첕ړZQCM4%[XXI,mqGdjk7.֫O0ڮMtVD1ȲJSQ1/1NSa1DjxڥřbC9kaە$ "" dXn'T !˲M+?frF1vM=,!o0'q,O6bvHp6qJO9dt *|uffS$@*wfZV7) 2Xs5KpV _TO?R?Ugqx,Lq$ur,2Z<g.a 2dTndݵK彏1!Tp2X?|:rJYcllٱT4TQU*1&Ū˴y˱cJ^Yᨷ\(ZJC47PcQ%e A tL[si`1͊dq a&8l)<{20vDܻ Sqsu8-z.A욥fSs^SEluTD;PWBW0 K#Qf c+.UMBt]i(1bAySIRWfݚv{J6JR9Q\PK@N7b8%@ -5kpF֪[ܽ-q,8lH7%c{Ēw_4¥Wg+`d"HOQbIT˷PQ1Q)iƙ{"`_,NF10vRE)H+)'NQ2S6DzZYZݙi<9OFREqi㋂B\6eV!D:34-˄6S$]_u$jIF{&jV6mpK;3ՊWGM\ꘈSLP.g(C5Zp'K.! xZ%EnXB%:Z_RXrȴ2MЖ)CM[YJ@v|q/5d K-FiA!m*qnl!$q[m qťBJd7,v-:0V٨ùModW;nS2M%T܉0{3%)f"SX#9 dT )C=yvPJgCd!sCtYTM)W@ DYAٱoɉA9\|Qk_Q=h0fBVa!zK->\[)[RZmADggt5)֡24 ҧT,J@-ڍobmYŌ%etc(\sSL9.a~ޏ:n+^l `0D֋x"m"*lP ҪZ5fbfq/.!\iSeppW.($y1:T"EV{RV\"FsU`f wg|0_ lwBG!)V [&8zB3R%H,pgkD*J.JmZB_ KhI;i^*(;LRCr!Zr&ԧ]R\jU{PQ)VdVf>.+rT}`:RLAS% hm$ Yui+ޛs 7G1BCYqr8ʉ_] ? 8k4cpVyH :E[fVQ՚"dQhNz(VV,TU>|g%Y#:E؂qv*ò7+ROR3બnQf*GJ$ot$EU[(Pnw-ApWucjm+ j[9+",2 ݞ0WP\6d&AD]JvNʌHO227'2/L8fdV\+u\}at*?6K:y<ڶ(O4yW9븲ۛ% B^B[F(I(ߡVdp~nVfi%Q9beI_DLZ+OHͨ}Md I$7܈l?{_tp㪥%[8f*hnsҧa+;In e^Y(7ǷTU#ڣORA8PV-t3.R̴NAiޢXMhcÒupL 6{e&2* ݹXqˬlZTɴ3y2/вN[e gfbof"٤&ABvX ^DkXa1HUFG˫ J,ϮQn_fey~&Zm6Tٽ25MB rq2vJk0X.DZwNΉlGGHݚA]sLyNJ23L722o&$*w1U`wZbS:T *!γ]!ogn UQr*6QPSZ V(uHʃPI!_mHRmaA#b/| r4KRiA-'Jbm¹E2yDfYiID0nUEDH?n岠U9AV>#AVs-O*Nu-܏r+u$mu"@ab҈#PxyNDkB&(m6$ iTYBWЁFRnI6p!rX>˙&ľmȮvn=ۗeE}(3X!8Vixd A-1f@ȝȰA=5[S&aȗfz{rH^b.2>mF1I=Q,d9ܲ*%F͐rb3|QM9'֚" -HT ba`էD%^tIsY0V m_x=EzuL~m߮=5k^5`wA@P7 ^c]2׳}>M5i%kI)+k" ;& $)U<* pH өT:9IΑACtX !1A(D#Ǥhg<"ž: QE)q: !J$ܝ6M"M9u8ڢ KCZ$J'HV}j(XFGeJ"5ň*[)+Src"*ʘ .ݎZ(ͭt$ <-ٻ 2䍓z so"v@t7H}J*PO'S.9%xיiHyJ-m !<+ W2?1AyP͕tƍ{PvBJ=Hۉ^jI0t($m/!*EE BT?%TeWP(( 2ޏ5boF$^'BNHHB:2)%סAv )#*EIˤ0Nsۻ_8[F9ofΜd! ?u; "ܱ@YQFW:ҕtI̆ݐ-4>:ܕ ɺ8so_NHXS7`,e +ndՔnbv3r; 3(Cyِ)Tj~eL!IZ"=!ҖᲅJ*rm8:s $)סmmn)&. O];Aͥ{ j%R:NQ1 c).إLL"9 N)L=DzjKmƲ dV*%}^Hs9+N59W2n{Q^̩K_+7'-"QuyHI7h#?3ۮeUU. eWLۇb3d*R"]B_~LGmV-zHZ hc376릸.2%Otߞ6;mBDȲC.M3ItSxDE&j&徽ȥU'*p+ Pjd9MҴŵdYƔղ' 1ږñNmM*۝NUXpV8Fg# Ivʗ])bl8n9L:Rc~H~Zn$gz̚.%RchHp!Swt1"F2QlEA2"2Wc*Y wNԦ̢"SYI4@ i4oUn t#;Om'&m38=[HO6n)Ũ9˼4V4\\'Sn2ӵ Ë#JPGwS@͙c7}*5b5§3vq1Hcdi+?X&grԛ'@QQED%P^p!DR&>5?Mѩ(eHObǦ &cӰ"&***DJ'2o{z#e.s+vP^HQe:Oc#lqG8&eQ\@_'Յ|:yBB^-o{(iS.>PAzT6>I\J.hhlBZb+iܨVId݆lOI,Eԡ%ffݘ̢.=I#鸜M|RuuسIQ ?GN?hT:uv6TdV-#[ PpÞS8ve].u ˝Ay_z(8uko!ZUGi.ܰKo.2[Q5sfK4ճaY225RD*Ca9z|BSW+Y6}:5<@nEJB˂j}%7:Rn*'nI ij?Bg)BsM2ؒ)iL9ᄞ&K>Fzu{q-VB:v&y;I8i"q EF(%0byR[a܆^,ݼ@Fɹ+b4qi 6+Q59'Rh1d *)>1ba/dÃ%M٦g<YyziLk?X!Ԅ_Y(fW @e6h^ ,\ƥ-4<$+ۭH\\*g񐿣kBѤ-ń.mUC6ɝQM s H9LHECAciJ?<&&%NH+`]Ҁf-]>tb EURjM$O*1"z\(!ƒlHZpmnDVI[6ANhljRHas+H8.8蹗n9{ΒRMaܠVQf%Y%Q]E-Z6E&E4nDEHd"i"e! RM!8`WLGJL̚hٝ J8( 2t 6&X)pnNq2*^9~)Io"Ugg!&{ sl2[ &e"hX "pGRU̶6R4߸@c9jSkZ8w,4ʳ|ٜ⡚Xf#[!&?9)i!Jdk E)# dQȨCm?*B:DT+lҎF6h7hR)9.c9p :ʜn%pT{n1d-BޢII2FqU:2YGgAjU:Q􆖕4g gP7Y+JVӉ?*eDI +#}Id\ҮnI֫&}_70CPx؏"0<3XgőNK%`Y@]aۮ=t;ϹNCOUq.o`Md_zD\uڽt!LT! `)Jq( $2'{ˋJIE;g+_rzft`)U eC ؋3c7oi&ӯX1RIlQBnY͚8\ꂍ*AUM#S{UkpIWrN%9jMAt[ԥm,).%ѤնP(Y5V#ʃ)D{")MDGsN(CmRi1WL[l$&RMrk9fDI\8`h{xL.AMaFaDYTIR\eS}gzj)y5]^B҂.! 2kP;*ŦPu 3=>-Ez.~D#''7KJ˕IGlb+8.ȼ3P`g.rRL{q F"=䌬\T^ӯb~p:a͋5];\Ը[^ŸE{)v9+$#"n͹+Pa%gr%QjS]ڬXt\UUa\DHB]J 87֠) zE\"؁+""4% uVU-J㔗[QUa$d9;.Ækgoűc+ ~S,qudY4`)zV.s곖FWgW&wDT6jfVCdkiJrEN),e{NvTz<-r!c2l&DV2_i'9&=>8=q/ liqT#z#\QkocqNƬSUư9'6pA|^nL2rQM/pnSCBHYU=3Z SGq%+=o$ *yH*[:ԐvrNʰfy!)et4}%jD l HXC-)MԷy|Ɗ,l'G0HYX(H)xƊ3uX]~,ެ(&ۉ0p_U07XDw+)ۤg(v l{3$eHQtWMQk5?#'Rb"ul$/%E8Z13.[ٻxjsrMpes0gP1 c+2E]itʁMP<r,PK석V!$|uBWcHp }XR]Iuo`ŜICV3,|H^Eg@P8ljZc 2$4lU%ܻEVJk6Yb7GHPxB)"M4Uc7_s TTT;+4ت.C(&R4rYE2,+?n6m6N<*Us؍[AD]u 5rwl%Dż;#AnKEg.3 -A;i Uck n,[sgekSpNȱ`IǸ#/9DUMtL:jCK?C-V;+Ig,V3D7U2(M6"xf`"fw <)ؓ(cP;zS&t3Cs*"U$#u:_$$"#.{QǜXӲŖs%Cf: P'2٦SdJR] 'p(3.ݱF{LN? cK$!%r7N\icF|icH*ФiC㖀u}oÞ[5XamPzܓ *>nf1O#eZ:ED8fȹlf2jC Dszd *BT )*O0AbcFF4F0hѣF 4hу4hу4`}F h3ʹfBWeOzpxzj,̹L,Jڣ˹9m?82Frr`'A <\<$K.=^N6ԁȨE!'`Y&(5Vn0fՠ9tt2)9zܵr~=LXשL9UˤrD\ b`'e1DHXǼwO@^Ss& r gӡf%,IFm4jǺfB>$81IKjaHX5\㙫(]>[%(HZJBu"[V;X8TG!?z(=>0>h£tc601U' fv P^kxw/%:}ގOQ_:fkX弴E~1E\UfhJz‹V'#hd|̨.|vX- rΩ_z%٤MuΒVTJ'&ȴ2"eV1{w-VG3)dg>Ko44Ĉ8ӛ&[wLxeN`>9nbUhH4!hq@S6 l#VZ]WAxzH󨉗ڡ)-VfA^6f!㛪ᷬ?4x*ź2Uڤ48Tƣ4tPpp6Xa6l!'(Ż&&j$u$!v +E!`}tj;}%J*Ɯch ̽t3d=dsW1/;f sG%jeHTpsQ8xw)|:MoY:K7fUX`PS} *w8!ST3VylxCJB&>u>x箱OB1djwGQxbBe@DW6dq.EsSsX&w`tvII;Tu)J[)aj_[JQvPHs:o-2N]"kj1H*4S`%wDžEѕn{F+Nrj_qa&PUk5MwN^d"+`nie Iv@di4 {KEJLMQ' "Ɔgm wz6^(\E9IY `gGQ5mlA,CCx(bKud*Yxy+MAT%G8p*I*m n2X%m4HJkbMS%ǃNo;Ԏ;JRMWesknr]\ErE=k,[m}0#C}YZGhe_-qpU2qj5|;?,᫉.kA_ȶDlGmsl}N\?^.cMOY6ɗ^x{36*d%J?C+-SժW {n1,Ǚ{E4ȕyy╶ݰff;4Z*O關ɋ,a{loXiHJ0xo֥d!R!..ܴx_|MV3(O9h*tF+WRVUG(I7^Sm[fFc8㞮p).u@5r RJIJ7Df[〄iXykЅt,+ fC[e $fiJ\N!'Cݜخ6uEb-^. um$y~2.°WQq"ƶDຓR|em!Y+ٻ7cYm3SS*Tcdd[XG::m/xno Ⱥ2友a1TAϙLVOn[KNq y,a<,Ck/9b^Iv˪*2DD@D`6$1Cm#xjeT Ɛ%>)q ]sk cb?]DvMG:q±HO$ eC [8 ݢe0SSc(@@"pml4 c$a0tN`f)#HIC9 'dX7Q2n`7"KDST™C2cJW(YV)<7|N@@_pƿ l'1`gq+GeM%z䨽ܠ٠wJ; @ PP9bb ?!אæ`*c5Mm vo53i)r!wqyDp 'ʺhEBlqP05{ZR1k(Ld=E>z9tRLQ)D(̬t5HY; >ΌZ-tMH\8G49zА_hs _IlONFVC؄yAI }!;{ۻ3Y BF.;^a@ԀtGz#ilLUQ;.|y;a PIm]mח\HN1|n6c=ww6@ l)bm AM\:Csm^۞߫ i`&Mi{ېUlݱ}Xy䢆Omto!ۦBME>|Ra~/lmu>/ՇeMF3snVQ]"=`"'iM6H6ZNw+*]Y#Ģ1/U~f8Ԥ-?T?gy ;L]:wþ mGo boI-հ>1~ 5}oVWԆ?mǍ nU/h},1$PV9@LB"Pmf&pۯc6Gk;|c@Yӯv&{Q<MMA:__?+o HF\ɟ`xuDniק]69o0؛.zr"3Gp˿iqݼ9wgۄdz.\P6Ze_gx6%Fx:,/W > O@$&&b q@ra~~]wN`ܔ~;ۗ;ۖ8.\o=yv_[yucy?Xx ^B ePf!`r=;%W"ZrT6 iǝRH %K iX[mamH>z6 5 oI < Q)yCњ=YPqB;)QTaR7ՒF8ZWn MS8ai,ۺ]ђ 8`9eErӵp b|WM #ue-CN7azOЋP1K#|>"MzZ9L+#(1C&WT9 gl\U6lodUA3cWHE]TM@/.7h~oa":vz5bfkϗlF"V|141o\^4<۷M2"#e3#6!a&rCi\u){1D2L.}s %c5Hm"TJPа- l#o&vQݑ f+7Y$`+hCSu<0 1qH70ñaڔñJ#@ Pq%CZȥXcI(Ǐl7x9T?inS$P1L`ҋDBN@ҩ+qZE3ITX>Rna0b)=*3;|pjZ3b$lyve/5%2via*@Do&H_UQƾ36Љʋ|'J8MUd$T"oK0#|j*zcQ7M9Ԍbw۔00bHh4,}QVJ`1uY̢A3ItrRO9Sgސn2&LD;!=: `g VĴswglvKJ8z5ThgoJp85Ċ&ȊYb|j̈^]ͯj o"Lf6`(NI n-{q8*cU"`w ma)(͢J$f0s@9%Hb |MQ*<9|QQNt5eV%[&ټQVlWg.rM8.[&SDTH 0KpӈB%BI.0fB"i&nVG8*J4>/1[p|Ca+*+Vm$[un<$T,uW|cUN\2וa]tᑔt5fi $,BIr#tAc<(,D p\IGLlČ&"Q#9@&hJ,u=?svG9tf9trTE$r,cN'8aG!M"bU`TձJP۬J4vl2 sJ{EsS]((6͔;sAaNMZ6Z2MD<ʁt 0#rI޹)TJgNtk*GPirG5>%EJWȈJӮ8f6x,G!UզmLTjd᪉f`VQcӷڪux5RZ( сJRFQ<57ƨFGFyܼ:.,@G)+ؚz PQxQq693z>)^H@F#*${[2S嘗-)W68RѭB;੠B-*6-ؓ}ĤXC6:d>NuVI>m}8dz {Ze֙p|kUT^$,"YY򡎸kACø* yhk3<-mRf)5½[y >q =]Y3NN +cEgQ&--ع#Q1}ǹkDWNJi4Mwmd?J1]GK!&% 0W'[ 9MAVr7s~Fmr?ɔ+=ΣxSSRa.d$jܖ:wzY>d{j_ťimo K/2:;ݹPr8uRQ9!6i|pi1Rk\He[rjd 4*g+fy%X ^lLݎxZR6tʼn1nGr5:LZ0v4tTSB)|.>FuyMa.wk.8᠜:`s1u逵*B8@k|# 8U.dl-h˹O0@ƌVOF+;ݞ2eGh)ͪyhlpWˉ)ՓȬg~նW`|V6ݐ>\RWmX4F B<̧K.VJ42BQ(mR D߿ͰZKza(JuCZ[x$[xzHV)334I  YҌټ?[2nuk;xHW۫sn"@ c a7D<6=XC{_} lnn4+ 6@ansy>A9vr(%Ha]Vqι2"v8}~aLC~J6?qʯ}?V.QB>D)ID]9k~X*Uypo=]-\ (ٰwn;GCW˜0WH!b8 gcb[cJx4WE>~Sl$X)!uW`Oa6/6@6v[}ܪ\ɱdF[2wZHSo4 A"l42~y9ey-2#=pW_=zp~n/1;{;:yorC;y@vo yzl;^[N CV$ny\۞xj6@- mZ w{|Ln%omzk{~?o]A>oȐAv૆6Qr}:XVU(?q*(( BpOf~0x4vg ;˻ǾEĦUIe(qvY7 ScP;r/~0`e@~gdJ#}:E6ðuzyt!qUFͽ;5^HSۢI;XUٰ|zoiõ?S$NJ;ox|į^j "(zw<߿!p} \&&~^cHʄy%(M|?: DG=|yㄹ]%rۨ}î;3SdïO/=ʠ{}_ŮNolrjJoa4YQ"ޗ<% v?C>=6ۿwLf1mͻp w:t=|{<{k21)S߿o~ӮR({=-S^ˮ$87Ks%10;matMcyy7xq7Ob}l>unG J"r߬@@<~ݩF\R]ې@;w=[,'ѷ]"y}K1S߱C6iMB'‰;ǧۆ {rNM7N~KtZu;۝(ҥ&ַßMEN>X_kv; 5a$cQLP )S9yZSc%^Qh!B>؃++s0 m|9vDLQuE$^poӧ[|ԶZ &܀#t 1=JS)BẢ]13r4#ltd].BPL`lX;($^P4T9C "$TG嫸ʋQ슬/QF:#$R*S*qm{;p~9Bf?XGČ c?)Hǣqmo}6BI@׻}|gez_Iu=ƌ8{~; ԗդJ8mڙJM@T$E# !.co$袰4Oq83GJTآЛhb%pY%%^*9ҧwki覭d" Haǽ rX2 J6*̈RۂL%)QXOIkJCR-AOIbKZSF9X'h!侒_>3=/ל 5q7(Wڧ8]IYTJ YZ`Qk6)-081ni~f[C5FЙ:R/"K7hLr1*_TYD0pUW}!>#d˅%e9f]yRyn@)*N)*W~5R0e֕[o-4U[sB}>3=/_kjg2q3 qK_0;} r%z&62!5Nl݂hĕk$ λng:(z~i~@tfʤAv)̆%KKz Lr<mi; $\hDϦ2jBRa53! z:ũf4wϧv_uZ_(>%i6@BEV~&&L˥ف3yv뿙o#Zeqt-?_Y)?*]?ƞfaCaS~E֏OK"^c?iQGд_GY)?*yspt-7v_u;/ט#Zeq 9B;?{QK̏=&?Gm7r +5js(ESEC9ÔD2G1@LDw]OK/38VI?ҴuYٛ?7y/agW/|gez_Iu1I?ҴuGOlw %Xl%b6PQp2#|W5Lv fP*_vesƵ.;.<&ʏC*"ޱ^Bqn8.WzT94yHuM8򜑙Zm65-D܄k7Gv_Z(mlnl9# ӫ^]+q|s]JK 6NB©P`ě1ʵhŝ9Q 3E?}5gOrAKCI";m5@]wq$de* FE5E(Ybv}k?U"4:`F@"<89yT2̎D\"  H^-B@˽URGdlJQ9is*n$% rNIM+o[3tZ!.H{7xjnQXK$@T2V= Z36z(4JhAb굲E]dS3eC짅.ˋݡeI]إ03NJ(y7N*)C2kQ2N9#6M̴IY& ujKVTEUVSsi nBtpԕ7e G9^(9މKT$`fJibLͨJZN ?u&[.P←I p4GpɲXDL J=>;30ѣF 6-4`P$ؖsqs?HaY2+̀FU&@I6CQtJ#%~[S9s >a#,,J6f?wf-7t>0[l, %kK!ZӆkRҎYt13+4D R>Yz ^w@T)sgk ͲzEKUpl 2n܁Z~hdforLVn\>#Lutl WCm\@M90믥LnKii+񥲖ChZrK:PY4 8Ѿʬ W_u4ưY_.яVp:.eSfѹd0Fyʟg:I$31qHܽx1ԼK/jX1XӔtطVrl<85*~sWYӸޕ%ӚY᭵|Sh $fߵ)kXt9 X 吶I˨JYJ.\pM-0,TC2M7i|)3$Վ^))Un]VDNeˇ;F2ar$RRbML#lqx=\%DZ'*P˒豴㶗Zr\IaJfQ rM`2ky& CUoߣ.4Lg/$F5Kǩj[ǯwRYgҍnrk_೻V[^|H; Mp=-3dR.7yrYNr!IB.\JB kB|۝m”Q`% JOC+ɬ1%\m% xæ>y9g9 cepCVɘX^u#v`J:t Yyę=/kUkm2N,0< ./^c[cfonBΪAHvAp邤[SS*Y|?Ĺpq#>۱hC׉C&gxk8pN60d 4j앆R*:MsO &gƶZojC1EY(vlC8X(.ox ZOXe9xe:|WVI̸eVHky%֟eA-li Al8-n؄~z㭲cEʈXMPL<2*RP#,55J&TCbohoׯvt U b=%:JRl (Cc bJ'.4 !8=oKclڷz\²s 1HTS!C=09@M5Dc Ko2[|6wJ"Crԛצ(?@w_{9mۧqYނ* Gʶ.2Jg}hPPL6(́/U'R1Lw˷;!>akWdV'PHi(=j%Q9DyNQ oA 8wmn :mQanq@Vx&"'DIG ȧ)Ԋuw(շΚ%9+),ԂJ C [6 Vb+03DDqA3Pl-|zHJmͥ ^&.uow^f P߻~}zl S!밆;奆"P>OmT1M,;#I{mT{Þ](Sx?/p/t ǿo`=DG}ZZ*|=G{Zayuk0ũFm!R/{;wO*cxO53 >p{teHWg%+U?"Q=w](D<75!yxa+woۿxxtקVjSH!AVAO$k߯Ls97~=k2rz=t'>zTTM^ܮH班;~Ǐ}xàJ :}rtfM./қi6k]-~^xQ(_laGO>w]}(^z<<59@~~LqGnvۻǿYF)1c6~{"`u{t(1 v?{o|[$ ߸o@=]aH3uGpExp۫V!`@mϖ́ۿ{NRÿ}/#Hq߯?=߿cXz7(w{H뤟\i -?R/ $@xC*1M"[Co"mGG!㤊mw8swsum׻2QϢ?f"Zuq7Sh7Nd^=:[T́DD9ۯI6Scx}mJTtwPVܭ7{n{5C|w_PvY'7߯A/~H|h2o=~#SѽONV #RU-퀟dGmvH |FFR|AaSU_lnjZv܌C3l`X;Ph8Hd v7&A/xl#&އ#-pHf o "v CʤEX r"0yUH5Lˤݤp;PMWMS72&@5xjIRcNZ:M2LBym>W\8nu#.Gy]>Bd-H. QIpM۪`5Zn( !DN<͸PxLc d*\t!ݠčSrXD+OQDi1ī ^bfg6|[75kX'B2~Nj~rW(<P-2)/R>UEENFrqa \Ě)FDKC5BHvh&CWA6+B( N8icz4&~3j1l.Si(IGsȢ )/&gqLȋ3B퍺RX C3NSپHn Yr2Yb79Ci1`Ƈ:Kl8im 1vS}}L$͖sNdTЇ iuV[;B֯z2sٚ\Uǖ(k"jq,&d6@(;LtleW(\g; F1nMJ7u=VIђYIc#@-1? *De59, FEosBIU$W)"564dee K4}k2>olX68biZͪQvfNRdO:,iF]g{*ٙoR*\Oq}dYɔd~sRPZNhcdaJq濣 .`3@+W K8VTDnqLLK)p?lv'R=fbQY IEҏ*ddRJ*]z:53.C<i˨>K)5N)O,/qVkp_e\VSe(d-bVkRW\I% 8RZHoKmY8A9c8U6Ih*dLujôWdn^gz 򭍜d<928F96k4QFʑ7+rvU%Nu8{Faҭ>Iov$Oc$WWbȚ8^᳐<20^3x4;+'rI;;(H9k>lEL۵Ew0zv$֠f̿7l":v FBҴ0tea+$4ׅ?ї'+h j3JȖTED!襤Uk)j'>lӐ-dmv,U+H-L3f!$ѡVA(*dnWh a9{ͽ&Cौ)$`JAv"nZivްʤP9~=XW)rQ E>I^G\, ZCi%3yC6LTQ0*g"ETp"EUߠ&t@~wqlcK֤nQfFGa`ꪦfcc9z}zP0(D9*nT2PU!֩Rj ȧ)3& n!Y~_ cRޅ$+IAnVL3g qVQÔ^\h]\ !%kn|];OWRtUR+Ktڼٖ+0Rq3rՍ]&~{5w2Mם(@c ;f9·vʎؑa&Nh{/SV)MFa~;Mmtd:SX %Dfԟsԅ4m2 X[eQY.b_X=ZqRkK#kJ"NG㥑%Aii )JBx$)hS`)$n sR"B]mn0[GJ4Kڲ\kƠk=qeLQ,R &y{O)Vњ8ZSM>""BnV.u8xZd.-F`Y<{+E{56 '7*vziEcX<:nV='sFIy+4g<4iys {\\[-q`F/)d긁mbbbXS%E%}>J`ǶZt] 9m+J'L@bb?] 6~mzw5p0nut6u:Mp!\Pk%k |.g݇|' huxr9sjsǸ{X~]=]f|<~71zxSU>^SͶB7>V-/<&Hk[~t; Q}[#{cwC}|5}G.e>Hkr _o~=k4ie_3KJbboo;Z+Z|;Gn"eP;s7w.}(ol 'o-pnV #7ۻ4jrBl.Sl||W<\Znݺ{$!Ul =O_xmgLpmϒm>`?F5Q2ܪn"&(}^SZaa, >``ݷsNEA oH`e7>קwnZNY p66鷏KOs*w]-M(z5_k^v=qۛs;ut։#):~s(@;if<l~Ht$myr;=Aq[yI&7Xrp8wL;_Ş~bnw|w}Ynx'*^f7O1b?% ,߱MDR}#}kQ4٨_~]<`4jҌ |R??Vr' eY8r<$4ۘ#=MDIR(N_$[eMx%/7|H "CnĻDJ;: XjVj|צkrfhr'#Ƽ3UEH7rTPs&p/!XflN$i)dU{:|)$$.dtQWQM^d*vaTuE{$n V .٤LL5R09RPAJ;z=:s8 iؿ*FC? :B6vt^jҬ"$܋;rlHWLPb6%1D:!A nKё!lC~5@y76R3n!St}W\Ip^o0`̀{0`Ya_ ?9TXR`Bz>_BOѨ ؘ4hF0`4VW TR`jdCw*C(YZGEX!1Lui/-$pEPSq*"v0UEC":2fE~*<ڒ<668aٷ}%7"[uߘ,I4Or%s5H/HE 4]ۮ]- 6BV1"Gti7ȐʥR1 0Jz,Vj" u>ҙ]u߰$^Sy".kUxBCx[37pϐA1zM#%ۤdZLJwE19dPĖ_urƳy1y 4d1̶mHИ!5Quw,ЮB p'5ҷxDw5wREONC݄&%e Yaj#!QzƼ4G.LN} &N^g3Ķ` 67sAV6["F7E2IUch̓C`y{(q+[̚V3(ǔ$cڭ'Gq=I-ֵng/1 WJV G"0}q<9N۫-Qy ^ ;_^j2gy8Snҥ?ſ 7dT ]L2oD&]#ۓ/񍗅8+⫕9K%^>y+Zߴȧ5Py+,KLh%4Q삣GKm(m )NVRB|F8nmnnpU `@6r 0вYO_)[=c72 ݴt׳URҺ8fnr|Kfe\HLafױpm0q2l"=G٫"!VD֬%:ÿXiS3BehYFLy-- ՕJ*ejv]*f-b]-tjI遀YWDP `jv첤5&*R 7}BSiڊ%I)Ze8NdisS >[8P)%7xw֠`cr{w5I`܂y{~SRVx\/62KHw<>W~of+|`_7wI{7.!=?v. PjN Bu*Zp#,'sEZٙ3KMS[Z#P7vZYhg#o n Λj1۝}H ,Rs7Qf[f۬zF٩Ϋ_Ei5&U73uNWMT1*7z#ӖZZ+lLjѺ{-ellzo\;H)Z&-jʐ.-8=emᨤܧR %r8}nvzuZHmo#=\^`";lH^bQ( ;ep8q샶G-!h_b/ץVp>mw^r9Tv@o:N1m9oӇd*;wӿq}uægN[ۑ.RRVF7ۧ/o4XTQ0{;=yKlX۠z{붣&zrpKe ӂ\W9"@~N(Gv뿇P|Br>ڈR}c.^V=Aۘ_\eHJ\x } >ޞ;<E^d{vߤ.a:v0xyuZW/zroN~a~3M\؃o,>$#ag66~9mncӿ` c=vkzGyu\?GѾ5IE ԛW^xi6<gϩ?r w^߿߰|hRL1#Qa_\6k#P86~xl=zyo{ U.v@<gצ0-IS|C{@:AouhAzg@+n =D~Q֟x*8HW"Tǟ^iwTEqDyV6;t}MFpᵱwZI7Ԃw1ڮ'TVWn}ÿYns]7 uþK1ta]-|BwyKJMv;[`w !I䕛unNnCI+8uv^>WӝڅA7@7:}K42ӥvk{;m8j*2҄-J{ۙ=os|+/Dm߭Me@w=~L\D(v7.:Ӣ (C6$@KfǞn71A#HSe@ZO<ۮm˷pn;yZRy|Fc6uOŇOS(uPn:=zxjߟc8a7Xae3iZdyWZQ&mQIU;:HѺlILgTmhѣ^bןץ ~dqݩ[[dS"Ԭڬ#GtKc=%3eH`֎Us#()U ,,(iYmzr$ F[5,],E u FމNtJVŊDT\C4:i>a*bEMbwRi{:лnN \V{g&T8@dl U Cbs "Xw|%ysz}#5 >qNE>U*h1~` >(WɸA_^ CEy2H _FG|²x+T)TIT*T!b1 Qn4_e.X5F)M_BOѨ ؘ4hF0`ѣF?Jz<_l!Z]JR)`vULrr rjX?YP dX#Eȿt robj\Nu:ۖ945᢯[sbڻj/{n$1Yo>WE]YEbDPY`0gK@`(ӏ%c9c\XW](nYDlH+W, VЌܰUy#YÐȱMu#uS!uRM)mn3VjBiWFܮu+P6-kF}+j%XQQIgTЉ)rE ׎iyJXfXC,$_+=KY<q<3e:#F]N+N߲[cW-NH¯~e6+d-ڪ՛:.CBAH9V#ܳ -XiP왿c;{e]eȹƣ4hg Akw"89imWw#Uȳ7:+bp6r*cܱO61gp!I,%ĒSoq7 ZVE %A 8HJvl@6Vg+Bn[p>2!\)..p=n35Vue նHa,LKk%m-iORŸ&^s$>vyb?$d'T{ݾH =w J^J\퓵qFۄ2E2Y8WIJ\ث 9w"TQoxHR~TGjTEY# ^>hY.mG2tCo,Xv}ZG{Cpp\DZT7ۧSclp>ԦJօ(]ZCN\jW4 Zm#n[Ow5 ɒMZe:ǏSpe1]8[ڲS |[q'M 4ladr4+ўAY%zq(7)pl~7P_\6"c6-hd\tA*5lf*t; |i7biZPOUm cz20ujؤ-ViDs+b\&2ϜH;T;rΒn~ [QtP|F(pT_H5+(q40BTʉ^Mb@y`!ui~=_^þw Z*{߇[,k}`?2]Ǘ r~W?7R#VV0 sة)CI}VH7ۘhuyy{;߿v$%~22vЊz.Eɐ &c},JQ"_DCwo:; SyQTޗ\l9.LHeiSmRG X=2Y>5m͆ؕϸN6SpZ祰j=}DULT[CC`(Pz4qcC٬.9Բ;bk #suۛpêzv)'rw]vdS{ R"rT޺`J@Av#G7.qR!C~7\^]5Bl&>{oҳťT )Q709M_(xP쌙F/JJfhC&US2ՉL PɳFuCEfQ0 DݷP12u(wҫ!pjSO[F (P!;=%jv.֧5`x]CVywgQB=(=<;9-({! 1ĂA!-qn$H -$0( 钸~̫ʲfni;dr ۬*$U38'<29jBut~Q,> @YHK$\m۷5=/4Q+LRAK >wJPgF*m1$r P!÷~ ߻E.@T7/h>ۿٷxD\2/a@Dvo1 j8r~l\>~wۧ^h%Z\u()W-i;7?5ᖑ'jQvp cmm禳jIZ662~xe>W8uco[t뿴@79 g[o7۸|D;:Y/)qM(3F p|S*-s#P)OWz={xq67߯0>CۿvOYcmAݷw?@yWst]a߶}&mhJy!H#˟[ܨ|.8 :CnNgFP[76|mA[~yO 7XU0;twۻ_V:]haMi\:s{97dWҡI Jv㚯kg BNmCǻ}ߧo|-IqߜC>}zJPxw뼗,m =@M=7?Ф+Oёo#n?cR }Rrz;XO0*7~/܎%}YD~7l> B;÷vAOWt (7~]A:XDQ}"u\s a~Ӹ}÷/T7C?n7-EX)B{mwﶮ116Ox8.* b ?l;l!mQg}=cYןE4)-4):n #ر5v. 1H߸Ft".;QEJs%6 'l+E:P2eQ7ãX8ڽ5^im+ZJNc2\cX>ARw(Pʭ 'f<9Tp2H@Xy[6Q övͪbq~v*oJ17jb=NRK1A 0;^uT]QAO3BA&(t!P]EiR%QDx{MRC\\iIW;|qǞiR96rK2$@!=UPH*n\#ND ;@ ϊٞW!xϐZth ;,Ю rcE?m4GTZ<P5U?^əRf;MU6ZA\zq -$ZVʔ4e6HN+V|6DP:!-1hQqqh- c%]e(#;^` {@OSm>}?ê?H3W?NO3lb J%6(\qo5 EL@9ma7(r6w^[fx _1ŖmHFRIH#則lHIRC)K7c)J6W1L٨@t=ӿHA.jB%!&1 ";wpYj6<ې* ^Q0wde)USH )opkF\qXE[`$_B\EB acVa.>䆛S6J'{\XW6a㡚.DqADzn==j=,2JKէ2#!(Ɉ:` Q0|c3w1n<1X:xs]dE }Q8uaw;wɲN\HËBѡu$CpAmo.3܇SRP9! u%"/6}!֓}(}=-͡0RVx;Y4C3)*e{)S%&5pmǼBc<1YLPBB6A˲J d6Q7 -x'Q"J& ~`n!onwdVX/+\l>ޅsnWO`#_X.:Auʹ$bI1Mbe~"0UpUDCN!&4RЏWi/iRyG2?>A~ǘQNVKvqwI+!c\JF~U<^3/Hmbİÿ^|~Yua_Mz Dq%̽^|^GR 埦Xfq?ړaBz>_BOѨ 4hF0`ѣF{uYH%o-Xlx; Ǩej3~1U4Į?mۦ@fM1N=9 h_ ӌwfS*xe$|e w#N:lf>M_rʒS ^7􄛏67vئFk.̴ʥ U$xUT1tԂӄ^Xqˈ ddb^vFl0HlX=]CpbVx"70;Ռ3S:^FjE7$onԦ>j 1JHہ-!uV:arOHȱi`44ME솉8cJ8+ c{-RȐ5_v¹cqLLo(FX%8ha- >cɶS5@M͈6qKSj J2H+ dNHVuVTQw<7u7o(1AY bqWcV8;@a%tswUi5Rq\V;9W2PsT-HbʩEavnӦ4훪9v)+~8Ƣ ad+%g{SuTL1 G ORj(W!.1]VdXf>-nb#rvÕ8>F^G`&|Nlu̍dv ^]"HP[Sa>j6>Pd{{m)#.bqݖ꘮TYnt.@,M$ZC`sѧ`Z*!ȫm e۫"e,cI0zDv6ӻo|f,&rMr@VRM87E["moќkR"m$D@)H]1y?{vX0i\}wk]4-d|-pS;EҐM#]#ӯwA;[R=Cq/=jmg/Hj]+b<߆߭i@~<b=G־A!n(/n:ͧGsn{a]ZZ}`a߷28勣VFJXy %Yd\ڐLfE` 'XIXu"J.mk_RmrE6]e)ԄNw-s=[\dyNcyp1zti) kU>6EZ?$*)O\٢<&(u8\Yh׶9%n樳N`nssν^&HEEDF7Q0_G|BWfDrD*!HufVT',\yc(jgK t?*MJME&:IY sENp),.WmqS ~=f0|u=ø8*jvb8E]mAdLRbWL ʪV &xqP+TO;^-!aq-Š:mۤ묲EUSCKEYs_YgzI<ӧ T2Qf8:bz܁mbl&*e K@ʪ9p<`svѣF?hѣF A|nu| %@p^dP-YnxK@MdPN"ZVngHzQuɽx7n:=9~UM+*HR1R},ar7[HbbPM5Tfi&IP1`@*DbKZ.3#Zj*FZRɍP@Z2y)-'GD)9$3iSsY͔ζO>-dJ8)W7 #kp ̭EZ"dJV}W>C-=NX$$[֭StYCydPWi-a*mWʬV~`tsnf4Ï $QGpo \^|^ҏWGBGzzDGpĸ{1s|ibF9g_aK?qu/!_cE+l}ѣF4hѣ 4hXxQ/*9`#(GEb2g0v dk]*JGUmClP2pj5`FWb'”ԛ(6p]6̈CpDZ*>z*!p4d8DcQSF'+OXzewX|kRsu5 T4M!XQfݠ;"Vq!lW) T)A/ ͵nO \+L"1A!)T̛wXh ;39ZA&$WRygOd;xBId$*pN9XU˗.Ue*c**EIjB.PkőkR[ZA? nQ ]Z:]ٮ嬃bswO?Oޞ<$_{ew;uH@k??hߦ>kO'R^$/]P/(9w1>}8wQk.;[{d|`bn孿??SPL6ߠu=>.ɧ(>קI)$J]m#, ;{;=Wc1m}ōX[{ߠ11ha̜'s }eR=,G>=t s%8#ާw׿C]BPyl9n6%>׵Z9t/ox}ȥ6uܦot~0aԽ6}^?"*o#;؜=нm7x>iUK8Ím??/Ӷ7:  GmݦҥWm`߿H/r;cmvXͦĨ(؝of)+WԷ/-BuD7^u/rsvzNj[lASqvDvCmҫ?*N6ZEcxiMMZ%(ȚdF6g r" a8uHeB7pm x)Ē(@$'yue2;ϼzk]yH m~t7_R?ÿVxro =;4ǍXp\ %{t UU0  %~QYN: ;aHXsDs\mĝv:Bz*'LH0u-&ŘxHrBǕ7be5EcoymZ:\4UcD@(cJRq1~ICqJ!,'=%mCplQ_g[QEQ+ "M䬈(gN@ C0?/ GqdǑV]I|Ʃo[q];eihʔBS7dܜsJR)J(lwڂ3F`6[7i7 ؏-:y_ ÒTΕ?jFnJdctfCY*eT":@ ˣՙlH)\UY9L\M.x|v/b )4Y6)"*ii9wBR̛kzAW̓<|Pug13vR~@* K:G@Ki)H*za(ѭi()OSoa ?hѣHƁx0sG1sog#ΤuiFC{˸~8ԓ?iԬ6kr*Xp>`p}}m6 4h5 ?&g V?ĔnI?Atn0f ;bq0.۲@g |fGٷeBřr 9/EvrW+,(Ƴ]B. py;]\_ 챸I ZQqmx)&dtqf[X.MܝcGvJŒU\3A'U*)pwC_{٫/MȐoIfw7݈S/X&x c?'l=s^I弑 %-W3U y*eI[Ld3#IRl#nᥘQ)aHqfH}mוˊJ}-/KdZK Om5t$yJAW/sQcICW"o7Q)9Uȉf"`07 ǯ_Aq7nRU# kIAT7ؠ^||AjS  ^P{Cg{@9βbTǧij4tjM]]y帧RĦQ\#EַN)]ۓl;sE`D4PA&;`PQjʎ33q%E*Ce'.OT!8 G *,TCM^^`x꠼a1T*,"τrD}Z)eٱT6+7֍eb 5*$R/'$dL^ʯ?Tw vUa]2MBߎN6 d䧠th4T6X)@{(U>}-`4lAkJcov 4k 4hу4`0]IDu79įkϋ[\?B_&g?LT!7qH_hЅޟ폣E{j=&3F0`ѣF4hѣ 4hCzMZ[D^啍/D,)C}D6eQY'+#Qg2vjkSLXz60K(EH$@G^4@}2e6\*/B.h0xD8YuF*;1)4 W6'ѫ$%Ө5F0*rn&ɐ^mFH*a2)~VL8ΖI;eT`7u9`ثd Dm5uٻwƘ:I"qh/)@;M2wx/5wmli4֩q#Fa:Za*)ߩ,pyWV,r?f%專y^ʠNMm>^c,:_w ?O{jl_ZpVW ߟMA۔}r v_?=e/H -o9 eY:>{:þwpm vW99y\40o E ߸toi"&1W"LWD(wLpGNz 9PRQmAّФ= Dž]3 ' ~|-ςU #ݶ|:.ui!O KD80wwWD8yu(uH qh}0myr_z;xx8(:ۭ7Ow^5"0o_$_v/wev_8 Ƀ큰 Uda2]0ݎzӕY›þ;tm_UhS>?vQyko񵾯RYw?k$)Ϡ:~l=`1Dpg g=GoQ~mѾ>(?tm~Gź w?m]#,EEܯys}C !-+Q^ֶkkmA&ۛ~w|ԧKF/Z~ayy3)bydKf0k^QQ!ZQb$&@8 t+̋۶<ɶ-=` CP[y˧",7ӿm:[sE`%XIe?\b&.CWVAE9rCsIvH(9PgsrwnD[L̽>W\J.F?X5r"֤#b@!!I6X}+ $vUNćVXcfPGL9La7(KS1FJD}k498u֚") & 747,W7f!sǥZP!VVR2{q_GA'VlWKU M+֦+rRůOKE֘u]fy!m~"b:-}, n$숪gTxRULې뎦J mKMa|QKg(8cg;원+Q 6H)D:! d9e}ej/" Gu]tSK>Q ]7H)(S`ȪQ)GaC˘+!@DD60y>?sqf:JG2T,qU mh/! i`2pY/a I!JPIFhB #V/ݢ ̙01:`>f6K?4(թ$z?4(թ$z]mgJ~doщX1\%Ƭ`"gW.XnjĎSM$"N+ t"j!D=ڒ=7llVSbehѨ|4hуj_d뛞JR.c$MTAI|>'|N 4[KhJ x9΃\srA> !:!gM|^?7NӑW|VXnܩ]-Pk夗 {SEE?m" y]'ʧU9 D Ϻ!ߡݍ(B֤RʔjQRG; c +;)Un2XpI2;#*a!œUеd 8NwG Ty!%'o=pddV)F*^LAAq TN*,`3lT̺(Sd%)J.dT7fS&#FI[cS옇:}}CT_=vWӤJ3/-q(K^lt-kUŝiÉ„w#tsZ;(Rn=$)h&R$iK@9zoH[v5n|=Op᭭h {wo_GQQY67**êoc~CЃaorHҤNR!{yw^;kcmUrm|m.k/gA߬q% m>~?f_sk;Q}>^{DJ˥[焜l%̥xB'NtP/GhwͥUH FA!OnM+*sVn2nPPg=m}:H"$xtߦ6)M_o=VdR!!uQL]mS}RmpͶ-kFѢ>;o79 8m6hw_l|0j:WquxYB-xUbb }in6䳖]TW|.*.=#:|Edś6ɀӧJt(4 ky5&5RBHbլĔ).cQ/L8BM ϓ;,GoױHoХR+?/&XQ$4C!,g Wlu5!Yf;RJaj!ԓ:B5[rn.TrPʋJR !VP:7Z굚ci 1Am%aYlZJw :)LTt,IahO?ǭJrHnAGiG{{|-tƶ/BlA^Q䅒$%^n'\Yw:vÀ9W œXI!%coIW,$@؁zӰ4ͨ9fHl̛UܖXb[s͋nQ3MN)W\nԏy)_kbi}o>x0qyANDlxWcQ%6IԪq,U1$ô6\(AoXԲ^ΙN??h:| cJU~WZ׃Y_L蹿Qe׿XԲ^S@Ch=XRՏ?e+1K%$K%$)Տ?e+=XRу }7|LBHw\m)s_r3P96Jrr/Eܦpwxm3;p0*s&11s++cӿ?8lVSbdhѨ|4hуa~o|>B3ɊдL5g(VW燳c̫zKSreuQpnX,eHDϰUV$(v:+ rCnf:&=d{Dc ɛdCqlh!Ku&Ca4rQeASm!. (J@}V9.MYӋy1aMYu̗q{] AF6X89l , ]E?ƱnP"RAŁ=6(6W$l6/gQ̙/uOpRA6KF tvEe!(mk gy 4h֌(`ѣF4hѣ 4hF0azD_q/eV4׿cG^~(\Kks|1wgR ;?ixAG#{YBz>~CޟѨ ؘ4hF0`ѣF5x{W/^.VYӧ.*(7l3,*q&)ꪡd1 R볨""~7RѵPHcTۨ`@.-}&bp4g}:avlISCI {m;n8BWO<=2kHfu\$8y5Π\iExHy&(sTR%"J{buLyCn07"(qCqy밒"& =;6oOR驇v[ XlN)r|!h˔5`C`s7ͪ,LђT y7oonvbheZT"Vs+yK$w%ФE"upJRRZNW-HVGy[&r9[a/sAm>Pk쫂]H?'n]`rB۔?|3n ˷^RcF}8jˆٱ6 8^'hmlRE=$sm|G|r#P<<+`dygiΐ}o~㿇mj×n!w_]m//M;}<IHU9L^{6cU|/U׹Ia ?f5I T2)@6>>ZnB [r}vxۭk*EWo0 G}~x%b{+sFgd :{ X@SV|H@>Q`TH+47R"ֵ{9|i)R*[ahy$ FtWb2?9J$%/v-b b*5{H(PlQ#vqё&mPL ٺi !uM:*# jq,>ڎby oX ݿ:qGoD>nWSpgo>ZPef*9x49"pz<#^թlylZUO.@z{ջ٭qa`Āf *(#"< 0v=v CsCD K1DGIX 6#ii*ʾMIxʶpUe5E]K$DvI'z řo5}$Ez&+i6*k[(a)%Q1Hb B1L8+l(*W`!7 &.Ԁo}%).ᕲud D>Z'AǷTȳMeK9űnͅl,tyFMLVm/[wt@@1ƪ7(X"!jm'CpԱ_r-Pi E~F a®Db_our6],D "nM}5;<Lˤ~ӡL@ aA?TnqzyӔ2 -EC+42ѪZF},`9su=T PC9%stWJw;0ITW HL&̭BP$tG##&qLƩw8y^02srstb*o?OLuc sw=76HsrQ]=X+$WJ6MdY4UL\#خa\s&o-:ΎbNjw2kp1'zTlVzTmUZ=tRBz}KO"{[^kUͳ//SYLc**$TiQG}&c<#:l)-U"! CkN c,֛8fC~<6Y@Kkb5L{ZNGt2C]X9_OghV?OZ/ՏzO3n]1+B8=p+XDmGwþI\%jUNVͷ5vм]-Y%kpͫiQ84;jU,a NAb<`^n(=:`M]cVnpu2%AqDSdEu< .Fum6؋oz濲&4WO$ٴ"PB̛!TyOy9ʞxϲˁE0nb˻N (_ނؙyDC;r 28G^Ԩݖ<^үXI̍y}xOӇ0X1N?%SlL)JNrni+ r{}t󱗠[K!\_7\[kSΑrw7 uK ;,|8( cW1&U=NvRa:ާdf9́GNkri}V?ӕ//!9;GOP9ؤ?ITA}_Z@BSHkgqoDTk\nr.0hG`͙$:'oƥ\& Y+qDƦI-=fbG ݿp-__5MI4̒Dv{JMob2+~]lQ*S\3<'i9=ؤIT1CLxb%S@W  7Te2B'X9ԊdY8F2#nn:Ȩt9C;2#9R=ldlN+֔,]crHE8BK7#sVaL)JG~S4_^CN3G=d?_OUz?{Ǐ|)RU? Pb,SMLQ3rzJ 2>ZJA|s~)&TlfnԫBTJ}ҥZ5y|B5Jmax Se`SELȼIrSMbJOO=ߣ{ |c|zϕ//!?X9!撫sߦC%WWBz Cgx8ԲT5}eWwV>=ZYJxoY V# pqঈ:RHn=:z@' jVI6#yG|1Y~*my5~_M?]6|2i*=cǾ{)*(t^]>?6_/ d3\[Uvo/{(Uoa?IUU}Ϧ<ñO^*ӻ%r_\?O&G^?UſY'kF(Uo}}+߰ǍJUۛ3Nn/3ڽl,\/$舻nrAa".ZJ!K7SdJ!8(Kpmt+~ɹIa7F8QѣF4hѣ 4h^ce-Xs*҆{xFcREP;jskVFK䁻xrQ]~`o'R`\ïE*3[3gW5JIj+mDc~Q&P)rd" x񓮏dK}jX26;lٷcȾry~Z"{5mFEEhBh6'bR<_V 6Z JVxƚTPۿ2ɦḔzӿqwm4E|e#W:!K nFfzSk_y42:yӿV8oG8\8pEy8bf D4m5"D"%p}JLZg)#[ٴ@v*Ŷxn܎m[@fbMDHDB1#YWLB=ǃ,xOd6Ul}[k$ͬycL &7m3epbDs9V6J UvCt&\mȮpʺ%: ?d3m] q**hui[MC"ĸHQ6 ;'qb0$ۼ]S`2}:r~ݰvݦ.EG"Sn=?? ѶCQmPi b!o.# ;5WW#lYJ67'[ >ոPQq.7ܶ<^ߦ8w;uۇOgxJje/vt{};￘`-sYS}ۨw{7 z`˗` >?7ӁHaB{r:>wp- ;rXώ/ ~k;oçy4Dw{||5ƴC{Bw>gx3^GRy{:4_k[,nneD;>Ξu o||<OoMk/& ?l};٭"Rx9L<;wȖ <~/3R ۿ˻}#{U~7лm:y0LG|~˿g]N"JE0&IE8I$"u1TS ~P:QL ﵉ko;R:t>^]:x3 b VqF4ubܥShA,ki۶~cӷ"(s Z똲)Hjf@.OQL%uEDqir:*=\Ý\˔#)6Q-Ҥ~΢,9k5&I !JBe>lAE.8}Sة(RVڈF>@W`ѣFxߏŸpov}0ڛNAO7MAvM0I<οh1G^4k3tr.$n"nl1 - ((u1[$.Tg" CY#X'[tΥ*2ݳď&wڦ"l;%?"l~'ݨk[ϟuYQdU|y lQ3Kl}GǿD1Z`Vדf$$k짻ms&m6Xf\Ӧf_AB9\c8XvNܺpx`(u!EL!]~,ǖ(c::L5+xv͂":1Itùɓ:Ew;NjWY㈢WtʴW,d%PwR(%>F+4'U*}|r2ܫ;2o1?d4=%.wc`=T"[WdKĸIda\k4-cje\ˬr(r5YRbʼ1LWt}f\ "k]Ƌv[1V'b+{S풵#''1 fs(D(2v !3OW 9!]DNpU4$x{-̢Dd! 졥eq#'<\nW%[U]O[YCEzhу/ú5.3{wF|=wW> ~4~@ wthQ`!ޏ[YCwhy"Q/7QG{֟/G|"cF2) !=*{=th M4`FdR< q$ S*ٱRaFdP~ DD}J*qd\1hC mהwwP;2}Mb]~Q(* z0bɔ xThpv'7nCoٚ" ihN[u>۸M`a^u=Јo+$LLv᳕P,7|t␰tv?J{{|DǻZN֞ݿrY:MI>$|xXېǸa6+Ca^sۖUFh-QF[y]Y[;,\FF*ɦ.SU le=*VsM"^,sG+:ԝv0%F̴JfR"L-Ub`*pNe Pm h̞D)27Kz>|N&(l9wn0BIZ8)Ack"r+K gmTY]>-R:r(D5Zn{+dǫHtz?=ݺtO??,h>~dΜ&mOXzb1K.pQD jJЕa]C 퉞X2AݤuiES6_g#"֬D*,I5XUQrO%S!h9QVi&(j4( ="[$<"IuH^VG~ x!Lj]GTi{=^"zN:7ftAIWs3I!9u)Us[w =SD(>b=n=OU @X|~.>6j߸^.ӧń[o\ZCktvx FcCC75ޝo!."w|:x_/ ;׿['z܉ܡa_/!>`YC&< vt7>o*(2-S] mLn<<|4r;ph P2 mZ5ѣ 4hF0`ѣFjq60ìSb-6bU9V_b1O اzTk$tMRb C ={m}`e1ĢhG=+ 7jV*]Mw7bV:NI- )`0)nB6{tY(ʋ 'MrX޻vLUN!Ƶ^ܒ%>[n|.q~j5Har4p<(!!lI.\{r]| }}&6U3n0l ٿN_]7ɌDܛm;~6nD:oQm{ 7MWo|X:a.4+mc.G+vU}P`t ~7oyopH\.l"=NZˡ:Pe>{o˦DvSmH>|/xQeoGܬp7}wi04(oڔ~okpvw>aӧ}>8o|>p~oק^@/o^?v8]JX >n72.-`$tI眐L@b#TL_{!uzZICTpz14KTFEEǠVزhJ-D!R@&H<%:m$nM[㲔ӓe< YBIbwP-ZX?pmF<0F1S oF_ݣSiȟ ݿ:0a:[7H_" |/0 X+!%5۩[b.$;e#$%# 䲇%.feN3ԂE.&ȗG-e݊)alx+""vLaXx;վ2rw]-L<¯.ɛi#t9&Kv.Z<`l]Zz2s\8oE&2J-24ݠfnXQWV1HIWq"YZwEÆGJ/t]Ҿ&A#qȨ}٦|M5{ jrBFIV0ՕTZHMvKuW&kL1 *QFFv%c,z8w`4F ;ä(LQҢ-b]v[+z\,)HYIM H63xrj r:ȃ;.KOTU.LdEm F.` ZdϨ Ӗ7L!M:i__|JzcQ^rԡ$ge|+]ARzܓhL:NUUF*A΋?,ۚ=E6pڶ eTK&K3wHjm^r-͒n9H6.5op ٢WV#9]"+ju:E7P5l͘{};D4/4Y0+c1)vc>&سMɀ4V:*8i+E,N2 K08uzTQ" װ%mF6mhJǥ$sYپQvzً$"ͪJ_?Ql3O-i}p} OTǩr#e*uZ@qpp>&RH})?o_ǎ#]'zGǎ#]'zIôI#?|Li8t 8t 0>&RH})?o_ǎ#GE-A>A3V1y #@ܻD"(l}ĎEQRqCb*H&G[նTkUΐ}+ыԡ\7d]89䒣Q 'N=HP3'`B qLH!*mQ怜͋^ARDG T?eNґpqJ̝\Eۧg,hi n·Lal[Jds-֟Ƿ9*1ʧn]0n^صjnH `)P=F CgO!dF!p4^=$P~=DWQу8q-]޷ʇ?2Z>Y=٭jw[M~*w(rYJ^Ȼ :r2GHlS7]kwmٮ0J,T5 w$:czǁ1hh(˹4@h!➼tꕋD$Yf3O1sÿvƆEO"-]BG r4aEbW.eܝH-Gc ` @Ohl& ZىDZeʳE$@ȧG=W+~6$IƬYIӊF[i=)hqJK;`JVqr>,*9.ŝIpVFTHade]h' Hhl$|K0~6&QsZI eI >C)D)̬NH8;Y(ɉ36 [1Q蠛XtUlu̡d&~Dʺ " T$-DU@'_+Rg4ćuB٥2nI\4ߒU Xgh$5& ʎ%R/m;kߜVn8h\ =0qI?2cq?l1.*ca?OmkG{c##޿qH{oIкQ?j~0F0hѣF 4hу4`F0k)) ^Yh踶.䤤,FG@nF25 ɒh nq-kc8{)IQyWUq1B2dlE.jP# tQ `4017|wQ%^S&'}~ݶ?4iȑL!=u=uy- :f# tKMs梳u8*B;NmR \f&&#w)?1z~|:DNl@8w_~ç Hq0mpw5SsiU،oq67幎;E~M3k䶜2 Hk{Xo 5@T@.m6 9`Tq 1<7jJ {:Ҩ6Q*{ o~|wn;*ܯ݊ y:nmwmvR8(v=D<7ݮp,:;Ͱ=o ݇lltۧӯ،!oݰOQ鰏v%-}`:yǮ,="~|Cw#Ѵ('P.0 l vpi8|v=wmlĚ6#~RE_opo5`^~ߡ{&l@(tJ B?|~jR&S!=6ukõܭә?`;߷t;l*iEP~g8i'b7IfiK!DO A$eEy@G2"$*mJ fykp7VYӷU~ϳ4&DH([YǎEƵ{(5/ׂ|=HZdc%/5Kޭn$tª o (ֿ Sx(ZE(/̠[C29UkE=}+غokmH agc-? 8dE5$eOM9L̂HpH7"(ѥ^?hKiJ!)JE`ml4hьhѣF ~Oxtnфp~877[aF pDB=oqml[?N׿yߒj5?>AWۤH+CJ;IE55v&49AgnΜP1@MTMV|gb?ԂMRZWnHdF.ÕQFIQF)'Jmԭ>lI?v 8̯|ab%V,DüA%QI}MrȢq_If.l|kVekw0hPUNucW;`QDg6,JxvJv %ü0Tl Tg>4RC62IUP-;8Ja E5^릒k<[XVrDz/ȒB)2 Z/6VkV-#@_ȸB9YtyصffOA^z,&\SVe*n֮x`i%M)yktmdVzC֎Ѫq{ mVBHlu!9" ΚO_2OW2¸Qk0WV)ɲ~KVdk,j)>Z 5Qݞ.r:˱fLyRPf-􀇞תȻ4+Y@c~4u!.RֆVQF _ZtzN29'q~=Hg`(c99L(v,b#<ո$ C*6gYaWLB;C)P7[rCtn??ApyyINۜ ܭ间B۔if^nnPR7iG틤-N!FU )e7 󁹇n]G?sq..ӵ_t/mާ/i݇?{O^rOⰪaPp@QLb&A.˸tn??A1DSOA$uF)70?h`ܼÿ:j%P:g*?ybt@w 7qD:?V:VjMdΒt(@o!f#Ѹt`XzH}Y']$u]DOQу8/u=Pp'_G'{5No~>J~Gѣ[q娖Ⳉ۾>dxOi|S)I$v ߶z@6t_LyVg)B]寍ͧ=-JZOKc B9 4sXRa!ԩ$ iZmjORD[2vQ~$GZ6_{9Rva*1\ Je׬=˶Jv&s]FRCIrfoGP)sM6"|-KU(g7%"qMw$Ƌf3ufe]FUد*c/*ᴂqҡ_З<{?|&<{ǔ7Go pk_= W-o×Yf.PYؚ8]Dors2"Fr16ܥ|fbǬ]& h\wH8G?êuq>qOIwqU mT}kB?{܏aF06OچF06Oچߌ0hѣF 4hу4`F0hѣF cf6GU'! syۈZlR=˺=NJCbm1E昘U1Q#ZTD0x' xwޮcZ <="@0DCw@' #JJ"? `;lypmns刾Dp<{qЂi ! xo}{7G}&*@ll;%נ=e6w `(DN||~=j@$߼6۠t'_xoߦK+O,ED,|ȱGj3\ P(4[rL]oYɏ(|/?? "'{Ci,FKD㷖~㯋Le7<xIXz~9a]3&ַO|/<(o,%)DQnsEb,M[{pbqͱIH80hK::m#X& 4}g2N|ՌE272 ldJ6&1(NM⨵f2ތFJ!&È{CbdK/nnOFTȫ:DH^1R{f|9 Mcz;0EW{Msp}a啨s@S4 6&ŦM{ cws= bŢtט.}2 ($fjlƣ˄b*l*QdXwi)JRTug$F j,f !$rbz 4ktѣF4hѣ?'Cb?Xmv8 >zq}m_ jm0U>O@z~TC |[O#yߧ̍X[5>I ѬM,K'fjr#7v]$Uyؔt@DڥjAԜV{;I {S+4DSC3$:o.a9UnIM6-cg=0INJ#`g$~A@l&ߞ sŜ pd+KgP@a E$VU$>%p'I-dyI2#6&;Qhߍf.$ksU%oLQ)Th,?,GRJI>^w|U$!1FYhYnalB(+ &kw6̣/ )Ҥ$N%gRVUQ - qQQ JIV'k OՃ<~TBt;V~穮UD"۫82Kn՜QJvF?՜~͉DfcW4"dZIxÄu~5,s !gx\TQpK¹|ɬbӫkX rUeZ.G)dKGW7RPkq _fayaLo2- j+تӉyؕ{YH3#TTI2%q ABzZmhU͹#?iJHu ƍ;l޽CE{J9_ddS@E;w:Z@\MMKc;v-ڞ),Eo~$9]L<ϼ;t?JSE @yJmo cs?w+OǣN<ح?ΞC~Z$9]?w+Oǭ3!t?$9]?w+Oǭ3!t?$9]5P~ wpUlԫ$IDHKBb%at? pF EgO';YD_C/m~H>j\}$sMDgO@=)v;hx*Ow?.]hu? P֏?.oZVfW0hѣ[q 4hF0`ѣFaޒT>\4qkl:Oؘ;XO?+/*ccSZ_,w`Q>d5wԟ 4hу4`F0hѣF 4hу~U䏤A*~f|0U0)2EUؘPf)J"%B;^*6gI.AA;M)<1znI]+Α5t:!5_ ⛥:SZ7o6*-J;uw@V)Q0s}=|r@M?Bwϭ9\ =GM}}.eP;C&P*R_H@b ?ۦu^s\O{;tګ*>@:A0@=yijz$)@b;ow *;[oͩTz=|n97_ Y@<}ṳsmCwpZ(}ק}Ӫp"㿼L~@sD27˧_ua ۠wټ~f&۟~=J ?lx{Gtkaq7.l$HA6;/3PX~o/cLFL?Y{n[\c0G& (ZQȃF**dSp|Pm|[pkDF8:gHS((HQjK<c5vkňS^LY'l,݉}\HZx Qkg&uYd)H1E`6Iw62FJɕ%=ĉGI׹](U/Fߣ+I4Rퟭq(yATLR$J-&nHP$)LGJ@p{4rxw}5Lg]q]V:řR5 )b3) \ZQԵ( X 4hּvѣF4hѣ 4hŸpov}0ڛNAO7MAvM 7Ɉ ÈLXDa Dv-lpۻa7~]=5Tw5?ZMAw1q3T?YYDʳo뭄vb7 $.اUӵ@H9I/n?vwGLV:NFa7q"W:b"V^!0=icgLVQbYd5U霩0ܒ%|Yo-HoɛBhLS2ږnT8 Qp%il_$[PH>Ҷob $}*$[pF)ɵ=ww!?^)Z^%`L.u"'d#E>9#IQb* -ᦫ-],V("IHGa2-ƩI"lQ#:nړm6vƍZ "E3(fn,8Lp^t8bq]JpcnZ"0 w-.TvEc&񟭋PISdOQY7R~.D(謹` F`#$=L!UӞ:]V}j>l3g)'NՔ̚u9:fh%E""gr|9"В 3lɴi|5j,!h4q~ϻvQ;wMHNeJ\./܏ u1|ګ\]QJUnVHwLB~Q$=#XdqvR!^,qWEZML"%dUj)-2fĻY,;Q:+s0CBpyy2sU4hԠ# gB9I;i$IE˸bL=qHSג& -~HE6Mob7 (;KvA?z|LaSsoM~F v.?H'/G1OͿ4|LaSsoM1\ `  8"y:?1LmSLSM|NF3#^>Tp5阪 `+b: `׼@kY>3OR"kYQu`泥-Pp7'CZ>[=ӹo2~5˷;֧y7Uߨ~ѣF 4hу4`F0}%=_Y5Sl1j,A6?e5GQϰz"'B#tOh]du`ѣF4hѣ 4hF0`ѣF5C.cS)KozjUNr^]dPSv#  qP.c cU# _%C vvEtż$b02 .r#Hjgg&YƉ!he>ɶ5<(۸mAܓDoCٿ3~oyt Z.rw9b;3~]|އh`A(]}ϭCpkJhܻ{ǻ[SOCiYNAEEM)'}D o7Fk(/]/pdHRnt_N@%鰆~# 2TuW,֫LJ\T۳d؇T`* *8Y$Hc4 Zt)IQV>xF -BqjCMSR[ۖ8MHM@=^V^+ 7{xxc"ٲܼ0c!qk$k,Ɋn%'t;GGW@f:28EJyQMz*c%'0Tf{WDW"ABŴAdLS&A6MSIfȦ"H!C`(uŤ뛥jI(nw~˾i&Q"_ li IupexjaJNNxW|!bI +5vFr& Ղ4r˰) +7fѹAkVq6 (kY̓ ;Yn1\?Y?hpfHτ^_zhEǯ?WjDZ>1\?Y?h3W>{Q#֏WjDZ0aLEǯ渖tYUT'0 u9ya) "_ߦ+5{G޴`wzH?@}?гj"At{kTGԼJ[:e;_fWE^&oh5#wOd@";!Ψ/mj8hmӻbo֍eg`'oɑö_X4C͘Aèv;8uas%90 ,w6ff$&\E.:5-\;9l"{ASN=؆+8yyy?>G]ѪQ}O8b?_>{ |/S|o|1uyS!<s)ǿ_#//384~߰b5J/WϩCGyS!G^^gpiO珑aś=%AWZ85SlHn??鿘~=nK}~%R[7 Ҹ-&o*#3TeB2%¶:`P(2g&,.7~ d# A -U{j'kZqwkǧN<5 L>]w5`ѣF4hѣ 4hF0`ѣF5V`5ڝ*>vgؠZ6fmұr UM'\.eR]ʤp{FA F#pw#yzWy;l?bW<*Zw;4^ʶ\E:bDa#"lq鶑py_!aS~fc^V* Ng景3?bZ=A7Ld$pG(]n $U3D97WRfxlI?WXvG$&uUOm[M7[hfc% 4EFmk ,\:)`S]CqJpB Zn@pIl[ǚ* pp.Q( DTq>yK(dc+u[L(fdncu('踤j=;f69 wH5 ܝR$7Ra߳qD}LMVXG(u۝"#pD{,%Y$M%{UTb*sp?Ʈd#d5 Qɑ8luTǭ]ļhź͕^8q : 4Tz+&S/ܸnOT8Qw"t .ʦHCmF-QٗH 癱-Ī(0t$.j]$T"WMnFEH6Hmr B8k^|E/K0Ôd`NAGJtoPP`LoQ$0#r>gnmʑԫN-{VUUT[c,B(;$4u~!rT72NH ,RTkK JWLuJ*P ^S-f:]HC M+3A.*]44pv?f(WI&xk7Py꽞>ق'p.P$̑Hv]Pu$2(e𑆚,qTLYm0bq1Ҫ+$̨dc$(D~3&߬Hgv(PP"PjSns1lXZ2rƾwoj]`VGz%YXԲ,oeDnxG$c&39'YIJ%X>%&Ld "gEBj q[7 h8 <;Scc,p22#oC1xIÍo) \]TkXEBȴh.!f60bGi % q"s&3ȴUbP9ZB-2JPh˝bf"s2b<"ͯ,ջvPod$--izwyԌ;#PrYU#wI #1v8G5t|U=d=U3q3\{z LpS_">4&7K?j5}&]Nk(ݳxePEEQESQ$%!D1PQiyuI) 𱵼z~>8(g4Dj].$Zz9FJB(8U2ZT=ՏR (8}M^+,KU1egudbŻ4|C%%MR8]Dݏ`1Z ID0]RAU0ZE"B &;rcA3E_U#(v3PF7n2zeƷg!Sf 4I""BX.qȷHc/Ž(YqP#0ƆFT\#U@"ڎp I b}J k [rv-52O"N1~z$d%Rul䍒tT+.Ig8` UejQ̣dfwbhJ\~c%^N0\HRhS,۴VUdqƪ)&7PpιL]$BO=Isr1#lCv 6nRd+pELKkSSGa0rⲝ)&-v9Z2i O,f")|Z1YT-îR{9WušQ;UT]]\$\`#=H "Homzo[mۦwmqQ.5o pFn(,xլ,D@pQ*Jr$FvVBcI=Θ@""?s#-G)FO=@}}~m6#xh̭o*7@b`:D0 ^^mw0~?LKnQně/yknx1.8uS[0"ta>o2=>d_净0` o2o2=>eWGE0` o2o2=>eWGE0` o2o2=>eWGE0` %i,J !op~|7pZAAJ!ȑ۔a- d"5)~p}@!JB6 ~hу4`F0hѣF 4hу4`F0hѣ_߃6 5`ۯ4`F0hѣF 4hу4`F0hѣF 4hу R%}ȤrbvӻF1 # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. from preference_manager import preference_manager def get_scene_preferences(): """Return a dictionary of the scene's default preferences.""" pref = preference_manager.preferences res = {} res['stereo'] = eval(pref.get('tvtk.scene.stereo')) res['magnification'] = \ eval(pref.get('tvtk.scene.magnification')) res['foreground'] = eval(pref.get('tvtk.scene.foreground_color')) res['background'] = eval(pref.get('tvtk.scene.background_color')) return res def set_scene_preferences(scene, prefs_dict=None): """Setup the preferences for a scene given a scene and an optional dictionary with the preferences. """ if prefs_dict is None: prefs_dict = get_scene_preferences() # Set the preferences. scene.set(**prefs_dict) # If this isn't done the background isn't set. scene.renderer.background = scene.background mayavi-4.1.0/mayavi/preferences/images/0000755000175100001440000000000011674464502021030 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/preferences/images/preferences.png0000644000175100001440000000150611674464502024041 0ustar ischnellusers00000000000000PNG  IHDRa IDAT8uSMle}M/^gel)RDOR/(M`HЖ8Pz(BJF*@8Eij5v&'GF{yf@D;FE- nb~~޿~Xt;N/!˲0=="#"@,,x_j'GF"BW^?5ð^=r/J}:595 I+vFNTUU*W*T3 2- Z^^ζ$Z:7Ǿ'ǁ (O(R4m`- VΙ3#Qw" ^N?666P7ûgϾ}sd*lmӃkT(黥%Ep xsU?P,Q:iUOԉ/\;Cö۷nc\^?hDi?ᢟ;NLطprDSFNz@T 8F ib1>Q|$<|D"qc^cM+|zʴ(_(/FJ=ޤͿOwe_6|pve0 K%(yBZSvݿgY$IAપ cvTU8>>dY,Nd>U(eIY? q!ݗ}sZ2S.{pmp'E"H$q\o;? /q IENDB`mayavi-4.1.0/mayavi/preferences/images/preferences.ico0000644000175100001440000031342611674464502024036 0ustar ischnellusers00000000000000(vh   00%F/@@(BT( R,R,  R,  R,  !#$%&(()))(('%$#" R,Z; "%(*-0247:<>@BCDDDCB@?=;8530.+)&#  ʿ๬俳M(wAiLk֩ڱ๬ $(,059=AEILPSWZ]`bcddc`^[XTQMJFC?:72/+&"Ĺ๬ݷݴ Ѣnw^Y9U/j8J&`By` q ʗѢگݷ⽱ $ź)ź.ź4Ƚ9ʿ>DINSX]bfkpsx{~܀ځځ}yuqmid`[ʿVźQźLGA俳<俳7⽱2⽱,๬'ܳ#ٮӦ˛ÎznuZ bD T2t@M(J'e6{C R0 cFqWhtĐ{"ʗ&˙+ϟ1Ѣ6Ѣ=ӦB֩HٮNگU۲[ݴa๬fẮl⽱qv÷{ĹȽÒĔĖĖÓʿź÷俳Ắ~๭yݷtܳoڱjٮe֩_ӤZѢTϟMΞH˙A˛;ʗ5ő}0Œw+n&}e!v]lQ_AS1H$p= S,< 3F% ^2 o<F!R0Z;bC"hL'pT+tY1w_6{c<}eBhHlNpUu\Îzbő}iȓnȓtʘ{˙ΞϟѢӦԧ׬گگ׬֩ӤϟΞ˛ȕȓƒ~Đ{yŒwtqnnihc|d\y`Vw^PtYIqWBlR=jN7eH2_A.Y9(R0$L(}D p<b5 M(8 &   /A$R,b5 l9u@!|D&J&*M*/R05T39W6?Y9E[',  8Iv]hLG&U+*>"NL(ő}Ƚɾ÷÷÷÷÷÷÷øøøøøøøøøøøøøøøøøøźøøȽ⽱|du@-<!%Z0rkøźźø^>'-  8Iv]ʿÎzlQߨlQܿt÷ɾø÷÷÷÷÷÷÷÷øøøøøøøøøøøøøøøøøøźźźźøøݴ{c`BġdG˛øźźźźø^>'-  8Iv]Ⱦø÷÷÷÷÷÷÷÷øøøøøøøøøøøøøøøøøøźźźźźźøøøźźźźźźø^>',  8Iv]Ĺ÷÷÷÷÷÷÷÷øøøøøøøøøøøøøøøøøøźźźźźźźźøøøźźźźźźźźø^>',  8Jv]Ĺ÷÷÷÷÷÷÷÷øøøøøøøøøøøøøøøøøøźźźźźźźźźźøźȽȽźøźźźźźźźźźźø^>'- 8Iv]Ĺ÷÷÷÷÷÷÷÷øøøøøøøøøøøøøøøøøøźźźźźźźźźźźźźźźźźźźźźźźźźźźø^>', < Dw^Ⱦø÷÷÷÷÷÷÷÷øøøøøøøøøøøøøøøøøøźźźźźźźźźźźźźźźźźźźźźźźźźźźźø^@+) cFɾø÷÷÷÷÷÷÷÷øøøøøøøøøøøøøøøøøøźźźźźźźźźźźźźźźźźźźźźźźźźźźźźøݴL(H'-y`ɾø÷÷÷÷÷÷÷÷øøøøøøøøøøøøøøøøøøźźźźźźźźźźźźźźźźźźźźźźźźźźźźźźøϟhJ  jN⽱Ƚ·÷÷÷÷÷÷÷÷źźȽȽȽȽȽȽȽźźźøøøøøøźźźźźźźźźźźźźźźźźźźźźźźźźźźźź·ӦY8b3 Klź÷÷÷÷÷øźø⾲ຮڲ׮ժժժժժ׭ڲߺ⾲ĹȽźøøøźźźźźźźźźźźźźźźźźźźźźźźźźźźź·ʿqW< $]1 LzbȾĹ÷÷øź۴ӧϡϡϡѦԧ׮ٱڲڲڱ׮ժУϢϡӧ۲÷ȽȽźźźźźźźźźźźźźźźźźźźźźźźźźźźøʿnS8#< ;uZȽ·øź߹ӦΟӦݷȽɾźȽɾɾźݷӧ˜УݸȽƽźźźźźźźźźźźźźźźźźźźźźźźźøʿnS8$M(DiźøܴϡУάȽ·øŹȽȽɾɾ߹ТΟ۴ȽȽźźźźźźźźźźźźźźźźźźźźźźȽnS8"]1 Quɾ÷ܷڲȽøøźźȽɾɾźѦ̝ݷȾȽźźźźźźźźźźźźźźźźźźźźȾnS8'zCẮȾø÷źźȽɾøΡѦøȽźźźźźźźźźźźźźźźźźźź֩[0fR,VӤø÷źźȽȽ۳ʛ߸ȾźźźźźźźźźźźźźźźźźźʗH'; l9۲Ĺ÷źźȽȽ˜׮ȾźźźźźźźźźźźźźźźźźѢT-[ (1v]ĹøźȽɾɾϡԧȾźźźźźźźźźźźźźźźźɾW6  zCẮź๭ݷᾲڱ÷ժӧȾźźźźźźźźźźźźźźźźӤW-j/;iø̜đ|pĐ{Ӧ׮ԧȾźźźźźźźźźźźźźźøhJ zC⽱÷foӦ}fđ|ӧ֬Ⱦźźźźźźźźźźźźźźź֪Z0j-0}eɾȽȕĐ{q֪Ο۴ȾźźźźźźźźźźźźźźȾ_@k9ܳ߹źøźԧʙڱɾ˜·ȽźźźźźźźźźźźźźźʘF%LhJУ׮Ĺ÷øɾ⾲ϢȽźźźźźźźźźźźźźźȽ|D L(UΞάÎzɾø÷ŹźȽȾɾӧ۲Ⱦźźźźźźźźźźźźźźw_&{Cźɾƕź·øȽȽȽȽΟźźźźźźźźźźźźźźźԧW/`sWɾźȽ׮ƕȽ÷׮֬ȾźźźźźźźźźźźźźȽJ' J'Q˙ɾȾɾȽźĹø÷źœøȾ̝øźźźźźźźźźźźźźźw^&  v@俳øĹźźĹĹøø·÷÷÷ĹܴȖĐ{Y9L[0   X-Y8Dq֬ڲȾźźźźźźźźźźźźź֩Y/a   N*dl÷÷÷÷÷÷÷÷÷÷÷÷÷Ƚ˜۴}ezW-' pU`ϡȽźźźźźźźźźźźźźȽ|dj9M(S,   5;d5 e6d5 d5 d5 d5 d5 yBo÷÷÷÷÷÷÷÷÷÷÷÷đ}֩X-+[0ȾУάȽźźźźźźźźźźźźź˛lqWУhJI&s>f6b5 ^2 s'+ H'B{cʿʿʿʿȽʿø÷÷÷÷÷÷÷÷÷÷÷źٮ˝uYm i8GȽø÷÷߸Ϣ⾲ȾɾȽźø÷÷÷øøźø÷÷÷÷øøȽຮu`3 rZ;Ƚ÷øøøøøøøøøøøøøźœ߹ɾJ', ˛ɾź⾲ݷУɾȽźø÷÷øٮ_3 rT2÷øøøøøøøøøøøøɾ̝׭էW6⾲ܴԨȾɾź÷⾲`3 a< 0ÎzȽʿʿʿʿȽ÷øøøøøøøøøøøȽ֬ϡp  s>vźźڲݷɾøʿȽʿʿʿȽzb28#e6dc5 hd5 gc5 gc5 gc5 gc5 ed5 siL⽱÷øøøøøøøøøøøĹ⾲ƓĐ{C$*  j9ըժl|Dc5 fc5 gc5 gc5 gc5 gc5 id5 [32 sXźøøøøøøøøøøøøɾɘຮȾȽԅJ'a  _2 M_@ӤɾժpU< ?yBĹȾøøøøøøøøøøøøȽٮ̝ɾøẮĖY8xT-?&  &F%8O,gԧݸά֪^2 fW/SէøøøøøøøøøøøøøźœɾȽ÷÷׬๭թi/#&iøøøøøøøøøøøźøɾ֬ӦɾȽźøøȽά๬W6W6øźźźźźźźźźźźźȽđ}ɾȽø÷źøȕd5 t_3 ZӤźźźźźźźźźźźźźȾڲɘɾȽø÷ȽĹqW8,&w^øźźźźźźźźźźźźȖ߹ɾȽźøøɾӤR0F!ܳøźźźźźźźźźźźźȽ·Ð{ɾȽźøøo[0WH'3tøźźźźźźźźźźźźȾڱƕȽȽø÷ź⽱⽱÷֩bC T2֩źźźźźźźźźźźźźźТ̝ȽȽø÷ź֪wđ}vٮpp<pM(7løźźźźźźźźźźźźź˜ժɾȽø÷ȽÎzoԧqȕٮhL/T2źøźźźźźźźźźźźźźźɘԨɾȽø÷ȽڱqÎyٮѢp<oH'7uȽźźźźźźźźźźźźźźźȾ˜ϡɾȽø÷Ƚ⾲˙⽱iL zC⽱źźźźźźźźźźźźźźźźϡƕȽȽø÷źڱ_3 mR,aٮźźźźźźźźźźźźźźźźź׮ŽzȽȽø÷ź·ʘL(E q=⽱źźźźźźźźźźźźźźźźźźὲŕТɾȽȽźøøٯ̝ӤW/g O*`oźźźźźźźźźźźźźźźźźźźźȽȾժđ}۳Ƚ÷÷·ɾ÷ɘάʿhL28 O*`løźźźźźźźźźźźźźźźźźźźźźȾøϡđ}ڲźøɾ߹˜ݷjN29  O*`lȾźźźźźźźźźźźźźźźźźźźźźźźźøӦƕ̝÷У̝ຮiM29  O*_løźźźźźźźźźźźźźźźźźźźźźźźźźźȾȾ߹Ӧ˜̞׭⾲ڲϡТ۲iM28 U/WnøźźźźźźźźźźźźźźźźźźźźźźźźźźźźȽ۲׮ӦТ̝̝̝ѤԧڱຮlQ54iLźøźźźźźźźźźźźźźźźźźźźźźźźźźźźøźɾٮS1 J(+zb俴źźźźźźźźźźźźźźźźźźźźźźźźźźźø÷źϟhJ  bD·źźźźźźźźźźźźźźźźźźźźźźźźźź÷øȽԧQ/@$4qW·źźźźźźźźźźźźźźźźźźźźźźźźø÷øɾ⽱eH5< 5pU·źźźźźźźźźźźźøøøźźźźȽȽźĹø÷øɾ⽱cF3< 5pU·źźźźźźźźźźøź·źɾȽȽźĹø÷ĹɾܳcF3< 5pU·źźźźźźźź·ȽźȽȽźĹø÷źȾ۲_A5< 5pU·źźźźźź·źøźøø÷źȾ۲_A5< 6pU·źźźź·Đ{kOǤiLΞź÷÷źȾʿŒwdGeHÍy۲_A5< 5pU·źź·źiS1h [0WiM֪⾲÷Ƚ̜^>O*:^2 Po۲`B5< 5pU·ʿnSD%+, q=fsX֪̜`BY/K _3 Lo۲_A5< 5pUʿʿnS8#-s>ehLΞȽ๭ÍyY9Y/H ^2 N}e۲_A3< 5pTlQ8#X-DT3}e׬ʿ˙nSzCM(-^2 M|d_A5< 5w_{c8"5b5 XT2nSȓ俳niM|DU/=  `3 KtkO38d5 db3 _2  R,9]=Ắ⽱cF6-; (e6j`3 X- W/FuɾĹtY5,,*pUܴ֩`B$kOݷ׬bD %lQݷ֪bD %lQݷ֪`B  %kOܴը^@ ${chJʗzbnSW6@$3pUtzby`y`y`y`y`y`y`y`y`y`y`y`{cŒw`B5??????????????(  zCzCqW|Do<-a2n-g2fk9R0·øJ&^>ٮ׭׭׭گW6n;Ξ⾲÷Ď{øe6zCn߹qiloڱw`zCR,mźຮԧdGv^ő}·L(f]1 vȾὲԧj9~F!֬Y/oH$JhLȾڱ|dP'\ɗ۲jNW/CiMȽȕ⾲ϟeIh7۲Ӧݷc5 "hk|doŒww_绅nŒw|dΞoThtYI%R0U5O, eHn;Jw^w_O*C?7?(,  != <3gA,oKiHeC 2-xgܰ%U #+ J-pV* p6u\GݲƼĸ⽯gA)*_cB,dH 8 N-ǶЩล䷤ʙ翰}U?13 ܦ⸭zosq߷ĴqKhPGҝڭܵʝ̞͡޳Ɣ㽲ǽ˽vi?.)%;& ~ئ߹ƺ·ŏ޴`M  hJ߲ᷫ㾲ďzƒ~ѠƼԣw:[){I/]B׭ƺϟĹvJ'kO̔h>8Ǚث俴ĺkNޑU25!c3[ϪǼἯҢ߸޲b4OsYƺÏ~ຯǼhH-3_ɠź⾲ПС˖|(@iPڰz໯źɿhE)C( PÛȾ¶ҡ׭带I,xKi༰xǻ佯fD'M(K(M+תۯ˞U:0A51}c೥qTɇCR, 7cBߺ޶˫l3H)8" tΛٯgW a@1濲໯Ɛ֨ǻx AaἯԡקݱ廯DpD-cE_ǛЦpسصwX@$]?lz`ƮsZ6_A9lM֯hȫpTVr*sXoR[9Z:V5A" jB1y2U5 Q.u\oy_⺁k ?8xxx?(0 !!N+s>s>ڈN+  ,̜֬6X- ThJ&ݷȽɾٮF!_  cX-2)H$bCR0ő}ȽøøɾoO,ٟcFwA)M+ø÷źøøźȾF!ʘ⾲|dzc{c|d俴źźźøn <گڱźɾϡøźɗ 4>"ຮٮຮѤźܴ6[=øő}tÍyő}źR0(Z;֞bD֬ɾ⾲ȽĐ{R,&R,&ȕ·øթ|DۤiL 9-X໮Ƚ÷ຮԧ}etYТ·źݷ; k-[ԧȽøź߹ϟڲU3qW⾲ອТ·źɾС; k-UݷȾøάϟøqU4iLźΞźøɾٮ< hI%T2Q/ܴȽ{C z Q/ź߹S1ӊN,< !fJźຮ^@W-Ƚ·ԧøȽJ(v [˙ź·Ѥ⾲ѢG ]=źźźø÷ӦܴݷܴX7cFøȽȽ]=_@`B۲eHuő}ő}ܳ`B`BQ/ísY,eH4T2·T3iL,cF6R0žbC !y_sYu@ʗƪnSpUὅo<<<<( @  <khhk5wAӦʘʘӤj8T3H$9"* 6dhLøøX7]1 6$8;|Db3 S,L'Îzøøøøp}D "Rl9s>9;N+hŹ÷÷øøøø׭v]}D 8F!ø÷øźźźøźźȽźȾq=˛÷άຮ໯άάźźźźźźøp@$%ʘȾŹȽźȽøᾲȽźźźl0N,źɾᾲȽźźF! IٮѣܵȽźƓ=l9źɾ⾲Ƚźøźź_3 6ikv]Ƚź߹˛ŒwĐ{ӦຮȽźjNog)p<ÍyŒwȽ÷÷άźpUpȓάźźź֩ʗf6 [=ø÷ø߹Ƚը J(Đ{⾲·źȽT3 W7øøøźݷάźӦoT̜⾲ԧᾲȽɾN,W6·øøźݷຮź۲}e3׭ȽڱȽ߹øĹźȽO,`B÷źάຮɾ@$|c5 ߺøW6  hL=iyQ/|ϟø÷άϟT- owc3 ȽѣT3py}D 5X7źȽ߹⽱J'W/bźźøøźB%Py_źȽ⿴ø۲nS.T2źźȽĸɾL("l9Ƚźźźźø⿴÷øb3 lRøźźøøźȽq}D ๭·ź·lW6-}D ܴv]O,eHő}iMWگ}feH.F!ϟ˦iM*sXKpUٮݷqWnSCdG8lRޡdG5S2H$^@O,hLaÎzľppʗyBU@???(0` $  Ӧ ,:FR\fnrpiaWK@3%ẮO, e6 ^@h)ƒ<ΞOѢd׬yڱ۲๬Ĺ俳ݴ۲گӦ̜rȕ\ÍyH|d6cF'|DA$   H')b5 :o<MwA]F!rI&R0_AƟcFΦiMԩlRڤhLڞbDՍR0L(H$zCp<rj8__3 NL(=0.   "/ =M$[+jsT3iL$-( pa S B5''2<DDM+ȽȽȽȽkOf]TKB6*   M*øøøøiM  A%e .'RÎzøøøøɗ-Y %C$iv@h/ '^2 dGܳøøøø÷ອhLb5 , 'pTO, yBh; oi8ƒ÷÷øøøøøøȕq=0anSQ/n;ź÷÷÷øøøøøøøøźɾźøH$t@Ƚ÷÷÷øøøøøøøźźøȽźźźøJ' "3ź÷øøøøøøźĹøźźźźźźźźźøA$T^@øά߹ڱڲݷݸ۲۲ຮ·ȽźźźźźźźźźqW Z;ɾᾲźŹɾɾȽ߹߹øȽźźźźźźnS &9⽱ȽźݸὲȽźźźźȾ·@$V]1 ɾ߹άάȽźźźźn;Œwøʙ̝źὲȽźźźϟ3Y/|ɾź÷ຮ߹øźźźźh7`B۲ຮȽźźźsX Bԧźøø߹ܴO-b3 [c5 ]^>ݷøźźźݷC%i Y9үuZnSƒ÷÷÷øܴ{c-F%.ڱ÷źźźź๭u|dߝ`B"5A$TŹ÷÷÷÷øڲԧH'2÷ݷȽźźźƽF%j@$T֪Ƚ·÷÷÷÷÷÷Ӧ^>uZٱȽźźɾ߸C$j@$T׭Ⱦøøøøøø·УȽɾ۳Ӧ⾲W/P}D v⾲У߹ȾڲŻźȽɾ߸C$j@$T׭Ⱦøøøøøø·ТøȽѤȕ⾲R,P|DyܴĐ{ӦȽ׮źøźȽȽɾ߹D%j@$TթźøøøøøѦźȽȽźW6qWɾȽø⾲ڱɾøøøźźø۱C$lA$Sźøøøźڱź߹& 29Ⱦݷὲɾø÷J(i [4,! ٮL(b5 Y9 |dʗ#ԧ/ڱ<ݷJ๭Y俳fĹtʿċɕ̞ЦӫЪ̤ƜʿĹ俳๬r۲eگV׬HѢ:ȕ.k#jNL(R, /W-v@#N,/Z;"k  l9ɾźøøøøøøگH'z  /< l&eH`B+To<nSɾøøøøøø÷๬dGd5 B 4tYX7$bD]=!C$o[<ܳ÷øøøøøøøøѢO,3[  6pTU5&bDhLe6]=ʿɾ÷÷øøøøøøøø÷øȾݷR0b5 w^ȾU5$bDȽ÷÷÷÷øøøøøøøøøźźøźȽøźøU5&bDȽ÷÷÷÷øøøøøøøøøźźźźøȽźźźźźøU5^@Ƚ÷÷÷÷øøøøøøøøøźźźźźźźźźźźźźøN,$˙ź÷÷øøøø··øźźźøźźźźźźźźźźźźźźøhe6Ƚøøάݷڱٱ׮ڲڲڲڱ۴ຮøȽźźźźźźźźźźźźź⽱T-fX-{ʿɾຮڱߺźź÷źɾɾøߺڲܷøȽźźźźźźźźźøẮR,hn;øźȽɾڱݷȽźźźźźźźźȾ·]1 }< U÷øȽɾ÷ڲźźźźźźźźٮ2>R0÷ຮڲźźźźźźźȽzC8W๭ѤÎzȕڱƻźźźźźźѢ'?R0۲Ѥ׭۲ȽźźźźźźyB6ϟٮź÷÷ȽɾάάȽźźźźźkb5 ຮݷڱȽźźźźźI'e^>ȽȽȽźøĹժ蹀iU5bDԧ߹໯ȽźźźźźL(0UC$|A$z; so<๬Ƚ÷÷÷÷÷άٮR/" J',Ѣɾ۲ȽźźźźźܳR0j8O*oB%o+JB%Vٮ÷÷÷÷ø۲߹۲W/Rw]ڱøźźźźźø'<b3 xĹȽ÷÷÷÷÷źթźu@۲ڱὲȽźźźźź< \_2 vܴȽ··÷÷÷÷÷÷÷øӧɾ๭&{Cm۴߹ȽźźźȽ; Z_2 vݷȾøøøøøøøøøøУźȽȽøtY5!ɾݷݷȽźźȽ; Z_2 vݷȾøøøøøøøøøøϡάźȽϟƓТȽR0 ߸άĐ{ȕݷ۲ȽźȽȽɾɾ; Z_2 vݷȾøøøøøøøøøøТ⾲źȽТƓԧX7 ຮຮƓĐ{άɾ۴۴źøźźźȽɾɾ; Z_2 vݷȾøøøøøøøøøøУɾȽɾɾŒwJ'?ŹźøȽڲݷź÷÷øøźȽȽɾȾ; Z_3 xݷȽ÷÷··øøøøøźӦάJ'AZ;ȾĹø׮Ƚźø÷øøøźȽ; [_3 o÷øøøøȽ׮߹t @$Møڲź÷< Q Y9lQΣhJʞbDǸhøøøøøź߹۲kO &OȓȽߺĎzbDɣhJʩlRϑT3  høøøøø÷׮Ѣq=}+9  'F%MX6ٮźɾw^M*øøøøøȽڱɾøຮŹȽn;O*`Ĺźźźźź·ڱɾźøѢ0B iøźźźźȾ۲źȽøȽ_@s>ȾźźźźźȽ֬ȽøɾУݷW-t ő}øźźźźźź֬ȽøȾ๭Œ}ΞqW k9ȽźźźźźȽø֬ȾȽøɾW-tA$`ຮźźźźźźȽź׮ź÷Ƚ๭2G 4pUźźźźźźźźźȾ߹ڱźȽ]= 4pUȽźźźźźźźźźźȽźݷ۲άɾø^> iLȾźźźźźźźźźźźźźȽɾȽȽźɾT2 ʗ·źźźźźźźźźźźźøĹ|d U/søźźźźź···źźź÷źڱM(YS,tøźźźøȽøĹ÷ź׬J'YS,tøøøȕź⾲źÍy׬J(ZS,søẮh7S,jhlQI'Yf6׬J(YS,t÷R,hX-lhL俳׬[ # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. import sys from os.path import isdir, exists, join, basename from os import listdir from traits.api import (HasTraits, List, Str, Instance, DelegatesTo, Button) from traitsui.api import View, Item, SetEditor ################################################################################ # `ContribFinder` class. ################################################################################ class ContribFinder(HasTraits): """ This class helps find installed mayavi contributions. """ # The preference helper whose contrib_packages trait we contribute # to. preference_helper = Instance(HasTraits) # The selected contributions. contrib_packages = DelegatesTo('preference_helper') # The found contrib packages. found_contrib = List(Str, desc='the mayavi contribution ' 'packages on the system') # Search for contributions. search = Button('Search for packages', desc='search again for contributions') ######################################## # View related code. view = View(Item('contrib_packages', show_label=False, editor=SetEditor(name='found_contrib', left_column_title='Available '\ 'contributions', right_column_title='Selected '\ 'contributions', can_move_all=False), resizable=True, ), Item('search', show_label=False), resizable=True ) ###################################################################### # `object` interface. ###################################################################### def __init__(self, **traits): super(ContribFinder, self).__init__(**traits) # Find the contributions by default. self.find() ###################################################################### # `ContribFinder` interface. ###################################################################### def find(self): """Find the contrib directories from sys.path.""" found = [] for d in sys.path: if isdir(d): for s in listdir(d): if exists(join(d, s, 'user_mayavi.py')): found.append(s) self.found_contrib = found ###################################################################### # Non-public interface. ###################################################################### def _preference_helper_default(self): from preference_manager import preference_manager return preference_manager.root def _search_fired(self): self.find() mayavi-4.1.0/mayavi/preferences/preferences.ini0000644000175100001440000000043511674464502022567 0ustar ischnellusers00000000000000[mayavi] confirm_delete = True show_splash_screen = True show_helper_nodes = True open_help_in_light_browser = True use_ipython = False contrib_packages = '[]' [mayavi.mlab] backend = 'auto' background_color = "(0.5, 0.5, 0.5)" foreground_color = "(1.0, 1.0, 1.0)" offscreen = False mayavi-4.1.0/mayavi/preferences/mayavi_preferences_page.py0000644000175100001440000001454111674464502025005 0ustar ischnellusers00000000000000"""Preferences for Mayavi """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import (Bool, Enum, Tuple, Range, List, Str, Instance, HasTraits) from traitsui.api import View, Group, Item, RGBColorEditor from apptools.preferences.ui.api import PreferencesPage ################################################################################ # `MayaviRootPreferencesPage` class ################################################################################ class MayaviRootPreferencesPage(PreferencesPage): """ Preferences page for Mayavi. """ #### 'PreferencesPage' interface ########################################## # The page's category (e.g. 'General/Appearance'). The empty string means # that this is a top-level page. category = '' # The page's help identifier (optional). If a help Id *is* provided then # there will be a 'Help' button shown on the preference page. help_id = '' # The page name (this is what is shown in the preferences dialog. name = 'Mayavi' # The path to the preferences node that contains the preferences. preferences_path = 'mayavi' #### Preferences ########################################################## # Specifies if the nodes on the tree may be deleted without a # confirmation or not. If True the user will be prompted before # the object is deleted. If it is False then the user will not be # prompted. confirm_delete = Bool(desc='if the user is prompted before' ' a node on the MayaVi tree is deleted') # Specifies if the splash screen is shown when mayavi starts. show_splash_screen = Bool(desc='if the splash screen is shown at' ' startup') # Specifies if the adder nodes are shown on the mayavi tree view. show_helper_nodes = Bool(desc='if the helper (adder) nodes are shown' ' on the tree view') # Specifies if the adder nodes are shown on the mayavi tree view. open_help_in_light_browser = Bool( desc='if the help pages are opened in a chromeless' 'browser window (only works with Firefox') # Contrib directories to load on startup. contrib_packages = List(Str, desc='contrib packages to load on startup') # Whether or not to use IPython for the Shell. use_ipython = Bool(desc='use IPython for the embedded shell ' '(if available)') ######################################## # Private traits. _contrib_finder = Instance(HasTraits) #### Traits UI views ###################################################### traits_view = View( Group( Group( Item(name='confirm_delete'), Item(name='show_splash_screen'), Item(name='show_helper_nodes'), Item(name='open_help_in_light_browser'), Item(name='use_ipython'), label='General settings', show_border=True, ), Group( Group( Item('_contrib_finder', style='custom', show_label=False, resizable=True, ), ), label='Contribution settings', show_border=True, ), ), resizable=True ) ###################################################################### # Non-public interface. ###################################################################### def __contrib_finder_default(self): from contrib_finder import ContribFinder return ContribFinder() ################################################################################ # `MayaviMlabPreferencesPage` class ################################################################################ class MayaviMlabPreferencesPage(PreferencesPage): """ Preferences page for Mayavi. """ #### 'PreferencesPage' interface ########################################## # The page's category (e.g. 'General/Appearance'). The empty string means # that this is a top-level page. category = 'Mayavi' # The page's help identifier (optional). If a help Id *is* provided then # there will be a 'Help' button shown on the preference page. help_id = '' # The page name (this is what is shown in the preferences dialog. name = 'Mlab' # The path to the preferences node that contains the preferences. preferences_path = 'mayavi.mlab' #### Preferences ########################################################## # The mlab backend to use. backend = Enum('auto', 'envisage', 'simple', 'test', desc='the mlab backend to use') # The background color of the renderer. background_color = Tuple(Range(0., 1., 1.), Range(0., 1., 1.), Range(0., 1., 1.), editor=RGBColorEditor, desc='the background color of the scene') # The foreground color of the renderer. foreground_color = Tuple(Range(0., 1., 0.), Range(0., 1., 0.), Range(0., 1., 0.), editor=RGBColorEditor, desc='the foreground color of the scene') # Offscreen rendering. offscreen = Bool(desc='if mlab should use offscreen rendering' ' (no window will show up in this case)') ###################################################################### # Traits UI view. traits_view = View(Group( Item('backend'), Item('background_color'), Item('foreground_color'), Item('offscreen'), ), resizable=True ) mayavi-4.1.0/mayavi/preferences/preferences_helpers.py0000644000175100001440000001137111674464502024163 0ustar ischnellusers00000000000000"""Various preference helpers for the mayavi preferences. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports from traits.api import (Bool, Enum, Tuple, Range, List, Str, Instance, HasTraits) from traitsui.api import (View, Group, Item, RGBColorEditor, InstanceEditor) from apptools.preferences.api import PreferencesHelper ################################################################################ # `RootPreferencesHelper` class ################################################################################ class RootPreferencesHelper(PreferencesHelper): # The preferences path for which we use. preferences_path = 'mayavi' ###################################################################### # Our preferences. # Specifies if the nodes on the tree may be deleted without a # confirmation or not. If True the user will be prompted before # the object is deleted. If it is False then the user will not be # prompted. confirm_delete = Bool(desc='if the user is prompted before' ' a node on the MayaVi tree is deleted') # Specifies if the splash screen is shown when mayavi starts. show_splash_screen = Bool(desc='if the splash screen is shown at' ' startup') # Specifies if the adder nodes are shown on the mayavi tree view. show_helper_nodes = Bool(desc='if the helper (adder) nodes are shown' ' on the tree view') # Specifies if the adder nodes are shown on the mayavi tree view. open_help_in_light_browser = Bool( desc='if the help pages are opened in a chromeless' ' browser window (only works with Firefox)') # Contrib directories to load on startup. contrib_packages = List(Str, desc='contrib packages to load on startup') # Whether or not to use IPython for the Shell. use_ipython = Bool(desc='use IPython for the embedded shell ' '(if available)') ######################################## # Private traits. _contrib_finder = Instance(HasTraits) ###################################################################### # Traits UI view. traits_view = View( Group( Item(name='confirm_delete'), Item(name='show_splash_screen'), Item(name='show_helper_nodes'), Item(name='open_help_in_light_browser'), Item('_contrib_finder', show_label=False, editor=InstanceEditor(label='Find contributions'), ) ), resizable=True ) ###################################################################### # Non-public interface. ###################################################################### def __contrib_finder_default(self): from contrib_finder import ContribFinder return ContribFinder() ################################################################################ # `MlabPreferencesHelper` class ################################################################################ class MlabPreferencesHelper(PreferencesHelper): # The preferences path for which we use. preferences_path = 'mayavi.mlab' ###################################################################### # Our preferences. # The mlab backend to use. backend = Enum('auto', 'envisage', 'simple', 'test', desc='the mlab backend to use') # The background color of the renderer. background_color = Tuple(Range(0., 1.), Range(0., 1.), Range(0., 1.), editor=RGBColorEditor, desc='the background color of the scene') # The foreground color of the renderer. foreground_color = Tuple(Range(0., 1.), Range(0., 1.), Range(0., 1.), editor=RGBColorEditor, desc='the foreground color of the scene') # Offscreen rendering. offscreen = Bool(desc='if mlab should use offscreen rendering' ' (no window will show up in this case)') ###################################################################### # Traits UI view. traits_view = View(Group( Item('backend'), Item('background_color'), Item('foreground_color'), Item('offscreen') ), resizable=True ) mayavi-4.1.0/mayavi/preferences/__init__.py0000644000175100001440000000013211674464502021670 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/preferences/preference_manager_view.py0000644000175100001440000000477111674464502025010 0ustar ischnellusers00000000000000""" A view for the Mayavi preferences outside of Envisage. """ # Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports from os.path import join # Enthought library imports. from traits.api import List from apptools.preferences.ui.api import PreferencesManager, \ PreferencesPage from pyface.api import ImageResource from pyface.resource.api import resource_path from apptools.preferences.ui.preferences_node import PreferencesNode # Local imports. from mayavi.preferences.mayavi_preferences_page import \ MayaviRootPreferencesPage, MayaviMlabPreferencesPage from mayavi.preferences.preference_manager import \ preference_manager ################################################################################ # `PreferenceManagerView` class ################################################################################ class PreferenceManagerView(PreferencesManager): """ A preference manager UI for Mayavi, to be used outside of Envisage. """ # Path used to search for images _image_path = [join(resource_path(), 'images'), ] # The icon of the dialog icon = ImageResource('preferences.ico', search_path=_image_path) # The preference pages displayed pages = List(PreferencesPage) def _pages_default(self): return [ MayaviRootPreferencesPage( preferences=preference_manager.root.preferences), MayaviMlabPreferencesPage( preferences=preference_manager.mlab.preferences), ] def dialog_view(self): """ Poor-man's subclassing of view to overload size. """ view = self.trait_view() view.width = 0.7 view.height = 0.5 view.title = 'Mayavi preferences' view.icon = self.icon ui = self.edit_traits( view=view, scrollable=True, id='mayavi.preferences.preference_view') return ui def _get_root(self): """ Subclass the root getter, to work outside of envisage, with no well-defined root. """ root = PreferencesNode(page=self.pages[0]) for page in self.pages: root.append(PreferencesNode(page=page)) return root def apply(self): super(PreferenceManagerView, self).apply() for page in self.pages: page.preferences.save() preference_manager_view = PreferenceManagerView() mayavi-4.1.0/mayavi/preferences/preference_manager.py0000644000175100001440000001105311674464502023745 0ustar ischnellusers00000000000000"""A preference manager for all mayavi related preferences. The idea behind this module is that it lets the mayavi library/application use the same preferences by managing them no matter if mayavi is used as an application (via envisage3) or as a library. The preferences helpers are divided into different categories for different kinds of preferences. Currently the following are available. - root: for global mayavi preferences of the form 'mayavi.preference'. For more details on the general preferences support in enthought, please read the documentation for apptools.preferences (part of the AppTools package). """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports from os.path import join import pkg_resources # Enthought library imports. from traits.etsconfig.api import ETSConfig from traits.api import HasTraits, Instance from traitsui.api import View, Group, Item from apptools.preferences.api import (ScopedPreferences, IPreferences, PreferencesHelper) # Local imports. from mayavi.preferences.preferences_helpers import ( RootPreferencesHelper, MlabPreferencesHelper ) # The application ID where the preferences are stored. ID = 'mayavi_e3' ################################################################################ # `PreferenceManager` class ################################################################################ class PreferenceManager(HasTraits): # The root preferences helper for preferences of the form # 'mayavi.preference'. root = Instance(PreferencesHelper) # The mlab preferences helper for preferences of the form # 'mayavi.mlab.preference'. mlab = Instance(PreferencesHelper) # The preferences. preferences = Instance(IPreferences) ###################################################################### # Traits UI view. traits_view = View(Group( Group(Item(name='root', style='custom'), show_labels=False, label='Root', show_border=True ), Group(Item(name='mlab', style='custom'), show_labels=False, label='Mlab', show_border=True, ), ), buttons=['OK', 'Cancel'], resizable=True ) ###################################################################### # `HasTraits` interface. ###################################################################### def __init__(self, **traits): super(PreferenceManager, self).__init__(**traits) if 'preferences' not in traits: self._load_preferences() def _preferences_default(self): """Trait initializer.""" return ScopedPreferences() def _root_default(self): """Trait initializer.""" return RootPreferencesHelper(preferences=self.preferences) def _mlab_default(self): """Trait initializer.""" return MlabPreferencesHelper(preferences=self.preferences) ###################################################################### # Private interface. ###################################################################### def _load_preferences(self): """Load the default preferences.""" # Save current application_home. app_home = ETSConfig.get_application_home() # Set it to where the mayavi preferences are temporarily. path = join(ETSConfig.get_application_data(), ID) ETSConfig.application_home = path try: for pkg in ('mayavi.preferences', 'tvtk.plugins.scene'): pref = 'preferences.ini' pref_file = pkg_resources.resource_stream(pkg, pref) preferences = self.preferences default = preferences.node('default/') default.load(pref_file) pref_file.close() finally: # Set back the application home. ETSConfig.application_home = app_home def _preferences_changed(self, preferences): """Setup the helpers if the preferences trait changes.""" for helper in (self.root, ): helper.preferences = preferences ########################################################## # A Global preference manager that all other modules can use. preference_manager = PreferenceManager() mayavi-4.1.0/mayavi/api.py0000644000175100001440000000031611674464502016405 0ustar ischnellusers00000000000000from mayavi.version import version, version as __version__ from mayavi.core.engine import Engine from mayavi.core.off_screen_engine import OffScreenEngine from mayavi.tests.runtests import m2_tests as test mayavi-4.1.0/mayavi/filters/0000755000175100001440000000000011674464502016732 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/filters/delaunay3d.py0000644000175100001440000000233011674464502021333 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `Delaunay3D` class. ###################################################################### class Delaunay3D(FilterBase): """Performs a 3D Delaunay triangulation using the tvtk.Delaunay3D class. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.Delaunay3D, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['structured_grid', 'poly_data', 'unstructured_grid'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/image_change_information.py0000644000175100001440000000244211674464502024302 0ustar ischnellusers00000000000000""" A filter that lets you change the spacing and origin of an input ImageData dataset. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ImageChangeInformation` class. ###################################################################### class ImageChangeInformation(FilterBase): """ A filter that lets you change the spacing and origin of an input ImageData dataset. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.ImageChangeInformation, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/mask_points.py0000644000175100001440000000235711674464502021642 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `MaskPoints` class. ###################################################################### class MaskPoints(FilterBase): """Selectively passes the input points downstream. This can be used to subsample the input points. Note that this does not pass geometry data, this means all grid information is lost. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.MaskPoints, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/extract_unstructured_grid.py0000644000175100001440000000227111674464502024614 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ExtractUnstructuredGrid` class. ###################################################################### class ExtractUnstructuredGrid(FilterBase): """Allows a user to select a part of an unstructured grid. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.ExtractUnstructuredGrid, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/extract_grid.py0000644000175100001440000002124711674464502021771 0ustar ischnellusers00000000000000"""This filter enables one to select a portion of, or subsample an input dataset which may be a StructuredPoints, StructuredGrid or Rectilinear. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Int, Range from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.core.common import error from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ExtractGrid` class. ###################################################################### class ExtractGrid(FilterBase): """This filter enables one to select a portion of, or subsample an input dataset which may be a StructuredPoints, StructuredGrid or Rectilinear. """ # The version of this class. Used for persistence. __version__ = 0 # Minimum x value. x_min = Range(value=0, low='_x_low', high='_x_high', enter_set=True, auto_set=False, desc='minimum x value of the domain') # Maximum x value. x_max = Range(value=10000, low='_x_low', high='_x_high', enter_set=True, auto_set=False, desc='maximum x value of the domain') # Minimum y value. y_min = Range(value=0, low='_y_low', high='_y_high', enter_set=True, auto_set=False, desc='minimum y value of the domain') # Maximum y value. y_max = Range(value=10000, low='_y_low', high='_y_high', enter_set=True, auto_set=False, desc='maximum y value of the domain') # Minimum z value. z_min = Range(value=0, low='_z_low', high='_z_high', enter_set=True, auto_set=False, desc='minimum z value of the domain') # Maximum z value. z_max = Range(value=10000, low='_z_low', high='_z_high', enter_set=True, auto_set=False, desc='maximum z value of the domain') # Sample rate in x. x_ratio = Range(value=1, low='_min_sample', high='_x_s_high', enter_set=True, auto_set=False, desc='sample rate along x') # Sample rate in y. y_ratio = Range(value=1, low='_min_sample', high='_y_s_high', enter_set=True, auto_set=False, desc='sample rate along y') # Sample rate in z. z_ratio = Range(value=1, low='_min_sample', high='_z_s_high', enter_set=True, auto_set=False, desc='sample rate along z') # The actual TVTK filter that this class manages. filter = Instance(tvtk.Object, tvtk.ExtractVOI(), allow_none=False) input_info = PipelineInfo(datasets=['image_data', 'rectilinear_grid', 'structured_grid'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['image_data', 'rectilinear_grid', 'structured_grid'], attribute_types=['any'], attributes=['any']) ######################################## # Private traits. # Determines the lower/upper limit of the axes for the sliders. _min_sample = Int(1) _x_low = Int(0) _x_high = Int(10000) _x_s_high = Int(100) _y_low = Int(0) _y_high = Int(10000) _y_s_high = Int(100) _z_low = Int(0) _z_high = Int(10000) _z_s_high = Int(100) ######################################## # View related traits. # The View for this object. view = View(Group(Item(label='Select Volume Of Interest'), Item(name='x_min'), Item(name='x_max'), Item(name='y_min'), Item(name='y_max'), Item(name='z_min'), Item(name='z_max'), Item('_'), Item(label='Select Sample Ratio'), Item(name='x_ratio'), Item(name='y_ratio'), Item(name='z_ratio'), label='VOI' ), Group(Item(name='filter', style='custom', resizable=True), show_labels=False, label='Filter'), resizable=True, ) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(ExtractGrid, self).__get_pure_state__() for axis in ('x', 'y', 'z'): for name in ('_min', '_max'): d.pop(axis + name, None) d.pop('_' + axis + '_low', None) d.pop('_' + axis + '_high', None) d.pop('_' + axis + '_s_high', None) d.pop(axis + '_ratio', None) return d ###################################################################### # `Filter` interface ###################################################################### def update_pipeline(self): inputs = self.inputs if len(inputs) == 0: return input = inputs[0].outputs[0] mapping = {'vtkStructuredGrid': tvtk.ExtractGrid, 'vtkRectilinearGrid': tvtk.ExtractRectilinearGrid, 'vtkImageData': tvtk.ExtractVOI} for key, klass in mapping.iteritems(): if input.is_a(key): self.filter = klass() break else: error('This filter does not support %s objects'%\ (input.__class__.__name__)) return fil = self.filter fil.input = input fil.update_whole_extent() fil.update() self._set_outputs([fil.output]) self._update_limits() self._update_voi() self._update_sample_rate() def update_data(self): """This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self._update_limits() fil = self.filter fil.update_whole_extent() fil.update() # Propagate the data_changed event. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _update_limits(self): extents = self.filter.input.whole_extent self._x_low, self._x_high = extents[:2] self._y_low, self._y_high = extents[2:4] self._z_low, self._z_high = extents[4:] self._x_s_high = max(1, self._x_high) self._y_s_high = max(1, self._y_high) self._z_s_high = max(1, self._z_high) def _x_min_changed(self, val): if val > self.x_max: self.x_max = val else: self._update_voi() def _x_max_changed(self, val): if val < self.x_min: self.x_min = val else: self._update_voi() def _y_min_changed(self, val): if val > self.y_max: self.y_max = val else: self._update_voi() def _y_max_changed(self, val): if val < self.y_min: self.y_min = val else: self._update_voi() def _z_min_changed(self, val): if val > self.z_max: self.z_max = val else: self._update_voi() def _z_max_changed(self, val): if val < self.z_min: self.z_min = val else: self._update_voi() def _x_ratio_changed(self): self._update_sample_rate() def _y_ratio_changed(self): self._update_sample_rate() def _z_ratio_changed(self): self._update_sample_rate() def _update_voi(self): f = self.filter f.voi = (self.x_min, self.x_max, self.y_min, self.y_max, self.z_min, self.z_max) f.update_whole_extent() f.update() self.data_changed = True def _update_sample_rate(self): f = self.filter f.sample_rate = (self.x_ratio, self.y_ratio, self.z_ratio) f.update_whole_extent() f.update() self.data_changed = True def _filter_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) mayavi-4.1.0/mayavi/filters/vorticity.py0000644000175100001440000000646711674464502021355 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item # Local imports. from mayavi.core.pipeline_info import PipelineInfo from mayavi.filters.optional import Optional from mayavi.filters.collection import Collection from mayavi.filters.cell_derivatives import CellDerivatives from mayavi.filters.cell_to_point_data import CellToPointData from mayavi.filters.extract_vector_norm import ExtractVectorNorm from mayavi.filters.extract_vector_components import ExtractVectorComponents ################################################################################ # `Vorticity` class. ################################################################################ class Vorticity(Optional): """ This filter computes the vorticity of an input vector field. For convenience, the filter allows one to optionally pass-through the given input vector field. The filter also allows the user to show the component of the vorticity along a particular cartesian co-ordinate axes. It produces point data on output which is ready to visualize. """ # This is used just for the UI. vorticity_component = Instance(Optional, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ###################################################################### # `object` interface. ###################################################################### def __get_pure_state__(self): d = super(Vorticity, self).__get_pure_state__() for name in ('vorticity_component'): d.pop(name, None) return d ###################################################################### # `HasTraits` interface. ###################################################################### def default_traits_view(self): view = View(Group(Group(Item(name='enabled', label='Compute Vorticity', )), Group(Item(name='vorticity_component', style='custom', resizable=True, show_label=False), )) ) return view ###################################################################### # `Filter` interface. ###################################################################### def setup_pipeline(self): """Setup our pipeline""" cd = CellDerivatives() cd.filter.vector_mode = 'compute_vorticity' c2d = CellToPointData() evn = ExtractVectorNorm() evc = ExtractVectorComponents() o = Optional(filter=evc, label_text='Extract Component of Vorticity', enabled=False) self.vorticity_component = o c = Collection(filters=[cd, c2d, evn, o], name='Vorticity') self.filter = c mayavi-4.1.0/mayavi/filters/gaussian_splatter.py0000644000175100001440000000210211674464502023027 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `GaussianSplatter` class. ###################################################################### class GaussianSplatter(FilterBase): # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.GaussianSplatter, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/quadric_decimation.py0000644000175100001440000000222411674464502023130 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.poly_data_filter_base import \ PolyDataFilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `QuadricDecimation` class. ###################################################################### class QuadricDecimation(PolyDataFilterBase): """ Simplifies triangles of a mesh """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.QuadricDecimation, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/delaunay2d.py0000644000175100001440000000232011674464502021331 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `Delaunay2D` class. ###################################################################### class Delaunay2D(FilterBase): """Performs a 2D Delaunay triangulation using the tvtk.Delaunay2D class. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.Delaunay2D, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['structured_grid', 'poly_data', 'unstructured_grid'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/warp_scalar.py0000644000175100001440000000237111674464502021605 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.poly_data_normals import PolyDataNormals from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `WarpScalar` class. ###################################################################### class WarpScalar(PolyDataNormals): """Warps the input data along a particular direction (either the normals or a specified direction) with a scale specified by the local scalar value. Useful for making carpet plots. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.WarpScalar, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['scalars']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/optional.py0000644000175100001440000000130111674464502021124 0ustar ischnellusers00000000000000"""The Optional filter is one which may be turned on and off and wraps around any mayavi filter or component. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. from mayavi.filters.wrapper import Wrapper ################################################################################ # `Optional` class. ################################################################################ class Optional(Wrapper): """ This class wraps around any mayavi filter or component and allows a user to turn it on or off. """ # This filter should allow us to turn on/off the filter. _show_enabled = True mayavi-4.1.0/mayavi/filters/api.py0000644000175100001440000000327711674464502020066 0ustar ischnellusers00000000000000""" Defines the publicly accessible MayaVi2 filters. """ # Authors: Frederic Petit, Prabhu Ramachandran and Gael Varoquaux # Copyright (c) 2007-2009, Enthought, Inc. # License: BSD Style. from cell_derivatives import CellDerivatives from cell_to_point_data import CellToPointData from collection import Collection from data_set_clipper import DataSetClipper from contour import Contour from cut_plane import CutPlane from decimatepro import DecimatePro from delaunay2d import Delaunay2D from delaunay3d import Delaunay3D from elevation_filter import ElevationFilter from extract_edges import ExtractEdges from extract_grid import ExtractGrid from extract_tensor_components import ExtractTensorComponents from extract_unstructured_grid import ExtractUnstructuredGrid from extract_vector_components import ExtractVectorComponents from extract_vector_norm import ExtractVectorNorm from gaussian_splatter import GaussianSplatter from greedy_terrain_decimation import GreedyTerrainDecimation from image_change_information import ImageChangeInformation from image_data_probe import ImageDataProbe from mask_points import MaskPoints from optional import Optional from point_to_cell_data import PointToCellData from poly_data_normals import PolyDataNormals from quadric_decimation import QuadricDecimation from select_output import SelectOutput from set_active_attribute import SetActiveAttribute from stripper import Stripper from threshold import Threshold from transform_data import TransformData from triangle_filter import TriangleFilter from tube import Tube from user_defined import UserDefined from vorticity import Vorticity from warp_scalar import WarpScalar from warp_vector import WarpVector from wrapper import Wrapper mayavi-4.1.0/mayavi/filters/poly_data_normals.py0000644000175100001440000000234211674464502023014 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.poly_data_filter_base import \ PolyDataFilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `PolyDataNormals` class. ###################################################################### class PolyDataNormals(PolyDataFilterBase): """Computes normals from input data. This gives meshes a smoother appearance. This should work for any input dataset. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.PolyDataNormals, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/extract_edges.py0000644000175100001440000000210611674464502022124 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ExtractEdges` class. ###################################################################### class ExtractEdges(FilterBase): """Turns edges into lines. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.ExtractEdges, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/set_active_attribute.py0000644000175100001440000002121011674464502023511 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, List, Str, Bool from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports. from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.filter import Filter from mayavi.core.trait_defs import DEnum from mayavi.sources.vtk_xml_file_reader import get_all_attributes ################################################################################ # `SetActiveAttribute` class. ################################################################################ class SetActiveAttribute(Filter): """ This filter lets a user set the active data attribute (scalars, vectors and tensors) on a VTK dataset. This is particularly useful if you need to do something like compute contours of one scalar on the contour of another scalar. """ # Note: most of this code is from the XMLFileDataReader. # The version of this class. Used for persistence. __version__ = 0 input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ######################################## # Dynamic traits: These traits are dynamic and are automatically # updated depending on the contents of the file. # The active point scalar name. An empty string indicates that # the attribute is "deactivated". This is useful when you have # both point and cell attributes and want to use cell data by # default. point_scalars_name = DEnum(values_name='_point_scalars_list', desc='scalar point data attribute to use') # The active point vector name. point_vectors_name = DEnum(values_name='_point_vectors_list', desc='vectors point data attribute to use') # The active point tensor name. point_tensors_name = DEnum(values_name='_point_tensors_list', desc='tensor point data attribute to use') # The active cell scalar name. cell_scalars_name = DEnum(values_name='_cell_scalars_list', desc='scalar cell data attribute to use') # The active cell vector name. cell_vectors_name = DEnum(values_name='_cell_vectors_list', desc='vectors cell data attribute to use') # The active cell tensor name. cell_tensors_name = DEnum(values_name='_cell_tensors_list', desc='tensor cell data attribute to use') ######################################## # Our view. view = View(Group(Item(name='point_scalars_name'), Item(name='point_vectors_name'), Item(name='point_tensors_name'), Item(name='cell_scalars_name'), Item(name='cell_vectors_name'), Item(name='cell_tensors_name'), )) ######################################## # Private traits. # These private traits store the list of available data # attributes. The non-private traits use these lists internally. _point_scalars_list = List(Str) _point_vectors_list = List(Str) _point_tensors_list = List(Str) _cell_scalars_list = List(Str) _cell_vectors_list = List(Str) _cell_tensors_list = List(Str) # This filter allows us to change the attributes of the data # object and will ensure that the pipeline is properly taken care # of. Directly setting the array in the VTK object will not do # this. _assign_attribute = Instance(tvtk.AssignAttribute, args=(), allow_none=False) # Toggles if this is the first time this object has been used. _first = Bool(True) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(SetActiveAttribute, self).__get_pure_state__() for name in ('_assign_attribute', '_first'): d.pop(name, None) # Pickle the 'point_scalars_name' etc. since these are # properties and not in __dict__. attr = {} for name in ('point_scalars', 'point_vectors', 'point_tensors', 'cell_scalars', 'cell_vectors', 'cell_tensors'): d.pop('_' + name + '_list', None) d.pop('_' + name + '_name', None) x = name + '_name' attr[x] = getattr(self, x) d.update(attr) return d ###################################################################### # `Filter` interface. ###################################################################### def update_data(self): self.data_changed = True def update_pipeline(self): if len(self.inputs) == 0 or len(self.inputs[0].outputs) == 0: return aa = self._assign_attribute aa.input = self.inputs[0].outputs[0] self._update() self._set_outputs([aa.output]) ###################################################################### # Non-public interface. ###################################################################### def _update(self): """Updates the traits for the fields that are available in the input data. """ if len(self.inputs) == 0 or len(self.inputs[0].outputs) == 0: return input = self.inputs[0].outputs[0] if self._first: # Force all attributes to be defined and computed input.update() pnt_attr, cell_attr = get_all_attributes(input) self._setup_data_traits(cell_attr, 'cell') self._setup_data_traits(pnt_attr, 'point') if self._first: self._first = False def _setup_data_traits(self, attributes, d_type): """Given the dict of the attributes from the `get_all_attributes` function and the data type (point/cell) data this will setup the object and the data. """ attrs = ['scalars', 'vectors', 'tensors'] aa = self._assign_attribute input = self.inputs[0].outputs[0] data = getattr(input, '%s_data'%d_type) for attr in attrs: values = attributes[attr] values.append('') setattr(self, '_%s_%s_list'%(d_type, attr), values) if len(values) > 1: default = getattr(self, '%s_%s_name'%(d_type, attr)) if self._first and len(default) == 0: default = values[0] getattr(data, 'set_active_%s'%attr)(default) aa.assign(default, attr.upper(), d_type.upper() +'_DATA') aa.update() kw = {'%s_%s_name'%(d_type, attr): default, 'trait_change_notify': False} self.set(**kw) def _set_data_name(self, data_type, attr_type, value): if value is None or len(self.inputs) == 0: return input = self.inputs[0].outputs[0] if len(value) == 0: # If the value is empty then we deactivate that attribute. d = getattr(input, attr_type + '_data') method = getattr(d, 'set_active_%s'%data_type) method(None) self.data_changed = True return aa = self._assign_attribute data = None if attr_type == 'point': data = input.point_data elif attr_type == 'cell': data = input.cell_data method = getattr(data, 'set_active_%s'%data_type) method(value) aa.assign(value, data_type.upper(), attr_type.upper() +'_DATA') aa.update() # Fire an event, so the changes propagate. self.data_changed = True def _point_scalars_name_changed(self, value): self._set_data_name('scalars', 'point', value) def _point_vectors_name_changed(self, value): self._set_data_name('vectors', 'point', value) def _point_tensors_name_changed(self, value): self._set_data_name('tensors', 'point', value) def _cell_scalars_name_changed(self, value): self._set_data_name('scalars', 'cell', value) def _cell_vectors_name_changed(self, value): self._set_data_name('vectors', 'cell', value) def _cell_tensors_name_changed(self, value): self._set_data_name('tensors', 'cell', value) mayavi-4.1.0/mayavi/filters/select_output.py0000644000175100001440000000651211674464502022207 0ustar ischnellusers00000000000000"""Filter that allows a user to select one among several of the outputs of a given input. This is typically very useful for a multi-block data source. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. # Enthought library imports. from traits.api import Int, Range from traitsui.api import View, Group, Item from mayavi.core.filter import Filter from mayavi.core.pipeline_info import PipelineInfo ################################################################################ # `SelectOutput` class. ################################################################################ class SelectOutput(Filter): """ This filter lets a user select one among several of the outputs of a given input. This is typically very useful for a multi-block data source. """ # The output index in the input to choose from. output_index = Range(value=0, enter_set=True, auto_set=False, low='_min_index', high='_max_index') input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) # The minimum output index of our input. _min_index = Int(0, desc='the minimum output index') # The maximum output index of our input. _max_index = Int(0, desc='the maximum output index') ######################################## # Traits View. view = View(Group(Item('output_index', enabled_when='_max_index > 0')), resizable=True) ###################################################################### # `object` interface. def __get_pure_state__(self): d = super(SelectOutput, self).__get_pure_state__() d['output_index'] = self.output_index return d def __set_pure_state__(self, state): super(SelectOutput, self).__set_pure_state__(state) # Force an update of the output index -- if not this doesn't # change. self._output_index_changed(state.output_index) ###################################################################### # `Filter` interface. def update_pipeline(self): # Do nothing if there is no input. inputs = self.inputs if len(inputs) == 0: return # Set the maximum index. self._max_index = len(inputs[0].outputs) - 1 self._output_index_changed(self.output_index) def update_data(self): # Propagate the event. self.data_changed = True ###################################################################### # Trait handlers. def _output_index_changed(self, value): """Static trait handler.""" if value > self._max_index: self.output_index = self._max_index elif value < self._min_index: self.output_index = self._min_index else: self._set_outputs([self.inputs[0].outputs[value]]) s = self.scene if s is not None: s.renderer.reset_camera_clipping_range() s.render() mayavi-4.1.0/mayavi/filters/point_to_cell_data.py0000644000175100001440000000222211674464502023125 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.cell_to_point_data import CellToPointData from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `PointToCellData` class. ###################################################################### class PointToCellData(CellToPointData): # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.PointDataToCellData, args=(), kw={'pass_point_data':1}, allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['point'], attributes=['any']) output_info = PipelineInfo(datasets=['any'], attribute_types=['cell'], attributes=['any']) mayavi-4.1.0/mayavi/filters/image_data_probe.py0000644000175100001440000002052711674464502022554 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. import numpy # Enthought library imports. from traits.api import Instance, Bool, Array, Button, Str from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports. from mayavi.core.filter import Filter from mayavi.core.pipeline_info import PipelineInfo ################################################################################ # `ImageDataProbe` class. ################################################################################ class ImageDataProbe(Filter): """ A filter that can be used to probe any dataset using a Structured Points dataset. The filter also allows one to convert the scalar data to an unsigned short array so that the scalars can be used for volume visualization. """ # The image data onto which the data is probed. probe_data = Instance(tvtk.ImageData, args=()) # The probe filter. filter = Instance(tvtk.ProbeFilter, args=()) rescale_scalars = Bool(False, desc='if the input scalars are '\ 'rescaled to an unsigned short '\ 'array') # Specifies if we can change the spacing/dimensions -- not allowed # for imagedata/structured points data. allow_changes = Bool(True) # Spacing of points in the image data. spacing = Array(value=(0.0, 0.0, 0.0), shape=(3,), cols=1, dtype=float, enter_set=True, auto_set=False, labels=['sx', 'sy', 'sz'], desc='the spacing of points') # Dimensions of the image data. dimensions = Array(value=(0,0,0), shape=(3,), cols=1, dtype=int, enter_set=True, auto_set=False, labels=['nx', 'ny', 'nz'], desc='the dimensions of the image data') # Reset settings to defaults. reset_defaults = Button(desc='if probe data is reset to defaults') # Name of rescaled scalar to generate. rescaled_scalar_name = Str('probe_us_array') input_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) ######################################## # Private traits. # A trait to prevent static handlers from firing unnecessarily. _event_handled = Bool(False) ######################################## # View related traits. view = View(Group(Item(name='dimensions', enabled_when='allow_changes' ), Item(name='spacing', enabled_when='allow_changes'), Item(name='rescale_scalars'), Item(name='reset_defaults', show_label=False), ), resizable=True ) ###################################################################### # `Filter` interface. ###################################################################### def setup_pipeline(self): """Creates the pipeline.""" self.filter.input = self.probe_data def update_pipeline(self): """Connect and update the pipeline.""" inputs = self.inputs if len(inputs) == 0: return fil = self.filter fil.source = inputs[0].outputs[0] reset = False if self.dimensions.sum() == 0: reset = True self._setup_probe_data(reset) fil.update() self._rescale_scalars_changed(self.rescale_scalars) self._set_outputs([fil.output]) ###################################################################### # Non-public interface. ###################################################################### def _setup_probe_data(self, reset=False): pd = self.probe_data input = self.inputs[0].outputs[0] if input.is_a('vtkImageData'): self.allow_changes = False self.set(spacing=input.spacing, dimensions=input.dimensions) pd.set(origin=input.origin, dimensions=input.dimensions, spacing=input.spacing) pd.update() elif reset: self.allow_changes = True b = numpy.array(input.bounds) pd.origin = b[::2] l = b[1::2] - b[::2] tot_len = sum(l) npnt = pow(input.number_of_points, 1./3.) + 0.5 fac = 3.0*npnt/tot_len dims = (l*fac).astype(int) + 1 extent = (0, dims[0] -1, 0, dims[1] -1, 0, dims[2] -1) pd.set(extent=extent, update_extent=extent, whole_extent=extent, dimensions=dims) max_dim = dims.max() dims = (dims-1).clip(min=1, max=max_dim+1) l = l.clip(min=1e-3, max=l.max()+1.0) pd.spacing = l/dims self._event_handled = True self.set(spacing = pd.spacing, dimensions=pd.dimensions) self._event_handled = False pd.update() def _rescale_scalars_changed(self, value): out = self.filter.output pd = out.point_data sc = pd.scalars if sc is None: # no input scalars return if not value: orig_sc = self.inputs[0].outputs[0].point_data.scalars if sc.is_a('vtkUnsignedShortArray') and \ sc.name == self.rescaled_scalar_name: pd.set_active_scalars(orig_sc.name) pd.update() self.pipeline_changed = True self.render() return s_min, s_max = sc.range # checking to see if input array is constant. avg = (s_max + s_min)*0.5 diff = 1 if (s_max > avg) and (avg > s_min): diff = s_max - s_min arr = (sc.to_array() - s_min)*65535.0/diff uc = tvtk.UnsignedShortArray(name=self.rescaled_scalar_name) uc.from_array(arr) pd.add_array(uc) pd.set_active_scalars(self.rescaled_scalar_name) pd.update() self.pipeline_changed = True self.render() def _dimensions_changed(self, value): if not self.allow_changes or self._event_handled: return max_d = value.max() dims = (value-1).clip(min=1, max=max_d) b = numpy.array(self.inputs[0].outputs[0].bounds) l = b[1::2] - b[::2] self.spacing = l/dims self._update_probe() def _spacing_changed(self, value): if not self.allow_changes or self._event_handled: return b = numpy.array(self.inputs[0].outputs[0].bounds) l = b[1::2] - b[::2] dims = (l/value + 0.5).astype(int) + 1 # Recalculate space because of rounding. maxd = dims.max() dims1 = (dims -1).clip(min=1, max=maxd) sp = l/dims1 self._event_handled = True self.set(spacing = sp, dimensions=dims) self._event_handled = False self._update_probe () def _update_probe(self): pd = self.probe_data dims = self.dimensions spacing = self.spacing extent = (0, dims[0] -1, 0, dims[1] -1, 0, dims[2] -1) pd.set(extent=extent, update_extent=extent, whole_extent=extent, dimensions=dims, spacing=spacing) pd.modified() pd.update() fil = self.filter w = fil.global_warning_display fil.global_warning_display = False fil.remove_all_inputs() fil.input = pd fil.update_whole_extent() fil.update() self._rescale_scalars_changed(self.rescale_scalars) fil.global_warning_display = w self.data_changed = True def _reset_defaults_fired(self): self._setup_probe_data(reset=True) self._rescale_scalars_changed(self.rescale_scalars) mayavi-4.1.0/mayavi/filters/transform_data.py0000644000175100001440000001652011674464502022314 0ustar ischnellusers00000000000000"""Performs a linear transformation to input data using a tvtk.BoxWidget. This does not work with ImageData/StructuredPoints/RectilinearGrid. """ # Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Standard library imports. import cPickle # Enthought library imports. from traits.api import Instance, Property, Bool, Int, \ Trait, TraitMap, Button from traitsui.api import View, Group, Item from tvtk.api import tvtk from apptools.persistence import state_pickler # Local imports from mayavi.core.filter import Filter from mayavi.core.common import error from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `TransformData` class. ###################################################################### class TransformData(Filter): """Performs a linear transformation to input data using a tvtk.BoxWidget. This does not work with ImageData/StructuredPoints/RectilinearGrid. """ # The version of this class. Used for persistence. __version__ = 0 # The widget that we use to perform the transformation. widget = Instance(tvtk.ThreeDWidget, allow_none=False, record=True) # The filter we manage. filter = Instance(tvtk.Object, allow_none=False) # The transform. transform = Property # Update the data immediately or at the end of the interaction. update_mode = Trait('semi-interactive', TraitMap({'interactive':'InteractionEvent', 'semi-interactive': 'EndInteractionEvent'}), desc='speed at which the data should be updated') input_info = PipelineInfo(datasets=['poly_data', 'structured_grid', 'unstructured_grid'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data', 'structured_grid', 'unstructured_grid'], attribute_types=['any'], attributes=['any']) ######################################## # View related code. # Reset the transformation. reset = Button("Reset Transformation") view = View(Group(Group(Item('update_mode'), ), Group(Item('reset'), Item(name='widget', style='custom', resizable=True), show_labels=False ) ), resizable=True ) ######################################## # Private traits. _transform = Instance(tvtk.Transform, allow_none=False) _first = Bool(True) _observer_id = Int(-1) ###################################################################### # `object` interface. ###################################################################### def __get_pure_state__(self): d = super(TransformData, self).__get_pure_state__() for name in ('_first', '_observer_id'): d.pop(name, None) d['matrix'] = cPickle.dumps(self._transform.matrix) return d def __set_pure_state__(self, state): mat = state.pop('matrix') super(TransformData, self).__set_pure_state__(state) state_pickler.set_state(self, state) self._transform.set_matrix(cPickle.loads(mat)) self.widget.set_transform(self._transform) ###################################################################### # `Filter` interface. ###################################################################### def setup_pipeline(self): self._transform = tvtk.Transform() self.widget = tvtk.BoxWidget(place_factor=1.1) self.filter = tvtk.TransformFilter() super(TransformData, self).setup_pipeline() def update_pipeline(self): # Do nothing if there is no input. inputs = self.inputs if len(inputs) == 0: return inp = inputs[0].outputs[0] if inp.is_a('vtkImageData') or inp.is_a('vtkRectilinearGrid'): error('Transformation not supported for '\ 'ImageData/StructuredPoints/RectilinearGrid') return # Set the input for the widget and place it if this hasn't # been done before. w = self.widget w.input = inp if self._first: w.place_widget() self._first = False # By default we set the input to the first output of the first # input. fil = self.filter fil.input = inp fil.transform = self._transform fil.update() self._set_outputs([fil.output]) def update_data(self): # Do nothing if there is no input. if len(self.inputs) == 0: return self.filter.update() # Propagate the data_changed event. self.data_changed = True ###################################################################### # Non-public interface. ###################################################################### def _get_transform(self): return self._transform def _on_interaction_event(self, obj, event): tfm = self._transform self.widget.get_transform(tfm) f = self.filter f.transform = tfm f.update() self.render() recorder = self.recorder if recorder is not None: state = {} state['elements'] = tfm.matrix.__getstate__()['elements'] name = recorder.get_script_id(self) recorder.record('%s.transform.matrix.__setstate__(%s)'\ %(name, state)) recorder.record('%s.widget.set_transform(%s.transform)'\ %(name, name)) recorder.record('%s.filter.update()'%name) def _widget_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) old.remove_observer(self._observer_id) self.widgets.remove(old) new.on_trait_change(self.render) self._observer_id = new.add_observer(self.update_mode_, self._on_interaction_event) self.widgets.append(new) if len(self.inputs) > 0: new.input = self.inputs[0].output[0] def _filter_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) transform = self.transform if transform is not None: new.transform = transform if len(self.inputs) > 0: inp = self.inputs[0].outputs[0] new.input = inp self.outputs = [new.output] def _reset_fired(self): self._transform.identity() self.widget.place_widget() self.filter.update() self.render() def _update_mode_changed(self, old, new): w = self.widget if w is not None: w.remove_observer(self._observer_id) self._observer_id = w.add_observer(self.update_mode_, self._on_interaction_event) self.render() mayavi-4.1.0/mayavi/filters/contour.py0000644000175100001440000000225511674464502021001 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. # Enthought library imports. from traits.api import Instance # Local imports. from mayavi.components.contour import Contour as ContourComponent from mayavi.core.pipeline_info import PipelineInfo from mayavi.filters.wrapper import Wrapper ################################################################################ # `Contour` class. ################################################################################ class Contour(Wrapper): """ A contour filter that wraps around the Contour component to generate iso-surfaces on any input dataset. """ # The version of this class. Used for persistence. __version__ = 0 # The contour component this wraps. filter = Instance(ContourComponent, args=(), record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['point'], attributes=['any']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/warp_vector.py0000644000175100001440000000232011674464502021634 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.poly_data_normals import PolyDataNormals from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `WarpVector` class. ###################################################################### class WarpVector(PolyDataNormals): """Warps the input data along a the point vector attribute scaled as per a scale factor. Useful for showing flow profiles or displacements. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.WarpVector, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/extract_tensor_components.py0000644000175100001440000000256011674464502024620 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ExtractTensorComponents` class. ###################################################################### class ExtractTensorComponents(FilterBase): """Wraps the TVTK ExtractTensorComponents filter to extract components from a tensor field. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.ExtractTensorComponents, args=(), kw={'pass_tensors_to_output':True, 'scalar_mode': 'effective_stress', 'extract_scalars': True}, allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['tensors']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/triangle_filter.py0000644000175100001440000000243611674464502022463 0ustar ischnellusers00000000000000# Author: Robert Kern # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `TriangleFilter` class. ###################################################################### class TriangleFilter(FilterBase): """ Converts input polygons and triangle strips to triangles using the tvtk.TriangleFilter class. This is useful when you have a downstream filter that only processes triangles.""" # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.TriangleFilter, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data', 'unstructured_grid'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/filter_base.py0000644000175100001440000000634611674464502021574 0ustar ischnellusers00000000000000"""The base class for many filters. """ # Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.core.filter import Filter ###################################################################### # `FilterBase` class. ###################################################################### class FilterBase(Filter): """The base class for many of the filters. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.Object, allow_none=False, record=True) # The view of these filters. view = View(Group(Item(name='filter', style='custom', resizable=True, show_label=False), springy=True), scrollable=True, resizable=True ) ###################################################################### # `Filter` interface. ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* its tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. """ f = self.filter if f is not None: # Just hook up the filter so the update_data method is # called when the traits change. f.on_trait_change(self.update_data) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ # Do nothing if there is no input. inputs = self.inputs fil = self.filter if len(inputs) == 0 or fil is None: return # By default we set the input to the first output of the first # input. fil.input = inputs[0].outputs[0] fil.update() self._set_outputs([fil.output]) def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Do nothing if there is no input and we aren't running. if len(self.inputs) == 0 or not self.running: return self.filter.update() # Propagate the data_changed event. self.data_changed = True def _filter_changed(self, old, new): if old is not None: old.on_trait_change(self.update_data, remove=True) new.on_trait_change(self.update_data) if old is not None: self.update_pipeline() mayavi-4.1.0/mayavi/filters/extract_vector_norm.py0000644000175100001440000000572411674464502023403 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ExtractVectorNorm` class. ###################################################################### class ExtractVectorNorm(FilterBase): """Computes the norm (Eucliedean) of the input vector data (with optional scaling between [0, 1]). This is useful when the input data has vector input but no scalar data for the magnitude of the vectors. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.VectorNorm, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ###################################################################### # `Filter` interface. ###################################################################### def update_pipeline(self): # Do nothing if there is no input. inputs = self.inputs if len(inputs) == 0: return # By default we set the input to the first output of the first # input. fil = self.filter fil.input = inputs[0].outputs[0] fil.update() self._set_array_name(fil) self._set_outputs([fil.output]) def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Do nothing if there is no input. inputs = self.inputs if len(inputs) == 0: return self.filter.update() self._set_array_name(self.filter) # Propagate the data_changed event. self.data_changed = True ###################################################################### # Non-public interface. ###################################################################### def _set_array_name(self, filter): # Do nothing if there is no input. if len(self.inputs) == 0: return o = filter.output pd = o.point_data ps = pd.scalars cd = o.cell_data cs = cd.scalars if (ps is not None) and (not ps.name): ps.name = pd.vectors.name + ' magnitude' elif (cs is not None) and (not cs.name): cs.name = cd.vectors.name + ' magnitude' mayavi-4.1.0/mayavi/filters/user_defined.py0000644000175100001440000000601211674464502021737 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from tvtk.tools.tvtk_doc import TVTKFilterChooser, TVTK_FILTERS # Local imports. from mayavi.filters.filter_base import FilterBase from mayavi.core.common import handle_children_state, error from mayavi.core.pipeline_info import PipelineInfo ################################################################################ # `UserDefined` class. ################################################################################ class UserDefined(FilterBase): """ This filter lets the user define their own filter dynamically/interactively. It is like `FilterBase` but allows a user to specify the class without writing any code. """ # The version of this class. Used for persistence. __version__ = 0 input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ###################################################################### # `object` interface. ###################################################################### def __set_pure_state__(self, state): # Create and set the filter. children = [f for f in [self.filter] if f is not None] handle_children_state(children, [state.filter]) self.filter = children[0] self.update_pipeline() # Restore our state. super(UserDefined, self).__set_pure_state__(state) ###################################################################### # `UserDefined` interface. ###################################################################### def setup_filter(self): """Setup the filter if none has been set or check it if it already has been.""" obj = self.filter if not self._check_object(obj): if obj is not None: cname = obj.__class__.__name__ error('Invalid filter %s chosen! Try again!'%cname) obj = self._choose_filter() self.filter = obj ###################################################################### # Non-public interface. ###################################################################### def _choose_filter(self): chooser = TVTKFilterChooser() chooser.edit_traits(kind='livemodal') obj = chooser.object if obj is None: error('Invalid filter chosen! Try again!') return obj def _check_object(self, obj): if obj is None: return False if obj.__class__.__name__ in TVTK_FILTERS: return True return False def _filter_changed(self, old, new): self.name = 'UserDefined:%s'%new.__class__.__name__ super(UserDefined, self)._filter_changed(old, new) mayavi-4.1.0/mayavi/filters/extract_vector_components.py0000644000175100001440000000513411674464502024610 0ustar ischnellusers00000000000000# Author: Varun Hiremath # Enthought library imports. from traits.api import Instance, Enum from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ExtractVectorComponents` class. ###################################################################### class ExtractVectorComponents(FilterBase): """ This wraps the TVTK ExtractVectorComponents filter and allows one to select any of the three components of an input vector data attribute.""" # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.ExtractVectorComponents, args=(), allow_none=False) # The Vector Component to be extracted component = Enum('x-component', 'y-component', 'z-component', desc='component of the vector to be extracted') input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) view = View(Group(Item(name='component')), resizable=True ) ###################################################################### # `Filter` interface. ###################################################################### def update_pipeline(self): # Do nothing if there is no input. inputs = self.inputs if len(inputs) == 0: return fil = self.filter fil.input = inputs[0].outputs[0] fil.update() self._component_changed(self.component) ###################################################################### # Non-public interface. ###################################################################### def _component_changed(self, value): # Obtain output from the TVTK ExtractVectorComponents filter # corresponding to the selected vector component if len(self.inputs) == 0: return if value == 'x-component': self._set_outputs([self.filter.vx_component]) elif value == 'y-component': self._set_outputs([self.filter.vy_component]) elif value == 'z-component': self._set_outputs([self.filter.vz_component]) self.render() mayavi-4.1.0/mayavi/filters/poly_data_filter_base.py0000644000175100001440000000217211674464502023621 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.components.common import convert_to_poly_data ###################################################################### # `PolyDataFilterBase` class. ###################################################################### class PolyDataFilterBase(FilterBase): """ Base class for a filter requiring polydata input. Converts the source to polydata. """ ###################################################################### # `Filter` interface. ###################################################################### def update_pipeline(self): # Do nothing if there is no input. inputs = self.inputs if len(inputs) == 0: return # By default we set the input to the first output of the first # input. fil = self.filter fil.input = convert_to_poly_data(inputs[0].outputs[0]) fil.update() self._set_outputs([fil.output]) mayavi-4.1.0/mayavi/filters/cell_to_point_data.py0000644000175100001440000000434511674464502023135 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `CellToPointData` class. ###################################################################### class CellToPointData(FilterBase): """Transforms cell attribute data to point data by averaging the cell data from the cells at the point. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.CellDataToPointData, args=(), kw={'pass_cell_data':1}, allow_none=False, record=True) # Information about what this object can consume/produce. input_info = PipelineInfo(datasets=['any'], attribute_types=['cell'], attributes=['any']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) def update_pipeline(self): # Do nothing if there is no input. inputs = self.inputs if len(inputs) == 0: return fil = self.filter input = inputs[0].outputs[0] fil.input = input fil.update() # This filter creates different outputs depending on the # input. out_map = {'vtkStructuredGrid': 'structured_grid_output', 'vtkRectilinearGrid': 'rectilinear_grid_output', 'vtkStructuredPoints': 'structured_points_output', 'vtkUnstructuredGrid': 'unstructured_grid_output', 'vtkPolyData': 'poly_data_output', 'vtkImageData': 'image_data_output'} # Find the input data type and pass that to our output.. for type in out_map: if input.is_a(type): self._set_outputs([getattr(fil, out_map[type])]) break mayavi-4.1.0/mayavi/filters/elevation_filter.py0000644000175100001440000000217411674464502022643 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ElevationFilter` class. ###################################################################### class ElevationFilter(FilterBase): """ Generate scalar data from the elevation in a given direction """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.ElevationFilter, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/metadata.py0000644000175100001440000006362211674464502021075 0ustar ischnellusers00000000000000""" Metadata for all filters. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran Enthought, Inc. # License: BSD Style. # Local imports. from mayavi.core.metadata import FilterMetadata from mayavi.core.pipeline_info import PipelineInfo BASE = 'mayavi.filters' ################################################################################ # Factory functions. ################################################################################ def make_user_defined_filter(): from mayavi.filters.user_defined import UserDefined f = UserDefined() f.setup_filter() return f ################################################################################ # Metadata. cell_derivatives_filter = FilterMetadata( id = "CellDerivativesFilter", menu_name = "&CellDerivatives", class_name = BASE + '.cell_derivatives.CellDerivatives', tooltip = "Calculate derivatives of input point/vector data and output these as cell data", desc = "Calculate derivatives of input point/vector data and output these as cell data", help = "Calculate derivatives of input point/vector data and output these as cell data", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) cell_to_point_data_filter = FilterMetadata( id = "CellToPointDataFilter", menu_name = "&CellToPointData", class_name = BASE + '.cell_to_point_data.CellToPointData', tooltip = "Convert cell data to point data for the active data", desc = "Convert cell data to point data for the active data", help = "Convert cell data to point data for the active data", input_info = PipelineInfo(datasets=['any'], attribute_types=['cell'], attributes=['any']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) clip_filter = FilterMetadata( id = "DataSetClipperFilter", menu_name = "&DataSet Clipper", class_name = BASE + '.data_set_clipper.DataSetClipper', tooltip = "Clip the input dataset", desc = "Clip the input dataset", help = "Clip the input dataset", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['any']) ) contour_filter = FilterMetadata( id = "ContourFilter", menu_name = "&Contour", class_name = BASE + '.contour.Contour', tooltip = "Compute contours of the input dataset", desc = "Compute contours of the input dataset", help = "Compute contours of the input dataset", input_info = PipelineInfo(datasets=['any'], attribute_types=['point'], attributes=['any']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) cut_plane_filter = FilterMetadata( id = "CutPlaneFilter", menu_name = "&CutPlane", class_name = BASE + '.cut_plane.CutPlane', tooltip = "Slice the input dataset with a cut plane", desc = "Slice the input dataset with a cut plane", help = "Slice the input dataset with a cut plane", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) decimatepro_filter = FilterMetadata( id = "DecimateProFilter", menu_name = "&DecimatePro", class_name = BASE + '.decimatepro.DecimatePro', tooltip = "Simpilies a mesh using the DecimatePro filter", desc = "Simpilies a mesh using the DecimatePro filter", help = "Simpilies a mesh using the DecimatePro filter", input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) delaunay2d_filter = FilterMetadata( id = "Delaunay2DFilter", menu_name = "&Delaunay2D", class_name = BASE + '.delaunay2d.Delaunay2D', tooltip = "Perform a 2D Delaunay triangulation for the given data", desc = "Perform a 2D Delaunay triangulation for the given data", help = "Perform a 2D Delaunay triangulation for the given data", input_info = PipelineInfo(datasets=['structured_grid', 'poly_data', 'unstructured_grid'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) delaunay3d_filter = FilterMetadata( id = "Delaunay3DFilter", menu_name = "Delaunay&3D", class_name = BASE + '.delaunay3d.Delaunay3D', tooltip = "Perform a 3D Delaunay triangulation for the given data", desc = "Perform a 3D Delaunay triangulation for the given data", help = "Perform a 3D Delaunay triangulation for the given data", input_info = PipelineInfo(datasets=['structured_grid', 'poly_data', 'unstructured_grid'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['any']) ) elevation_filter = FilterMetadata( id = "ElevationFilter", menu_name = "Elevation Filter", class_name = BASE + '.elevation_filter.ElevationFilter', tooltip = "Creates scalar data from the elevation along a direction", desc = "Creates scalar data from the elevation along a direction", help = "Creates scalar data from the elevation along a direction", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) extract_edges_filter = FilterMetadata( id = "ExtractEdgesFilter", menu_name = "Extract Edges", class_name = BASE + '.extract_edges.ExtractEdges', tooltip = "Turns edges into lines.", desc = "Turns edges into lines.", help = "Turns edges into lines.", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) extract_grid_filter = FilterMetadata( id = "ExtractGridFilter", menu_name = "Extract &Grid", class_name = BASE + '.extract_grid.ExtractGrid', tooltip = "Extract/subsample part of any structured grid", desc = "Extract/subsample part of any structured grid", help = "Extract/subsample part of any structured grid", input_info = PipelineInfo(datasets=['image_data', 'rectilinear_grid', 'structured_grid'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['image_data', 'rectilinear_grid', 'structured_grid'], attribute_types=['any'], attributes=['any']) ) extract_tensor_components_filter = FilterMetadata( id = "ExtractTensorComponentsFilter", menu_name = "Extract &Tensor Components", class_name = BASE + '.extract_tensor_components.ExtractTensorComponents', tooltip = "Extract tensor components from tensor data", desc = "Extract tensor components from tensor data", help = "Extract tensor components from tensor data", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['tensors']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) extract_unstructured_grid_filter = FilterMetadata( id = "ExtractUnstructuredGridFilter", menu_name = "Extract &Unstructured Grid", class_name = BASE + '.extract_unstructured_grid.ExtractUnstructuredGrid', tooltip = "Extract part of an unstructured grid", desc = "Extract part of an unstructured grid", help = "Extract part of an unstructured grid", input_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['any']) ) extract_vector_norm_filter = FilterMetadata( id = "ExtractVectorNormFilter", menu_name = "Extract Vector &Norm", class_name = BASE + '.extract_vector_norm.ExtractVectorNorm', tooltip = "Compute the vector norm for the current vector data", desc = "Compute the vector norm for the current vector data", help = "Compute the vector norm for the current vector data", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) extract_vector_components_filter = FilterMetadata( id = "ExtractVectorComponentsFilter", menu_name = "Extract &Vector Components", class_name = BASE + '.extract_vector_components.ExtractVectorComponents', tooltip = "Extract vector components from vector data", desc = "Extract vector components from vector data", help = "Extract vector components from vector data", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) gaussian_splatter_filter = FilterMetadata( id = "GaussianSplatterFilter", menu_name = "Gaussian Splatter", class_name = BASE + '.gaussian_splatter.GaussianSplatter', tooltip = "Builds a structured set of points from a cloud of points, the local density defining the scalar", desc = "Builds a structured set of points from a cloud of points, the local density defining the scalar", help = """Builds a structured set of points from a cloud of points, the local density defining the scalar. It is essentially equivalent to a 3D Gaussian kernel density estimate.""", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) ) greedy_terrain_decimation_filter = FilterMetadata( id = "GreedyTerrainDecimationFilter", menu_name = "Greedy Terrain Decimation", class_name = BASE + '.greedy_terrain_decimation.GreedyTerrainDecimation', tooltip = "Simplifies image data and performs a triangulation", desc = "Simplifies image data and performs a triangulation", help = "Simplifies image data and performs a triangulation", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) image_change_information_filter = FilterMetadata( id = "ImageChangeInformationFilter", menu_name = "Change &ImageData information", class_name = BASE + '.image_change_information.ImageChangeInformation', tooltip = "Change the origin, spacing and extents of an image dataset", desc = "Change the origin, spacing and extents of an image dataset", help = "Change the origin, spacing and extents of an image dataset", input_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) ) image_data_probe_filter = FilterMetadata( id = "ImageDataProbeFilter", menu_name = "&Probe data onto image data", class_name = BASE + '.image_data_probe.ImageDataProbe', tooltip = "Samples arbitrary datasets onto an image dataset (cube of data)", desc = "Samples arbitrary datasets onto an image dataset (cube of data)", help = "Samples arbitrary datasets onto an image dataset (cube of data)", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) ) mask_points_filter = FilterMetadata( id = "MaskPointsFilter", menu_name = "&Mask Points", class_name = BASE + '.mask_points.MaskPoints', tooltip = "Mask the input points in the data", desc = "Mask the input points in the data", help = "Mask the input points in the data", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) point_to_cell_data_filter = FilterMetadata( id = "PointToCellDataFilter", menu_name = "&PointToCellData", class_name = BASE + '.point_to_cell_data.PointToCellData', tooltip = "Convert point data to cell data for the active data", desc = "Convert point data to cell data for the active data", help = "Convert point data to cell data for the active data", input_info = PipelineInfo(datasets=['any'], attribute_types=['point'], attributes=['any']), output_info = PipelineInfo(datasets=['any'], attribute_types=['cell'], attributes=['any']) ) poly_data_normals_filter = FilterMetadata( id = "PolyDataNormalsFilter", menu_name = "Compute &Normals", class_name = BASE + '.poly_data_normals.PolyDataNormals', tooltip = "Compute normals and smooth the appearance", desc = "Compute normals and smooth the appearance", help = "Compute normals and smooth the appearance", input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) quadric_decimation_filter = FilterMetadata( id = "QuadricDecimationFilter", menu_name = "Quadric Decimation", class_name = BASE + '.quadric_decimation.QuadricDecimation', tooltip = "Simplifies a triangular mesh", desc = "Simplifies a triangular mesh", help = "Simplifies a triangular mesh", input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) select_output_filter = FilterMetadata( id = "SelectOutputFilter", menu_name = "&Select Output", class_name = BASE + '.select_output.SelectOutput', tooltip = "Choose the output of the source that should be used", desc = "Choose the output of the source that should be used", help = "Choose the output of the source that should be used", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) set_active_attribute_filter = FilterMetadata( id = "SetActiveAttributeFilter", menu_name = "&SetActiveAttribute", class_name = BASE + '.set_active_attribute.SetActiveAttribute', tooltip = "Set the active attribute (scalar/vector/tensor) to use", desc = "Set the active attribute (scalar/vector/tensor) to use", help = "Set the active attribute (scalar/vector/tensor) to use", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) stripper = FilterMetadata( id = "Stripper", menu_name = "Stripper", class_name = BASE + '.stripper.Stripper', tooltip = "Regularizes surfaces by creating triangle strips", desc = "Regularizes surfaces by creating triangle strips", help = "Regularizes surfaces by creating triangle strips", input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) transform_data_filter = FilterMetadata( id = "TransformDataFilter", menu_name = "T&ransform Data", class_name = BASE + '.transform_data.TransformData', tooltip = "Transform (rotate/translate/scale) non ImageData datasets", desc = "Transform (rotate/translate/scale) non ImageData datasets", help = "Transform (rotate/translate/scale) non ImageData datasets", input_info = PipelineInfo(datasets=['poly_data', 'structured_grid', 'unstructured_grid'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data', 'structured_grid', 'unstructured_grid'], attribute_types=['any'], attributes=['any']) ) threshold_filter = FilterMetadata( id = "ThresholdFilter", menu_name = "&Threshold", class_name = BASE + '.threshold.Threshold', tooltip = "Threshold input data based on scalar values", desc = "Threshold input data based on scalar values", help = "Threshold input data based on scalar values", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data', 'unstructured_grid'], attribute_types=['any'], attributes=['any']) ) triangle_filter = FilterMetadata( id = "TriangleFilterFilter", menu_name = "TriangleFilter", class_name = BASE + '.triangle_filter.TriangleFilter', tooltip = "Convert input polygons and triangle strips to triangles", desc = "Convert input polygons and triangle strips to triangles", help = "Convert input polygons and triangle strips to triangles", input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) tube_filter = FilterMetadata( id = "TubeFilter", menu_name = "Tu&be", class_name = BASE + '.tube.Tube', tooltip = "Turns lines into tubes", desc = "Turns lines into tubes", help = "Turns lines into tubes", input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ) user_defined_filter = FilterMetadata( id = "UserDefinedFilter", menu_name = "&UserDefined", factory = make_user_defined_filter, tooltip = "Create a UserDefined filter (will popup a selection dialog)", desc = "Create a UserDefined filter (will popup a selection dialog)", help = "Create a UserDefined filter (will popup a selection dialog)", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) vorticity_filter = FilterMetadata( id = "VorticityFilter", menu_name = "&Vorticity", class_name = BASE + '.vorticity.Vorticity', tooltip = "Calculate the vorticity (curl) of input vector field", desc = "Calculate the vorticity (curl) of input vector field", help = "Calculate the vorticity (curl) of input vector field", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) warp_scalar_filter = FilterMetadata( id = "WarpScalarFilter", menu_name = "Warp S&calar", class_name = BASE + '.warp_scalar.WarpScalar', tooltip = "Move points of data along normals by the scalar data", desc = "Move points of data along normals by the scalar data", help = "Move points of data along normals by the scalar data", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['scalars']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) warp_vector_filter = FilterMetadata( id = "WarpVectorFilter", menu_name = "Warp &Vector", class_name = BASE + '.warp_vector.WarpVector', tooltip = "Move points of data along the vector data at point", desc = "Move points of data along the vector data at point", help = "Move points of data along the vector data at point", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']), output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) # Now collect all the filters for the mayavi registry. filters = [cell_derivatives_filter, cell_to_point_data_filter, clip_filter, contour_filter, cut_plane_filter, decimatepro_filter, delaunay2d_filter, delaunay3d_filter, elevation_filter, extract_edges_filter, extract_grid_filter, extract_tensor_components_filter, extract_unstructured_grid_filter, extract_vector_norm_filter, extract_vector_components_filter, gaussian_splatter_filter, greedy_terrain_decimation_filter, image_change_information_filter, image_data_probe_filter, mask_points_filter, point_to_cell_data_filter, poly_data_normals_filter, quadric_decimation_filter, select_output_filter, set_active_attribute_filter, stripper, transform_data_filter, threshold_filter, triangle_filter, tube_filter, user_defined_filter, vorticity_filter, warp_scalar_filter, warp_vector_filter, ] mayavi-4.1.0/mayavi/filters/cut_plane.py0000644000175100001440000000273411674464502021264 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran # License: BSD Style. # Local imports. from mayavi.components.cutter import Cutter from mayavi.components.implicit_plane import ImplicitPlane from mayavi.filters.collection import Collection from mayavi.core.pipeline_info import PipelineInfo ################################################################################ # `CutPlane` class. ################################################################################ class CutPlane(Collection): """ This class represents a cut plane that can be used to slice through any dataset. It also provides a 3D widget interface to position and move the slice interactively. """ # The version of this class. Used for persistence. __version__ = 0 input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ###################################################################### # `Filter` interface. ###################################################################### def setup_pipeline(self): """Creates the pipeline.""" ip = ImplicitPlane() cut = Cutter(cut_function=ip.plane) self.filters = [ip, cut] mayavi-4.1.0/mayavi/filters/threshold.py0000644000175100001440000002270011674464502021301 0ustar ischnellusers00000000000000"""A simple filter that thresholds on input data. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # Author: Gael Varoquaux # Copyright (c) 2010, Enthought, Inc. # License: BSD Style. import numpy as np # Enthought library imports. from traits.api import Instance, Range, Float, Bool, \ Property, Enum from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.core.filter import Filter from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `Threshold` class. ###################################################################### class Threshold(Filter): # The version of this class. Used for persistence. __version__ = 0 # The threshold filter used. threshold_filter = Property(Instance(tvtk.Object, allow_none=False), record=True) # The filter type to use, specifies if the cells or the points are # cells filtered via a threshold. filter_type = Enum('cells', 'points', desc='if thresholding is done on cells or points') # Lower threshold (this is a dynamic trait that is changed when # input data changes). lower_threshold = Range(value=-1.0e20, low='_data_min', high='_data_max', enter_set=True, auto_set=False, desc='the lower threshold of the filter') # Upper threshold (this is a dynamic trait that is changed when # input data changes). upper_threshold = Range(value=1.0e20, low='_data_min', high='_data_max', enter_set=True, auto_set=False, desc='the upper threshold of the filter') # Automatically reset the lower threshold when the upstream data # changes. auto_reset_lower = Bool(True, desc='if the lower threshold is ' 'automatically reset when upstream ' 'data changes') # Automatically reset the upper threshold when the upstream data # changes. auto_reset_upper = Bool(True, desc='if the upper threshold is ' 'automatically reset when upstream ' 'data changes') input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data', 'unstructured_grid'], attribute_types=['any'], attributes=['any']) # Our view. view = View(Group(Group(Item(name='filter_type'), Item(name='lower_threshold'), Item(name='auto_reset_lower'), Item(name='upper_threshold'), Item(name='auto_reset_upper')), Item(name='_'), Group(Item(name='threshold_filter', show_label=False, visible_when='object.filter_type == "cells"', style='custom', resizable=True)), ), resizable=True ) ######################################## # Private traits. # These traits are used to set the limits for the thresholding. # They store the minimum and maximum values of the input data. _data_min = Float(-1e20) _data_max = Float(1e20) # The threshold filter for cell based filtering _threshold = Instance(tvtk.Threshold, args=(), allow_none=False) # The threshold filter for points based filtering. _threshold_points = Instance(tvtk.ThresholdPoints, args=(), allow_none=False) # Internal data to _first = Bool(True) ###################################################################### # `object` interface. ###################################################################### def __get_pure_state__(self): d = super(Threshold, self).__get_pure_state__() # These traits are dynamically created. for name in ('_first', '_data_min', '_data_max'): d.pop(name, None) return d ###################################################################### # `Filter` interface. ###################################################################### def setup_pipeline(self): attrs = ['all_scalars', 'attribute_mode', 'component_mode', 'selected_component'] self._threshold.on_trait_change(self._threshold_filter_edited, attrs) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ if len(self.inputs) == 0: return # By default we set the input to the first output of the first # input. fil = self.threshold_filter fil.input = self.inputs[0].outputs[0] self._update_ranges() self._set_outputs([self.threshold_filter.output]) def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ if len(self.inputs) == 0: return self._update_ranges() # Propagate the data_changed event. self.data_changed = True ###################################################################### # Non-public interface ###################################################################### def _lower_threshold_changed(self, new_value): fil = self.threshold_filter fil.threshold_between(new_value, self.upper_threshold) fil.update() self.data_changed = True def _upper_threshold_changed(self, new_value): fil = self.threshold_filter fil.threshold_between(self.lower_threshold, new_value) fil.update() self.data_changed = True def _update_ranges(self): """Updates the ranges of the input. """ data_range = self._get_data_range() if len(data_range) > 0: dr = data_range if self._first: self._data_min, self._data_max = dr self.set(lower_threshold = dr[0], trait_change_notify=False) self.upper_threshold = dr[1] self._first = False else: if self.auto_reset_lower: self._data_min = dr[0] notify = not self.auto_reset_upper self.set(lower_threshold = dr[0], trait_change_notify=notify) if self.auto_reset_upper: self._data_max = dr[1] self.upper_threshold = dr[1] def _get_data_range(self): """Returns the range of the input scalar data.""" input = self.inputs[0].outputs[0] data_range = [] ps = input.point_data.scalars cs = input.cell_data.scalars # FIXME: need to be able to handle cell and point data # together. if ps is not None: data_range = list(ps.range) if np.isnan(data_range[0]): data_range[0] = float(np.nanmin(ps.to_array())) if np.isnan(data_range[1]): data_range[1] = float(np.nanmax(ps.to_array())) elif cs is not None: data_range = cs.range if np.isnan(data_range[0]): data_range[0] = float(np.nanmin(cs.to_array())) if np.isnan(data_range[1]): data_range[1] = float(np.nanmax(cs.to_array())) return data_range def _auto_reset_lower_changed(self, value): if len(self.inputs) == 0: return if value: dr = self._get_data_range() self._data_min = dr[0] self.lower_threshold = dr[0] def _auto_reset_upper_changed(self, value): if len(self.inputs) == 0: return if value: dr = self._get_data_range() self._data_max = dr[1] self.upper_threshold = dr[1] def _get_threshold_filter(self): if self.filter_type == 'cells': return self._threshold else: return self._threshold_points def _filter_type_changed(self, value): if value == 'cells': old = self._threshold_points new = self._threshold else: old = self._threshold new = self._threshold_points self.trait_property_changed('threshold_filter', old, new) def _threshold_filter_changed(self, old, new): if len(self.inputs) == 0: return fil = new fil.input = self.inputs[0].outputs[0] fil.threshold_between(self.lower_threshold, self.upper_threshold) fil.update() self._set_outputs([fil.output]) def _threshold_filter_edited(self): self.threshold_filter.update() self.data_changed = True mayavi-4.1.0/mayavi/filters/cell_derivatives.py0000644000175100001440000000255511674464502022637 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `CellDerivatives` class. ###################################################################### class CellDerivatives(FilterBase): """Computes derivatives from input point scalar and vector data and produces cell data on the gradients. Can be used to approximately calcuate the vorticity for example. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.CellDerivatives, args=(), allow_none=False, record=True) # Information about what this object can consume. input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) # Information about what this object can produce. output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/__init__.py0000644000175100001440000000013211674464502021037 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/filters/collection.py0000644000175100001440000001515311674464502021444 0ustar ischnellusers00000000000000"""Defines a Collection filter which is a collection of mayavi filters/components bundled into one. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool, List from traitsui.api import Item, Group, View, ListEditor # Local imports. from mayavi.core.pipeline_base import PipelineBase from mayavi.core.filter import Filter from mayavi.core.common import handle_children_state ################################################################################ # `Collection` class. ################################################################################ class Collection(Filter): """ Defines a Collection filter which is a collection of mayavi filters/components bundled into one. """ # The filters we manage. filters = List(Instance(PipelineBase), record=True) ######################################## # Private traits. # Is the pipeline ready? Used internally. _pipeline_ready = Bool(False) ###################################################################### # `object` interface. ###################################################################### def __set_pure_state__(self, state): # Create and set the filters. handle_children_state(self.filters, state.filters) # Restore our state using the super class method. super(Collection, self).__set_pure_state__(state) ###################################################################### # HasTraits interface. ###################################################################### def default_traits_view(self): """Returns the default traits view for this object.""" le = ListEditor(use_notebook=True, deletable=False, export='DockWindowShell', page_name='.name') view = View(Group(Item(name='filters', style='custom', show_label=False, editor=le, resizable=True), show_labels=False), resizable=True) return view ###################################################################### # `Filter` interface. ###################################################################### def setup_pipeline(self): """Setup the pipeline.""" # Needed because a user may have defined the filters by setting # the default value of the trait in the subclass in which case # the filters_changed handler will never be called leading to # problems. if len(self.filters) > 0 and not self._pipeline_ready: self._filters_changed([], self.filters) def stop(self): # There is no need to override start since the wrapped filters # are always started automatically in the filters_changed # handler. super(Collection, self).stop() for filter in self.filters: filter.stop() def update_pipeline(self): """This method *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ self._setup_pipeline() # Propagate the event. self.pipeline_changed = True def update_data(self): """This method does what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Propagate the data_changed event. self.data_changed = True ###################################################################### # Private interface. ###################################################################### def _setup_pipeline(self): """Sets up the objects in the pipeline.""" if len(self.inputs) == 0 or len(self.filters) == 0: return # Our input. my_input = self.inputs[0] filters = self.filters if not self._pipeline_ready: # Hook up our first filter. first = self.filters[0] first.inputs = [my_input] # Hook up the others to each other. for i in range(1, len(filters)): filter = filters[i] filter.inputs = [filters[i-1]] self._pipeline_ready = True # Start filters. for filter in filters: filter.start() # Set our outputs last = filters[-1] self._set_outputs(last.outputs) def _filters_changed(self, old, new): """Static traits handler.""" self._handle_filters_changed(old, new) def _filters_items_changed(self, list_event): """Static traits handler.""" self._handle_filters_changed(list_event.removed, list_event.added) def _scene_changed(self, old, new): """Static traits handler.""" for filter in self.filters: filter.scene = new super(Collection, self)._scene_changed(old, new) def _handle_filters_changed(self, removed, added): for filter in removed: self._setup_events(filter, remove=True) filter.stop() for filter in added: if self.scene is not None: filter.scene = self.scene if len(filter.name) == 0: filter.name = filter.__class__.__name__ if filter is self.filters[-1]: self._setup_events(filter) self._pipeline_ready = False self._setup_pipeline() def _fire_pipeline_changed(self): # When the last filter fires a pipeline changed we should reset # our outputs to that of its outputs. Calling _setup_pipeline # is expensive and will cause a recursion error. self._set_outputs(self.filters[-1].outputs) def _setup_events(self, obj, remove=False): obj.on_trait_change(self.update_data, 'data_changed', remove=remove) obj.on_trait_change(self._fire_pipeline_changed, 'pipeline_changed', remove=remove) def _visible_changed(self, value): for filter in self.filters: filter.visible = value super(Collection, self)._visible_changed(value) def _recorder_changed(self, old, new): super(Collection, self)._recorder_changed(old, new) for filter in self.filters: filter.recorder = new mayavi-4.1.0/mayavi/filters/decimatepro.py0000644000175100001440000000222311674464502021577 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `DecimatePro` class. ###################################################################### class DecimatePro(FilterBase): """ Reduces the number of triangles in a mesh using the tvtk.DecimatePro class. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.DecimatePro, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/wrapper.py0000644000175100001440000001500711674464502020767 0ustar ischnellusers00000000000000"""The `Wrapper` filter is one which wraps around any mayavi filter or component. By default it does not allow the user to set it on and off from the UI, for that see the `Optional` filter. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool, Str from traitsui.api import Item, Group, View from apptools.persistence import state_pickler # Local imports. from mayavi.core.pipeline_base import PipelineBase from mayavi.core.filter import Filter from mayavi.core.common import handle_children_state ################################################################################ # `Wrapper` class. ################################################################################ class Wrapper(Filter): """ The `Wrapper` filter is one which wraps around any mayavi filter or component. By default it does not allow the user to set it on and off from the UI, for that see the `Optional` filter. """ # The filter we wrap. filter = Instance(PipelineBase, allow_none=False, record=True) # The text to show in the UI of form "Enable SomeFilter" label_text = Str('Enable Filter') # Are we enabled or not. enabled = Bool(True, desc='if the filter is enabled or not') ######################################## # Private traits. # Should we show enabled in the UI or not. This defaults to False, # the `Optional` filter merely changes this to True. This trait is # not meant for interactive changing. _show_enabled = Bool(False) ###################################################################### # `object` interface. ###################################################################### def __set_pure_state__(self, state): # Create and set the filter. children = [f for f in [self.filter] if f is not None] handle_children_state(children, [state.filter]) self.filter = children[0] # Restore our state. super(Wrapper, self).__set_pure_state__(state) ###################################################################### # HasTraits interface. ###################################################################### def default_traits_view(self): """Returns the default traits view for this object.""" if self._show_enabled: view = View(Group(Group(Item(name='enabled', label=self.label_text)), Group(Item(name='filter', style='custom', enabled_when='enabled', resizable=True), show_labels=False)), resizable=True) else: view = View(Group(Item(name='filter', style='custom', enabled_when='enabled', resizable=True), show_labels=False), resizable=True) return view ###################################################################### # `Filter` interface. ###################################################################### def setup_pipeline(self): """Setup the pipeline.""" # Needed because a user may have defined a filter by setting the # default value of the trait in the subclass in which case the # filter changed handler will never be called leading to # problems. if self.filter is not None: self._setup_events(self.filter) def stop(self): # There is no need to override start since the wrapped filter is # always started automatically in the _enabled_changed handler. super(Wrapper, self).stop() if self.filter is not None: self.filter.stop() def update_pipeline(self): """This method *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ self._enabled_changed(self.enabled) self.pipeline_changed = True def update_data(self): """This method does what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Propagate the data_changed event. self.data_changed = True ###################################################################### # Private interface. ###################################################################### def _enabled_changed(self, value): """Static traits handler.""" if len(self.inputs) == 0 or self.filter is None: return my_input = self.inputs[0] filter = self.filter if len(filter.name) == 0: name = filter.__class__.__name__ else: name = filter.name if value and filter is not None: filter.inputs = [my_input] if not filter.running: filter.start() self._set_outputs(self.filter.outputs) else: self._set_outputs(my_input.outputs) name += ' (disabled)' self.name = name self.render() def _filter_changed(self, old, new): """Static traits handler.""" if old is not None: self._setup_events(old, remove=True) old.stop() if self.scene is not None: new.scene = self.scene self._setup_events(new, remove=False) self._enabled_changed(self.enabled) def _scene_changed(self, old, new): """Static traits handler.""" if self.filter is not None: self.filter.scene = new super(Wrapper, self)._scene_changed(old, new) def _filter_pipeline_changed(self): if self.enabled: self._set_outputs(self.filter.outputs) def _setup_events(self, obj, remove=False): obj.on_trait_change(self._filter_pipeline_changed, 'pipeline_changed', remove=remove) obj.on_trait_change(self.update_data, 'data_changed', remove=remove) def _visible_changed(self, value): self.filter.visible = value super(Wrapper, self)._visible_changed(value) mayavi-4.1.0/mayavi/filters/greedy_terrain_decimation.py0000644000175100001440000000226011674464502024503 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `GreedyTerrainDecimation` class. ###################################################################### class GreedyTerrainDecimation(FilterBase): """ Performs a triangulation of image data after simplifying it. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.GreedyTerrainDecimation, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/tube.py0000644000175100001440000000207211674464502020244 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `Tube` class. ###################################################################### class Tube(FilterBase): """Turns lines into tubes. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.TubeFilter, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/filters/data_set_clipper.py0000644000175100001440000001415211674464502022611 0ustar ischnellusers00000000000000"""This filter enables one to clip a selection from an input dataset using various Implicit Widgets. """ # Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Standard library imports. import cPickle # Enthought library imports. from traits.api import Instance, Button, Delegate from traitsui.api import View, Group, Item from apptools.persistence import state_pickler from tvtk.api import tvtk # Local imports from mayavi.core.filter import Filter from mayavi.core.pipeline_info import PipelineInfo from mayavi.components.implicit_widgets import ImplicitWidgets ###################################################################### # `DataSetClipper` class. ###################################################################### class DataSetClipper(Filter): # The version of this class. Used for persistence. __version__ = 0 # The widgets to be used for the Clipping Filter. widget = Instance(ImplicitWidgets, allow_none=False, record=True) # The clipping filter. filter = Instance(tvtk.Object, allow_none=False, record=True) # The update mode of the widget-- this is delegated to the # ImplicitWidgets. update_mode = Delegate('widget', modify=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['any'], attributes=['any']) ######################################## # View related traits. # Button to reset the boundaries of the implicit_widget. reset_button = Button('Reset Boundaries') view = View(Group(Group(Item('update_mode'), ), Group(Item('reset_button'), Item(name='widget', style='custom', resizable=True), show_labels=False ), label='ImplicitWidget' ), Group(Group(Item('filter', style='custom'), show_labels=False), label='Clipper' ), resizable=True ) ######################################## # Private traits. _transform = Instance(tvtk.Transform, allow_none=False) ###################################################################### # `object` interface. ###################################################################### def __get_pure_state__(self): d = super(DataSetClipper, self).__get_pure_state__() for name in ('_first', '_observer_id'): d.pop(name, None) d['matrix'] = cPickle.dumps(self._transform.matrix) return d def __set_pure_state__(self, state): mat = state.pop('matrix') super(DataSetClipper, self).__set_pure_state__(state) state_pickler.set_state(self, state) self._transform.set_matrix(cPickle.loads(mat)) self.widget.set_transform(self._transform) ###################################################################### # `Filter` interface ###################################################################### def setup_pipeline(self): self.widget = ImplicitWidgets() self._transform = tvtk.Transform() self.filter = tvtk.ClipDataSet() self.widget.on_trait_change(self._handle_widget, 'widget') super(DataSetClipper, self).setup_pipeline() def update_pipeline(self): inputs = self.inputs if len(inputs) == 0: return widget = self.widget widget.inputs = inputs widget.update_pipeline() filter = self.filter filter.input = inputs[0].outputs[0] widget.update_implicit_function() filter.clip_function = widget.implicit_function filter.update() self._set_outputs([filter.output]) self.pipeline_changed = True def update_data(self): # Do nothing if there is no input. if len(self.inputs) == 0: return self.filter.update() # Propagate the data_changed event. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _on_interaction_event(self, obj, event): tfm = self._transform self.widget.widget.get_transform(tfm) recorder = self.recorder if recorder is not None: state = {} state['elements'] = tfm.matrix.__getstate__()['elements'] name = recorder.get_script_id(self) recorder.record('%s._transform.matrix.__setstate__(%s)'\ %(name, state)) recorder.record('%s.widget.widget.set_transform(%s._transform)'\ %(name, name)) recorder.record('%s.widget.update_implicit_function()' % name) recorder.record('%s.render()' % name) def _widget_changed(self, old, new): self.widgets = self.widget.widgets if len(self.inputs) > 0: new.inputs = self.inputs new.update_pipeline() self._observer_id = new.widget.add_observer(self.update_mode_, self._on_interaction_event) def _filter_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) if len(self.inputs) > 0: inp = self.inputs[0].outputs[0] new.input = inp self.outputs = [new.output] def _reset_button_fired(self): self.widget.widget.place_widget() self.widget.update_implicit_function() self.filter.update() self.render() def _handle_widget(self, value): self.widgets = self.widget.widgets f = self.filter f.clip_function = self.widget.implicit_function f.update() self.update_pipeline() mayavi-4.1.0/mayavi/filters/stripper.py0000644000175100001440000000227111674464502021156 0ustar ischnellusers00000000000000# Author: Gael Varoquaux # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports from mayavi.filters.filter_base import FilterBase from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `Stripper` class. ###################################################################### class Stripper(FilterBase): """ Create triangle strips and/or poly-lines. Useful for regularizing broken up surfaces, such as those created by the Tube filter. """ # The version of this class. Used for persistence. __version__ = 0 # The actual TVTK filter that this class manages. filter = Instance(tvtk.Stripper, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) mayavi-4.1.0/mayavi/__version__.py0000644000175100001440000000031511674464502020114 0ustar ischnellusers00000000000000"""Version information for MayaVi2. """ # Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. from mayavi.version import version as __version__ mayavi-4.1.0/mayavi/components/0000755000175100001440000000000011674464502017447 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/components/source_widget.py0000644000175100001440000002673711674464502022703 0ustar ischnellusers00000000000000"""A component that provides a selection of poly data source widgets to be used by various modules. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, List, Trait, Bool, TraitPrefixList from traitsui.api import View, Group, Item, InstanceEditor from tvtk.api import tvtk from apptools.persistence.state_pickler import set_state # Local imports. from mayavi.core.common import handle_children_state from mayavi.core.component import Component ###################################################################### # `SourceWidget` class. ###################################################################### class SourceWidget(Component): # The version of this class. Used for persistence. __version__ = 0 # The actual poly data source widget. widget = Instance(tvtk.ThreeDWidget, record=True) # Specifies the updation mode of the poly_data attribute. There # are three modes: 1) 'interactive' -- the poly_data attribute is # updated as the widget is interacted with, 2) 'semi-interactive' # -- poly_data attribute is updated when the traits of the widget # change and when the widget interaction is complete, 3) # 'non-interactive' -- poly_data is updated only explicitly at # users request by calling `object.update_poly_data`. update_mode = Trait('interactive', TraitPrefixList(['interactive', 'semi-interactive', 'non-interactive']), desc='the speed at which the poly data is updated') # A list of predefined glyph sources that can be used. widget_list = List(tvtk.Object, record=False) # The poly data that the widget manages. poly_data = Instance(tvtk.PolyData, args=()) ######################################## # Private traits. _first = Bool(True) _busy = Bool(False) _unpickling = Bool(False) ######################################## # View related traits. view = View(Group(Item(name='widget', style='custom', resizable=True, editor=InstanceEditor(name='widget_list')), label='Source Widget', show_labels=False, ), resizable=True, ) ###################################################################### # `Base` interface ###################################################################### def __get_pure_state__(self): d = super(SourceWidget, self).__get_pure_state__() for attr in ('poly_data', '_unpickling', '_first', '_busy'): d.pop(attr, None) return d def __set_pure_state__(self, state): self._unpickling = True # First create all the allowed widgets in the widget_list attr. handle_children_state(self.widget_list, state.widget_list) # Now set their state. set_state(self, state, first=['widget_list'], ignore=['*']) # Set the widget attr depending on value saved. m = [x.__class__.__name__ for x in self.widget_list] w_c_name = state.widget.__metadata__['class_name'] w = self.widget = self.widget_list[m.index(w_c_name)] # Set the input. if len(self.inputs) > 0: w.input = self.inputs[0].outputs[0] # Fix for the point widget. if w_c_name == 'PointWidget': w.place_widget() # Set state of rest of the attributes ignoring the widget_list. set_state(self, state, ignore=['widget_list']) # Some widgets need some cajoling to get their setup right. w.update_traits() if w_c_name == 'PlaneWidget': w.origin = state.widget.origin w.normal = state.widget.normal w.update_placement() w.get_poly_data(self.poly_data) elif w_c_name == 'SphereWidget': # XXX: This hack is necessary because the sphere widget # does not update its poly data even when its ivars are # set (plus it does not have an update_placement method # which is a bug). So we force this by creating a similar # sphere source and copy its output. s = tvtk.SphereSource(center=w.center, radius=w.radius, theta_resolution=w.theta_resolution, phi_resolution=w.phi_resolution, lat_long_tessellation=True) s.update() self.poly_data.shallow_copy(s.output) else: w.get_poly_data(self.poly_data) self._unpickling = False # Set the widgets trait so that the widget is rendered if needed. self.widgets = [w] ###################################################################### # `Component` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Setup the glyphs. sources = [tvtk.SphereWidget(theta_resolution=8, phi_resolution=6), tvtk.LineWidget(clamp_to_bounds=False), tvtk.PlaneWidget(), tvtk.PointWidget(outline=False, x_shadows=False, y_shadows=False, z_shadows=False), ] self.widget_list = sources # The 'widgets' trait is set in the '_widget_changed' handler. self.widget = sources[0] for s in sources: self._connect(s) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ if len(self.inputs) == 0: return inp = self.inputs[0].outputs[0] w = self.widget w.input = inp if self._first: w.place_widget() self._first = False # If the dataset is effectively 2D switch to using the line # widget since that works best. b = inp.bounds l = [(b[1]-b[0]), (b[3]-b[2]), (b[5]-b[4])] max_l = max(l) for i, x in enumerate(l): if x/max_l < 1.0e-6: w = self.widget = self.widget_list[1] w.clamp_to_bounds = True w.align = ['z_axis', 'z_axis', 'y_axis'][i] break # Set our output. w.get_poly_data(self.poly_data) self.outputs = [self.poly_data] self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self.data_changed = True ###################################################################### # `SourceWidget` interface ###################################################################### def update_poly_data(self): self.widget.get_poly_data(self.poly_data) ###################################################################### # Non-public traits. ###################################################################### def _widget_changed(self, value): # If we are being unpickled do nothing. if self._unpickling: return if value not in self.widget_list: classes = [o.__class__ for o in self.widget_list] vc = value.__class__ self._connect(value) if vc in classes: self.widget_list[classes.index(vc)] = value else: self.widget_list.append(value) recorder = self.recorder if recorder is not None: idx = self.widget_list.index(value) name = recorder.get_script_id(self) lhs = '%s.widget'%name rhs = '%s.widget_list[%d]'%(name, idx) recorder.record('%s = %s'%(lhs, rhs)) if len(self.inputs) > 0: value.input = self.inputs[0].outputs[0] value.place_widget() value.on_trait_change(self.render) self.widgets = [value] def _update_mode_changed(self, value): if value in ['interactive', 'semi-interactive']: self.update_poly_data() self.render() def _on_interaction_event(self, obj, event): if (not self._busy) and (self.update_mode == 'interactive'): self._busy = True self.update_poly_data() self._busy = False def _on_widget_trait_changed(self): if (not self._busy) and (self.update_mode != 'non-interactive'): self._busy = True # This render call forces any changes to the trait to be # rendered only then will updating the poly data make # sense. self.render() self.update_poly_data() self._busy = False def _on_alignment_set(self): w = self.widget w.place_widget() w.update_traits() def _connect(self, obj): """Wires up all the event handlers.""" obj.add_observer('InteractionEvent', self._on_interaction_event) if isinstance(obj, tvtk.PlaneWidget): obj.on_trait_change(self._on_alignment_set, 'normal_to_x_axis') obj.on_trait_change(self._on_alignment_set, 'normal_to_y_axis') obj.on_trait_change(self._on_alignment_set, 'normal_to_z_axis') elif isinstance(obj, tvtk.LineWidget): obj.on_trait_change(self._on_alignment_set, 'align') # Setup the widgets colors. fg = (1,1,1) if self.scene is not None: fg = self.scene.foreground self._setup_widget_colors(obj, fg) obj.on_trait_change(self._on_widget_trait_changed) obj.on_trait_change(self.render) def _setup_widget_colors(self, widget, color): trait_names = widget.trait_names() props = [x for x in trait_names if 'property' in x and 'selected' not in x] sel_props = [x for x in trait_names if 'property' in x and 'selected' in x] for p in props: setattr(getattr(widget, p), 'color', color) setattr(getattr(widget, p), 'line_width', 2) for p in sel_props: # Set the selected color to 'red'. setattr(getattr(widget, p), 'color', (1,0,0)) setattr(getattr(widget, p), 'line_width', 2) self.render() def _foreground_changed_for_scene(self, old, new): # Change the default color for the actor. for w in self.widget_list: self._setup_widget_colors(w, new) self.render() def _scene_changed(self, old, new): super(SourceWidget, self)._scene_changed(old, new) self._foreground_changed_for_scene(None, new.foreground) mayavi-4.1.0/mayavi/components/cutter.py0000644000175100001440000000462311674464502021334 0ustar ischnellusers00000000000000"""A simple wrapper for `tvtk.Cutter`. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Property from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports. from mayavi.core.component import Component ###################################################################### # `Cutter` class. ###################################################################### class Cutter(Component): # The version of this class. Used for persistence. __version__ = 0 # The mapper. cutter = Instance(tvtk.Cutter, args=()) # The cut function. This should be a delegate but due to a bug in # traits that does not work. cut_function = Property ######################################## # View related traits. view = View(Group(Item(name='cutter', style='custom', resizable=True), show_labels=False), resizable=True) ###################################################################### # `Component` interface ###################################################################### def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ if (len(self.inputs) == 0) or (len(self.inputs[0].outputs) == 0): return c = self.cutter c.input = self.inputs[0].outputs[0] self.outputs = [c.output] def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self.data_changed = True ###################################################################### # `Cutter` interface ###################################################################### def _get_cut_function(self): return self.cutter.cut_function def _set_cut_function(self, val): old = self.cutter.cut_function self.cutter.cut_function = val self.trait_property_changed('cut_function', old, val) mayavi-4.1.0/mayavi/components/actor.py0000644000175100001440000002065711674464502021143 0ustar ischnellusers00000000000000"""A simple actor component. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool, Enum from tvtk.api import tvtk from traits.api import DelegatesTo # Local imports. from mayavi.core.component import Component from mayavi.core.source import Source ###################################################################### # `Actor` class. ###################################################################### class Actor(Component): # The version of this class. Used for persistence. __version__ = 0 # The mapper. mapper = Instance(tvtk.Mapper, record=True) # The actor. actor = Instance(tvtk.Actor, record=True) # The actor's property. property = Instance(tvtk.Property, record=True) # FIXME: None of the texture stuff is picklable. This will NOT be # supported till the pickling infrastructure is cleaned up and # fixed. # If texturing is enabled for the actor or not enable_texture = Bool(False, desc='if texturing is enabled') # The source of the texture's image texture_source_object = Instance(Source) # The actors texture texture = Instance(tvtk.Texture, record=True) # The texture coord generation mode. tcoord_generator_mode = Enum('none', 'cylinder', 'sphere', 'plane', desc='the mode for texture coord generation') # Texture coord generator. tcoord_generator = Instance(tvtk.Object, allow_none=True) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(Actor, self).__get_pure_state__() for attr in ('texture', 'texture_source_object', 'enable_texture', 'tcoord_generator_mode', 'tcoord_generator'): d.pop(attr,None) return d ###################################################################### # `Component` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* its tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. """ self.mapper = tvtk.PolyDataMapper(use_lookup_table_scalar_range=1) self.actor = tvtk.Actor() self.property = self.actor.property self.texture = tvtk.Texture() def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ if (len(self.inputs) == 0) or \ (len(self.inputs[0].outputs) == 0): return self._tcoord_generator_mode_changed(self.tcoord_generator_mode) self.render() def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Invoke render to update any changes. self.render() ###################################################################### # `Actor` interface ###################################################################### def set_lut(self, lut): """Set the Lookup table to use.""" self.mapper.lookup_table = lut # A hack to avoid a problem with the VRML output that seems to # ignore the use_lookup_table_scalar_range setting # on the mapping self.mapper.scalar_range = lut.table_range ###################################################################### # Non-public interface. ###################################################################### def _setup_handlers(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) def _mapper_changed(self, old, new): # Setup the handlers. self._setup_handlers(old, new) # Setup the LUT. if old is not None: self.set_lut(old.lookup_table) # Setup the inputs to the mapper. if (len(self.inputs) > 0) and (len(self.inputs[0].outputs) > 0): new.input = self.inputs[0].outputs[0] # Setup the actor's mapper. actor = self.actor if actor is not None: actor.mapper = new self.render() def _actor_changed(self, old, new): # Setup the handlers. self._setup_handlers(old, new) # Set the mapper. mapper = self.mapper if mapper is not None: new.mapper = mapper # Set the property. prop = self.property if prop is not None: new.property = prop # Setup the `actors` trait. self.actors = [new] def _property_changed(self, old, new): # Setup the handlers. self._setup_handlers(old, new) # Setup the actor. actor = self.actor if new is not actor.property: actor.property = new def _foreground_changed_for_scene(self, old, new): # Change the default color for the actor. self.property.color = new self.render() def _scene_changed(self, old, new): super(Actor, self)._scene_changed(old, new) self._foreground_changed_for_scene(None, new.foreground) def _enable_texture_changed(self, value): if self.texture_source_object is None : self.actor.texture = None return if value: self.actor.texture = self.texture else: self.actor.texture = None def _can_object_give_image_data(self, source): if source is None: return False if not isinstance(source, Source): return False if source.outputs[0].is_a('vtkImageData'): return True return False def _change_texture_input(self): if self._can_object_give_image_data(self.texture_source_object): img_data = self.texture_source_object.outputs[0] self.texture.input = img_data self.actor.texture = self.texture else: self.texture_source_object = None def _texture_source_object_changed(self,old,new): if old is not None : old.on_trait_change(self._change_texture_input, 'pipeline_changed', remove=True) if new is not None : new.on_trait_change(self._change_texture_input, 'pipeline_changed' ) if new is not None: self._change_texture_input() else: self.actor.texture = None self.texture.input = None def _texture_changed(self,value): # Setup the actor's texture. actor = self.actor if actor is not None and value.input is not None: actor.texture = value self.texture.on_trait_change(self.render) self.render() def _tcoord_generator_mode_changed(self, value): inp = self.inputs if (len(inp) == 0) or \ (len(inp[0].outputs) == 0): return input = inp[0].outputs[0] old_tg = self.tcoord_generator if old_tg is not None: old_tg.on_trait_change(self.render, remove=True) if value == 'none': self.tcoord_generator = None self.mapper.input = input else: tg_dict = {'cylinder': tvtk.TextureMapToCylinder, 'sphere': tvtk.TextureMapToSphere, 'plane': tvtk.TextureMapToPlane} tg = tg_dict[value]() self.tcoord_generator = tg tg.input = input self.mapper.input = tg.output tg = self.tcoord_generator if tg is not None: tg.on_trait_change(self.render) self.render() mayavi-4.1.0/mayavi/components/glyph_source.py0000644000175100001440000002257311674464502022535 0ustar ischnellusers00000000000000"""A component that allows creates the source for the glyphs and handle transformation. """ # Author: KK Rai (kk.rai [at] iitb.ac.in) # R. Ambareesha (ambareesha [at] iitb.ac.in) # Prabhu Ramachandran # Enthought library imports. from traits.api import (Instance, List, Trait, Bool, TraitPrefixList, Property, Dict) from traitsui.api import View, Group, Item, InstanceEditor from tvtk.api import tvtk from tvtk.common import camel2enthought from apptools.persistence.state_pickler import set_state # Local imports. from mayavi.core.common import handle_children_state from mayavi.core.component import Component ###################################################################### # `GlyphSource` class. ###################################################################### class GlyphSource(Component): # The version of this class. Used for persistence. __version__ = 1 # Glyph position. This can be one of ['head', 'tail', 'center'], # and indicates the position of the glyph with respect to the # input point data. Please note that this will work correctly # only if you do not mess with the source glyph's basic size. For # example if you use a ConeSource and set its height != 1, then the # 'head' and 'tail' options will not work correctly. glyph_position = Trait('center', TraitPrefixList(['head', 'tail', 'center']), desc='position of glyph w.r.t. data point') # The Source to use for the glyph. This is chosen from # `self._glyph_list` or `self.glyph_dict`. glyph_source = Instance(tvtk.Object, allow_none=False, record=True) # A dict of glyphs to use. glyph_dict = Dict(desc='the glyph sources to select from', record=False) # A list of predefined glyph sources that can be used. glyph_list = Property(List(tvtk.Object), record=False) ######################################## # Private traits. # The transformation to use to place glyph appropriately. _trfm = Instance(tvtk.TransformFilter, args=()) # Used for optimization. _updating = Bool(False) ######################################## # View related traits. view = View(Group(Group(Item(name='glyph_position')), Group(Item(name='glyph_source', style='custom', resizable=True, editor=InstanceEditor(name='glyph_list'), ), label='Glyph Source', show_labels=False) ), resizable=True) ###################################################################### # `Base` interface ###################################################################### def __get_pure_state__(self): d = super(GlyphSource, self).__get_pure_state__() for attr in ('_updating', 'glyph_list'): d.pop(attr, None) return d def __set_pure_state__(self, state): if 'glyph_dict' in state: # Set their state. set_state(self, state, first=['glyph_dict'], ignore=['*']) ignore = ['glyph_dict'] else: # Set the dict state using the persisted list. gd = self.glyph_dict gl = self.glyph_list handle_children_state(gl, state.glyph_list) for g, gs in zip(gl, state.glyph_list): name = camel2enthought(g.__class__.__name__) if name not in gd: gd[name] = g # Set the glyph source's state. set_state(g, gs) ignore = ['glyph_list'] g_name = state.glyph_source.__metadata__['class_name'] name = camel2enthought(g_name) # Set the correct glyph_source. self.glyph_source = self.glyph_dict[name] set_state(self, state, ignore=ignore) ###################################################################### # `Component` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ self._trfm.transform = tvtk.Transform() # Setup the glyphs. self.glyph_source = self.glyph_dict['glyph_source2d'] def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ self._glyph_position_changed(self.glyph_position) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self.data_changed = True def render(self): if not self._updating: super(GlyphSource, self).render() ###################################################################### # Non-public methods. ###################################################################### def _glyph_source_changed(self, value): if self._updating == True: return gd = self.glyph_dict value_cls = camel2enthought(value.__class__.__name__) if value not in gd.values(): gd[value_cls] = value # Now change the glyph's source trait. self._updating = True recorder = self.recorder if recorder is not None: name = recorder.get_script_id(self) lhs = '%s.glyph_source'%name rhs = '%s.glyph_dict[%r]'%(name, value_cls) recorder.record('%s = %s'%(lhs, rhs)) name = value.__class__.__name__ if name == 'GlyphSource2D': self.outputs = [value.output] else: self._trfm.input = value.output self.outputs = [self._trfm.output] value.on_trait_change(self.render) self._updating = False # Now update the glyph position since the transformation might # be different. self._glyph_position_changed(self.glyph_position) def _glyph_position_changed(self, value): if self._updating == True: return self._updating = True tr = self._trfm.transform tr.identity() g = self.glyph_source name = g.__class__.__name__ # Compute transformation factor if name == 'CubeSource': tr_factor = g.x_length/2.0 elif name == 'CylinderSource': tr_factor = -g.height/2.0 elif name == 'ConeSource': tr_factor = g.height/2.0 elif name == 'SphereSource': tr_factor = g.radius else: tr_factor = 1. # Translate the glyph if value == 'tail': if name == 'GlyphSource2D': g.center = 0.5, 0.0, 0.0 elif name == 'ArrowSource': pass elif name == 'CylinderSource': g.center = 0, tr_factor, 0.0 elif hasattr(g, 'center'): g.center = tr_factor, 0.0, 0.0 elif value == 'head': if name == 'GlyphSource2D': g.center = -0.5, 0.0, 0.0 elif name == 'ArrowSource': tr.translate(-1, 0, 0) elif name == 'CylinderSource': g.center = 0,-tr_factor, 0.0 else: g.center = -tr_factor, 0.0, 0.0 else: if name == 'ArrowSource': tr.translate(-0.5, 0, 0) elif name != 'Axes': g.center = 0.0, 0.0, 0.0 if name == 'CylinderSource': tr.rotate_z(90) self._updating = False self.render() def _get_glyph_list(self): # Return the glyph list as per the original order in earlier # implementation. order = ['glyph_source2d', 'arrow_source', 'cone_source', 'cylinder_source', 'sphere_source', 'cube_source', 'axes'] gd = self.glyph_dict for key in gd: if key not in order: order.append(key) return [gd[key] for key in order] def _glyph_dict_default(self): g = {'glyph_source2d': tvtk.GlyphSource2D(glyph_type='arrow', filled=False), 'arrow_source': tvtk.ArrowSource(), 'cone_source': tvtk.ConeSource(height=1.0, radius=0.2, resolution=15), 'cylinder_source': tvtk.CylinderSource(height=1.0, radius=0.15, resolution=10), 'sphere_source': tvtk.SphereSource(), 'cube_source': tvtk.CubeSource(), 'axes': tvtk.Axes(symmetric=1)} return g mayavi-4.1.0/mayavi/components/common.py0000644000175100001440000000266311674464502021320 0ustar ischnellusers00000000000000"""Common code used by different components. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from tvtk.api import tvtk # Local imports. from mayavi.core.component import Component from mayavi.core.common import error def get_module_source(obj): """Given an object (either a component or a module), return the ModuleManager managing the module that contains this component. """ o = obj while isinstance(o, Component): o = o.inputs[0] return o def convert_to_poly_data(data): """Given a VTK dataset object, this returns the data as PolyData. This is primarily used to convert the data suitably for filters that only work for PolyData. """ if data.is_a('vtkPolyData'): return data conv = {'vtkStructuredPoints': tvtk.ImageDataGeometryFilter, 'vtkImageData': tvtk.ImageDataGeometryFilter, 'vtkRectilinearGrid': tvtk.RectilinearGridGeometryFilter, 'vtkStructuredGrid': tvtk.StructuredGridGeometryFilter, 'vtkUnstructuredGrid':tvtk.GeometryFilter} fil = None for name, fil_class in conv.items(): if data.is_a(name): fil = fil_class() break if fil is not None: fil.input = data return fil.output else: error('Given object is not a VTK dataset: %s'%data.__class__.__name__) mayavi-4.1.0/mayavi/components/implicit_plane.py0000644000175100001440000001540111674464502023013 0ustar ischnellusers00000000000000"""A component to manage an implicit plane widget. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool, Property from traitsui.api import View, Group, Item, InstanceEditor from tvtk.api import tvtk # Local imports. from mayavi.core.component import Component VTK_VER = tvtk.Version().vtk_version ###################################################################### # `ImplicitPlane` class. ###################################################################### class ImplicitPlane(Component): # The version of this class. Used for persistence. __version__ = 0 # The widget that controls the plane. widget = Instance(tvtk.ImplicitPlaneWidget, args=(), kw={'key_press_activation': False, 'place_factor':1.2, 'draw_plane':False, 'outline_translation':False}, record=True) # The plane that the widget controls. Do not change the # attributes of the plane, do it via the widget. plane = Instance(tvtk.Plane, args=(), kw={'origin':(0.0, 0.0, 0.0), 'normal':(0,0,1)}, record=True) # Convenience property for the normal delegated to the widget. normal = Property # Convenience property for the origin delegated to the widget. origin = Property ######################################## # Private traits _first = Bool(True) _busy = Bool(False) ######################################## # View related traits. if VTK_VER[:3] in ['4.2', '4.4']: _widget_group = Group(Item(name='enabled'), Item(name='normal_to_x_axis'), Item(name='normal_to_y_axis'), Item(name='normal_to_z_axis'), Item(name='outline_translation'), Item(name='tubing'), Item(name='draw_plane'), Item(name='normal'), Item(name='origin') ) else: _widget_group = Group(Item(name='enabled'), Item(name='normal_to_x_axis'), Item(name='normal_to_y_axis'), Item(name='normal_to_z_axis'), Item(name='outline_translation'), Item(name='scale_enabled'), Item(name='tubing'), Item(name='draw_plane'), Item(name='normal'), Item(name='origin') ) view = View(Group(Item(name='widget', style='custom', editor=InstanceEditor(view=View(_widget_group))), show_labels=False) ) ###################################################################### # `Component` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* its tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. """ # Setup our widgets and hook up all handlers. self.widgets = [self.widget] self._connect() def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ if len(self.inputs) == 0: return inp = self.inputs[0].outputs[0] w = self.widget w.input = inp if self._first: w.place_widget() self.origin = inp.center self._first = False else: n = self.normal # A hack to update the widget when data changes upstream. # This is perhaps a VTK bug, not sure. self.normal = n[0], n[1], n[2] + 0.001 self.normal = n # Just pass the inputs back out. This may trigger a pipeline # changed downstream if it does not then fire a data_changed. if self.outputs != [inp]: self.outputs = [inp] else: self.data_changed = True def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self.data_changed = True def update_plane(self): """Convenience method to update the plane once the widget is changed. """ self.widget.get_plane(self.plane) ###################################################################### # Non-public interface. ###################################################################### def _get_normal(self): return self.widget.normal def _set_normal(self, value): w = self.widget old = w.normal w.normal = value self.trait_property_changed('normal', old, value) self.update_plane() def _get_origin(self): return self.widget.origin def _set_origin(self, value): # Ugly, but needed. w = tvtk.to_vtk(self.widget) old = w.GetOrigin() w.SetOrigin(list(value)) self.trait_property_changed('origin', old, value) self.update_plane() def _on_interaction_event(self, obj, event): if not self._busy: self._busy = True self.update_plane() self._busy = False def _on_normal_set(self): w = self.widget w.place_widget() w.update_traits() def _connect(self): """Wires up all the event handlers.""" w = self.widget w.add_observer('InteractionEvent', self._on_interaction_event) w.on_trait_change(self._on_normal_set, 'normal_to_x_axis') w.on_trait_change(self._on_normal_set, 'normal_to_y_axis') w.on_trait_change(self._on_normal_set, 'normal_to_z_axis') w.on_trait_change(self._on_interaction_event) for obj in (self.plane, w): obj.on_trait_change(self.render) mayavi-4.1.0/mayavi/components/ui/0000755000175100001440000000000011674464502020064 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/components/ui/actor.py0000644000175100001440000001007011674464502021544 0ustar ischnellusers00000000000000""" Traits View definition file. The view trait of the parent class is extracted from the model definition file. This file can either be exec()ed or imported. See core/base.py:Base.trait_view() for what is currently used. Using exec() allows view changes without needing to restart Mayavi, but is slower than importing. """ # Authors: Prabhu Ramachandran # Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. from traitsui.api import (View, Group, Item, InstanceEditor, DropEditor, Tabbed) from tvtk.api import tvtk VTK_VER = tvtk.Version().vtk_version # The properties view group. _prop_base_group = Group(Item(name='representation'), Item(name='color'), Item(name='line_width'), Item(name='point_size'), Item(name='opacity'), ) _prop_group = Group(Item(name='property', style='custom', show_label=False, editor=InstanceEditor(view=View(_prop_base_group))), Item(name='property', show_label=False, editor=InstanceEditor(label='More options ...')), show_border=True, label='Property') # The mapper's view group. if VTK_VER[:3] in ['4.2', '4.4']: _mapper_base_group = Group(Item(name='scalar_visibility')) else: _mapper_base_group = Group(Item(name='scalar_visibility'), Item(name='interpolate_scalars_before_mapping'), ) _mapper_group = Group(Item(name='mapper', style='custom', show_label=False, editor=InstanceEditor(view=View(_mapper_base_group))), Item(name='mapper', show_label=False, editor=InstanceEditor(label='More options ...')), show_border=True, label='Mapper') # The Texture's view group _texture_group = Group(Item(name='interpolate'), Item(name='map_color_scalars_through_lookup_table'), Item(name='repeat'), show_border=True, #label='Texture', ) # The Actor's view group. _actor_base_group = Group(Item(name='visibility'))#, _actor_group = Group(Item(name='actor', style='custom', show_label=False, editor=InstanceEditor(view=View(_actor_base_group))), Item(name='actor', show_label=False, editor=InstanceEditor(label='More options ...')), show_border=True, label='Actor') actor_group = Group(_actor_group, _mapper_group, _prop_group, label='Actor', show_labels=False, ) texture_group = Group(Item(name='enable_texture'), Group(Item(name='texture_source_object', editor=DropEditor()), Item(name='tcoord_generator_mode'), Tabbed(Item(name='texture', style='custom', show_label=False, editor=InstanceEditor(view=View(_texture_group))), Item(name='tcoord_generator', style='custom', show_label=False, resizable=True, visible_when='texture_mode != "none"'), ), show_labels=True, label='Texture Properties', enabled_when='object.enable_texture', show_border=True), label='Texture', ) # The Views for this object. Pick the one that you need. actor_view = View(actor_group, resizable=True) texture_view = View(texture_group, resizable=True) view = View(actor_group, texture_group, resizable=True) mayavi-4.1.0/mayavi/components/ui/contour.py0000644000175100001440000000316411674464502022133 0ustar ischnellusers00000000000000""" Traits View definition file. The view trait of the parent class has been extracted from the model definition file. This file can either be exec()ed or imported. See core/base.py:Base.trait_view() for what is currently used. Using exec() allows view changes without needing to restart Mayavi, but is slower than importing. """ # Authors: Prabhu Ramachandran # Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. from traitsui.api import Item, Group, View view = View(Group(Item(name='filled_contours', defined_when='show_filled_contours'), Item(name='auto_contours'), # One group or the other, but not both. Group( Item(name='contours', style='custom', visible_when='not auto_contours', show_label=False), ), Group( Item(name='number_of_contours'), Item(name='minimum_contour'), Item(name='maximum_contour'), visible_when='auto_contours', ), Item(name='auto_update_range'), Group( Item(name='_data_min', label='Data minimum'), Item(name='_data_max', label='Data maximum'), visible_when='not auto_update_range', ) ) ) mayavi-4.1.0/mayavi/components/ui/__init__.py0000644000175100001440000000013211674464502022171 0ustar ischnellusers00000000000000# Author: Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/components/optional.py0000644000175100001440000001074611674464502021656 0ustar ischnellusers00000000000000"""A meta-component that allows a component to be optionally enabled or disabled. This component is mostly for illustration and is not used anywhere. This is because it is usually much easier to simply add a trait in the module to enable/disable a particular component. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool, Str, Property from traitsui.api import View, Group, Item # Local imports. from mayavi.core.component import Component ###################################################################### # `Optional` class. ###################################################################### class Optional(Component): # The version of this class. Used for persistence. __version__ = 0 # The outputs of this component is a property and not a list. outputs = Property # The component that is enabled or disabled. component = Instance(Component) # Is the component enabled or not. enabled = Bool(True, desc='if the component is enabled') # The label of the checkbox to use in the view. label = Str ######################################## # The component's view # This is defined outside the view so that the label may be easily # changed. enabled_item = Item(name='enabled') view = View(Group(Group(enabled_item), Group(Item(name='component', style='custom', visible_when='object.enabled'), show_labels=False) ) ) ###################################################################### # `Component` interface ###################################################################### def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ comp = self.component if self.inputs != comp.inputs: comp.inputs = self.inputs self.pipeline_changed = True def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self.data_changed = True ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. Note that when start is invoked, all the other information for the pipeline should be already set. """ # Do nothing if we are already running. if self.running: return super(Optional, self).start() self.component.start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return self.component.stop() super(Optional, self).stop() ###################################################################### # Non-public methods. ###################################################################### def _get_outputs(self): if self.enabled: return self.component.outputs else: return self.inputs[0].outputs def _enabled_changed(self, value): # Force downstream modules to update. self.pipeline_changed = True def _label_changed(self, value): # Change the displayed label for the enable trait in the view. item = self.trait_view_elements().content['enabled_item'] item.label = value def _component_changed(self, old, new): if old is not None: old.on_trait_change(self._fire_pipeline_changed, 'pipeline_changed', remove=True) old.on_trait_change(self._fire_data_changed, 'data_changed', remove=True) new.on_trait_change(self._fire_pipeline_changed, 'pipeline_changed') new.on_trait_change(self._fire_data_changed, 'data_changed') def _fire_pipeline_changed(self): self.pipeline_changed = True def _fire_data_changed(self): self.data_changed = True mayavi-4.1.0/mayavi/components/poly_data_normals.py0000644000175100001440000000536211674464502023536 0ustar ischnellusers00000000000000"""This component computes normals for input poly data. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item, InstanceEditor from tvtk.api import tvtk # Local imports. from mayavi.core.component import Component from mayavi.components.common import convert_to_poly_data ###################################################################### # `PolyDataNormals` class. ###################################################################### class PolyDataNormals(Component): # The version of this class. Used for persistence. __version__ = 0 # The filter that generates the normals. filter = Instance(tvtk.PolyDataNormals, args=(), kw={'feature_angle': 45.0}, record=True) ######################################## # The component's view _filter_group = Group(Item(name='feature_angle')) view = View(Group(Item(name='filter', style='custom', editor=InstanceEditor(view=View(_filter_group))), show_labels=False ) ) ###################################################################### # `Component` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* its tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. """ self.filter.on_trait_change(self.update_data) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ if (len(self.inputs) == 0) or \ (len(self.inputs[0].outputs) == 0): return f = self.filter input = self.inputs[0].outputs[0] f.input = convert_to_poly_data(input) f.update() self.outputs = [f.output] def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self.data_changed = True mayavi-4.1.0/mayavi/components/implicit_widgets.py0000644000175100001440000002357611674464502023376 0ustar ischnellusers00000000000000"""A component that provides a selection of implicit widgets to be used by various modules. """ # Author: Suyog Dutt Jain # Prabhu Ramachandran # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. import cPickle from traits.api import (Instance, Trait, Bool, TraitMap, Enum, Dict, Str, Int) from traitsui.api import View, Group, Item from tvtk.api import tvtk from apptools.persistence.state_pickler import set_state from mayavi.core.component import Component ###################################################################### # `ImplicitWidgets` class. ###################################################################### class ImplicitWidgets(Component): # The version of this class. Used for persistence. __version__ = 0 # The widget type to use. widget_mode = Enum('Box', 'Sphere', 'Plane','ImplicitPlane', desc='the implicit widget to use') # The actual poly data source widget. widget = Instance(tvtk.ThreeDWidget, record=True) update_mode = Trait('semi-interactive', TraitMap({'interactive':'InteractionEvent', 'semi-interactive': 'EndInteractionEvent'}), desc='speed at which the data should be updated') implicit_function = Instance(tvtk.ImplicitFunction, allow_none=False) ######################################## # Private traits. _first = Bool(True) _busy = Bool(False) _observer_id = Int(-1) # The actual widgets. _widget_dict = Dict(Str, Instance(tvtk.ThreeDWidget, allow_none=False)) # The actual implicit functions. _implicit_function_dict = Dict(Str, Instance(tvtk.ImplicitFunction, allow_none=False)) ######################################## # View related traits. ######################################## # Create the UI for the traits. view = View(Group(Item(name='widget_mode'), Item(name='widget', style='custom', resizable=True), label='Widget Source', show_labels=False), resizable=True) ##################################################################### # `object` interface ###################################################################### def __init__(self, **traits): # Call parent class' init. super(ImplicitWidgets, self).__init__(**traits) # Initialize the source to the default widget's instance from # the dictionary if needed. if 'widget_mode' not in traits: self._widget_mode_changed(self.widget_mode) ###################################################################### # `Base` interface ###################################################################### def __get_pure_state__(self): d = super(ImplicitWidgets, self).__get_pure_state__() for attr in ('_first', '_busy', '_observer_id', 'widget', 'implicit_function'): d.pop(attr, None) # The box widget requires a transformation matrix to be pickled. tfm = tvtk.Transform() w = self._widget_dict['Box'] w.get_transform(tfm) d['matrix'] = cPickle.dumps(tfm.matrix) return d def __set_pure_state__(self, state): # Pop the transformation matrix for the box widget. mat = state.pop('matrix') # Now set their state. set_state(self, state, first=['widget_mode'], ignore=['*']) # Set state of rest of the attributes ignoring the widget_mode. set_state(self, state, ignore=['widget_mode']) # Set the transformation for Box widget. tfm = tvtk.Transform() tfm.set_matrix(cPickle.loads(mat)) w = self._widget_dict['Box'] w.set_transform(tfm) # Some widgets need some cajoling to get their setup right. w = self.widget # Set the input. if len(self.inputs) > 0: w.input = self.inputs[0].outputs[0] w.update_traits() mode = self.widget_mode if mode == 'Plane': wd = state._widget_dict[mode] w.origin = wd.origin w.normal = wd.normal w.update_placement() self.update_implicit_function() # Set the widgets trait so that the widget is rendered if needed. self.widgets = [w] ###################################################################### # `Component` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. """ # Setup the widgets. self.widgets = [self.widget] def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ if len(self.inputs) == 0: return inp = self.inputs[0].outputs[0] w = self.widget w.input = inp if self._first: w.place_widget() self._first = False # Set our output. if self.outputs != [inp]: self.outputs = [inp] else: self.data_changed = True self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self.data_changed = True ###################################################################### # `SourceWidget` interface ###################################################################### def update_implicit_function(self): """Update the implicit_function from the widget data. """ dispatch = {'Sphere': 'get_sphere', 'Box': 'get_planes', 'Plane': 'get_plane', 'ImplicitPlane': 'get_plane'} method = getattr(self.widget, dispatch[self.widget_mode]) method(self.implicit_function) ###################################################################### # Non-public traits. ###################################################################### def _widget_changed(self, old, value): if len(self.inputs) > 0: value.input = self.inputs[0].outputs[0] value.place_widget() self.implicit_function = self._implicit_function_dict[self.widget_mode] if old is not None: self._connect(old, remove=True) self._connect(value, remove=False) self.widgets = [value] def _connect(self, value, remove=False): """Wire up event handlers or tear them down given a widget `value`. If `remove` is True, then tear them down.""" if remove and self._observer_id > 0: value.remove_observer(self._observer_id) else: self._observer_id = value.add_observer(self.update_mode_, self._on_interaction_event) if isinstance(value, tvtk.PlaneWidget) or \ isinstance(value, tvtk.ImplicitPlaneWidget): value.on_trait_change(self._on_alignment_set, 'normal_to_x_axis', remove=remove) value.on_trait_change(self._on_alignment_set, 'normal_to_y_axis', remove=remove) value.on_trait_change(self._on_alignment_set, 'normal_to_z_axis', remove=remove) value.on_trait_change(self._on_widget_trait_changed, remove=remove) value.on_trait_change(self.render, remove=remove) def _on_interaction_event(self, obj, event): self.update_implicit_function() def _update_mode_changed(self, old, new): w = self.widget if w is not None: w.remove_observer(self._observer_id) self._observer_id = w.add_observer(self.update_mode_, self._on_interaction_event) w.on_trait_change(self.render) self.render() def _on_widget_trait_changed(self): if (not self._busy) and (self.update_mode != 'non-interactive'): self._busy = True self.implicit_function = self._implicit_function_dict[self.widget_mode] self.update_implicit_function() self.render() self._busy = False def _on_alignment_set(self): """Event handler when the widget's normal is reset (if applicable).""" w = self.widget w.place_widget() w.update_traits() self.render() def _scene_changed(self, old, new): super(ImplicitWidgets, self)._scene_changed(old, new) self._foreground_changed_for_scene(None, new.foreground) def _widget_mode_changed(self, value): """This method is invoked (automatically) when the `source` trait is changed. """ self.widget = self._widget_dict[self.widget_mode] def __widget_dict_default(self): """Default value for source dict.""" w = {'Box':tvtk.BoxWidget(place_factor = 0.9), 'Sphere':tvtk.SphereWidget(place_factor = 0.9), 'Plane':tvtk.PlaneWidget(place_factor = 0.9), 'ImplicitPlane': tvtk.ImplicitPlaneWidget(place_factor=0.9, draw_plane=False)} return w def __implicit_function_dict_default(self): """Default value for source dict.""" ip = {'Box':tvtk.Planes(), 'Sphere':tvtk.Sphere(), 'Plane':tvtk.Plane(), 'ImplicitPlane': tvtk.Plane()} return ip mayavi-4.1.0/mayavi/components/actor2d.py0000644000175100001440000001217511674464502021365 0ustar ischnellusers00000000000000"""A simple actor component. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item, InstanceEditor from tvtk.api import tvtk # Local imports. from mayavi.core.component import Component ###################################################################### # `Actor2D` class. ###################################################################### class Actor2D(Component): # The version of this class. Used for persistence. __version__ = 0 # The mapper. mapper = Instance(tvtk.AbstractMapper, record=True) # The actor. actor = Instance(tvtk.Prop, record=True) # The actor's property. property = Instance(tvtk.Property2D, record=True) ######################################## # View related traits. # The Actor's view group. _actor_group = Group(Item(name='visibility'), Item(name='height'), Item(name='width'), show_border=True, label='Actor') # The View for this object. view = View(Group(Item(name='actor', style='custom', editor=InstanceEditor(view=View(_actor_group))), show_labels=False, label='Actor' ), Group(Item(name='mapper', style='custom', resizable=True), show_labels=False, label='Mapper'), Group(Item(name='property', style='custom', resizable=True), show_labels=False, label='Property'), resizable=True, ) ###################################################################### # `Component` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* its tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. """ if self.mapper is None: self.mapper = tvtk.TextMapper() self.actor = tvtk.Actor2D() self.property = self.actor.property def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ if (len(self.inputs) == 0) or \ (len(self.inputs[0].outputs) == 0): return self.mapper.input = self.inputs[0].outputs[0] self.render() def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Invoke render to update any changes. self.render() ###################################################################### # Non-public interface. ###################################################################### def _setup_handlers(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) def _mapper_changed(self, old, new): # Setup the handlers. self._setup_handlers(old, new) # Setup the inputs to the mapper. if (len(self.inputs) > 0) and (len(self.inputs[0].outputs) > 0): new.input = self.inputs[0].outputs[0] # Setup the actor's mapper. actor = self.actor if actor is not None: actor.mapper = new self.render() def _actor_changed(self, old, new): # Setup the handlers. self._setup_handlers(old, new) # Set the mapper. mapper = self.mapper if mapper is not None: new.mapper = mapper # Set the property. prop = self.property if prop is not None: new.property = prop # Setup the `actors` trait. self.actors = [new] def _property_changed(self, old, new): # Setup the handlers. self._setup_handlers(old, new) # Setup the actor. actor = self.actor if new is not actor.property: actor.property = new def _foreground_changed_for_scene(self, old, new): # Change the default color for the actor. self.property.color = new self.render() def _scene_changed(self, old, new): super(Actor2D, self)._scene_changed(old, new) self._foreground_changed_for_scene(None, new.foreground) mayavi-4.1.0/mayavi/components/contour.py0000644000175100001440000003022511674464502021514 0ustar ischnellusers00000000000000"""A contour component. This component wraps around the tvtk.ContourFilter and provides convenient options to either automatically generate a specified number of contours between a given minimum and maximum value or explicitly specify the contours. This component may be used for any input data. The component also provides a convenient option to create "filled contours". """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. import numpy # Enthought library imports. from traits.api import Instance, List, Tuple, Bool, Range, \ Float, Property from tvtk.api import tvtk # Local imports. from mayavi.core.component import Component from mayavi.core.common import error from mayavi.components.common \ import get_module_source, convert_to_poly_data ###################################################################### # `Contour` class. ###################################################################### class Contour(Component): # The version of this class. Used for persistence. __version__ = 0 # The contour filter being currently used. contour_filter = Property # Specify if filled contours are generated. filled_contours = Bool(False, desc='if filled contours are '\ 'to be generated') # Specify if contours are generated explicitly or automatically. auto_contours = Bool(False, desc='if contours are '\ 'given explicitly or automatically computed') # Number of contours, used when `auto_contours` are chosen. number_of_contours = Range(1, 100000, enter_set=True, auto_set=False, desc='number of contours to generate') # Minimum contour, this is the starting value when `auto_contours` # is turned on. minimum_contour = Range(value=0.0, low='_data_min', high='_data_max', enter_set=True, auto_set=False, desc='the starting contour value') # Maximum contour, this is the last contour when `auto_contours` # is turned on. maximum_contour = Range(value=0.0, low='_data_min', high='_data_max', enter_set=True, auto_set=False, desc='the ending contour value') # The explicit contours to create. These specify the contours # explicitly and are used when `auto_contours` is turned off. The # traits of the items in the list are dynamically generated based # on input data. contours = List(Range(value='_default_contour', low='_data_min', high='_data_max', enter_set=True, auto_set=False, ), rows=3, desc='explicitly the contours to be generated') # Specify if the filled contour option should be shown in the view # or not. This is useful in situations like the iso_surface # module where it does not make sense to use filled contours at # all. show_filled_contours = Bool(True) # Specify if the lower and upper bound for the data is to be # automatically reset or not. auto_update_range = Bool(True, desc='if the contour range is updated automatically') ######################################## # The component's view #view = View(Group(Item(name='filled_contours', # defined_when='show_filled_contours'), # Item(name='auto_contours'), '_', # Item(name='contours', # style='custom', # visible_when='not auto_contours'), # Item(name='number_of_contours', # visible_when='auto_contours'), # Item(name='minimum_contour', # visible_when='auto_contours'), # Item(name='maximum_contour', # visible_when='auto_contours'), # Item(name='auto_update_range'), # Item(name='_data_min', # label='Data minimum', # visible_when='not auto_update_range'), # Item(name='_data_max', # label='Data maximum', # visible_when='not auto_update_range'), # ) # ) ######################################## # Private traits. _current_range = Tuple # The minimum value of the input data. Set to a very large negative value # to avoid errors prior to the object being added to the mayavi # tree. _data_min = Float(-1e20, enter_set=True, auto_set=False) # The maximum value of the input data. Set to a very large value # to avoid errors prior to the object being added to the mayavi # tree. _data_max = Float(1e20, enter_set=True, auto_set=False) # The default value of the contour to add, this property is computed # from the _data_min and _data_max traits and used when the user # adds a contour manually from the UI when auto_contours are turned # off. _default_contour = Property(Float) # The contour filter. _cont_filt = Instance(tvtk.ContourFilter, args=()) # The filled contour filter. This filter generates the filled contours. _fill_cont_filt = Instance(tvtk.BandedPolyDataContourFilter, args=(), kw={'clipping': 1, 'scalar_mode':'value'}) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(Contour, self).__get_pure_state__() # These traits are dynamically created. for name in ('_data_min', '_data_max', '_default_contour'): d.pop(name, None) return d ###################################################################### # `Component` interface ###################################################################### def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ if not self._has_input(): return cf = self._set_contour_input() first = False if len(self._current_range) == 0: first = True self._update_ranges() # If this is the first time, create a default contour if first: cr = self._current_range self.contours = [(cr[0] + cr[1])/2] self.minimum_contour = cr[0] self.maximum_contour = cr[1] self.outputs = [cf.output] def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self._update_ranges() # Propagage the data changed event. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _contours_items_changed(self, list_event): if self.auto_contours or not self._has_input(): return cf = self.contour_filter added, removed, index = (list_event.added, list_event.removed, list_event.index) if len(added) == len(removed): cf.set_value(index, added[0]) cf.update() self.data_changed = True else: self._contours_changed(self.contours) def _contours_changed(self, values): if self.auto_contours or not self._has_input(): return cf = self.contour_filter cf.number_of_contours = len(values) for i, x in enumerate(values): cf.set_value(i, x) cf.update() self.data_changed = True def _update_ranges(self): # Here we get the module's source since the input of this # component may not in general represent the entire object. if not self.auto_update_range: return src = get_module_source(self.inputs[0]) sc = src.outputs[0].point_data.scalars if sc is not None: sc_array = sc.to_array() has_nan = numpy.isnan(sc_array).any() if has_nan: rng = (float(numpy.nanmin(sc_array)), float(numpy.nanmax(sc_array))) else: rng = sc.range else: error('Cannot contour: No scalars in input data!') rng = (0.0, 1.0) if rng != self._current_range: self.set(_data_min=rng[0], _data_max=rng[1], trait_change_notify=False) self._clip_contours(rng) self._current_range = rng def _minimum_contour_changed(self, value): self._do_auto_contours() def _maximum_contour_changed(self, value): self._do_auto_contours() def _number_of_contours_changed(self, value): self._do_auto_contours() def _auto_contours_changed(self, value): if value: self._do_auto_contours() else: self._contours_changed(self.contours) def _auto_update_range_changed(self, value): if value: rng = self._data_min, self._data_max self._current_range = rng self._update_ranges() self.trait_property_changed('_data_min', rng[0], self._data_min) self.trait_property_changed('_data_max', rng[1], self._data_max) def _do_auto_contours(self): if not self._has_input(): return if self.auto_contours: minc, maxc = self.minimum_contour, self.maximum_contour self.contour_filter.generate_values(self.number_of_contours, min(minc, maxc), max(minc, maxc)) self.data_changed = True def _filled_contours_changed(self, val): if not self._has_input(): return cf = self._set_contour_input() # This will trigger a change. self._auto_contours_changed(self.auto_contours) self.outputs = [cf.output] def _get_contour_filter(self): if self.filled_contours: return self._fill_cont_filt else: return self._cont_filt def _set_contour_input(self): """Sets the input to the appropriate contour filter and returns the currently used contour filter. """ inp = self.inputs[0].outputs[0] cf = self.contour_filter if self.filled_contours: inp = convert_to_poly_data(inp) cf.input = inp cf.update() return cf def _has_input(self): """Returns if this component has a valid input.""" if (len(self.inputs) > 0) and \ (len(self.inputs[0].outputs) > 0): return True else: return False def _clip_contours(self, rng): """Clips the contour related values when the data range has changed. The new range is given as the argument. """ ctr = [] dmin, dmax = rng ctr = [min(max(x, dmin), dmax) for x in self.contours] if self.auto_contours or ctr != self.contours: self.contours = ctr self.set(minimum_contour=self._data_min, maximum_contour=self._data_max, trait_change_notify=False) self._do_auto_contours() def _get__default_contour(self): return (self._data_min + self._data_max)*0.5 mayavi-4.1.0/mayavi/components/grid_plane.py0000644000175100001440000001422011674464502022124 0ustar ischnellusers00000000000000"""A grid plane component. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Enum, Int, Range from traitsui.api import View, Group, Item from tvtk.api import tvtk from apptools.persistence import state_pickler # Local imports. from mayavi.core.component import Component from mayavi.core.common import error def _get_extent(inp): """Get the extents from the given input. """ d = inp.dimensions return [0, d[0]-1, 0, d[1]-1, 0, d[2]-1] ###################################################################### # `GridPlane` class. ###################################################################### class GridPlane(Component): # The version of this class. Used for persistence. __version__ = 0 # The TVTK object that extracts the grid plane. This is created # dynamically based on the input data type. plane = Instance(tvtk.Object) # The axis which is normal to the plane chosen. axis = Enum('x', 'y', 'z', desc='specifies the axis normal to the grid plane') # The position of the grid plane. position = Range(value=0, low='_low', high='_high', enter_set=True, auto_set=False) ######################################## # Private traits. # Determines the lower limit of the position trait and is always 0. _low = Int(0) # Determines the upper limit of the position trait. The value is # dynamically set depending on the input data and state of the # axis trait. The default is some large value to avoid errors in # cases where the user may set the position before adding the # object to the mayavi tree. _high = Int(10000) ######################################## # View related traits. # The View for this object. view = View(Group(Item(name='axis'), Item(name='position', enabled_when='_high > 0')) ) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(GridPlane, self).__get_pure_state__() # These traits are dynamically created. for name in ('plane', '_low', '_high'): d.pop(name, None) return d def __set_pure_state__(self, state): state_pickler.set_state(self, state) self._position_changed(self.position) ###################################################################### # `Component` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* its tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. """ pass def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ if len(self.inputs) == 0: return input = self.inputs[0].outputs[0] plane = None if input.is_a('vtkStructuredGrid'): plane = tvtk.StructuredGridGeometryFilter() elif input.is_a('vtkStructuredPoints') or input.is_a('vtkImageData'): plane = tvtk.ImageDataGeometryFilter () elif input.is_a('vtkRectilinearGrid'): plane = tvtk.RectilinearGridGeometryFilter () else: msg = "The GridPlane component does not support the %s dataset."\ %(input.class_name) error(msg) raise TypeError, msg plane.input = input self.plane = plane self.outputs = [plane.output] self._update_limits() self._update_extents() # If the data is 2D make sure that we default to the # appropriate axis. extents = list(_get_extent(input)) diff = [y-x for x, y in zip(extents[::2], extents[1::2])] if diff.count(0) > 0: self.axis = ['x', 'y', 'z'][diff.index(0)] def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self._update_limits() self._update_extents() # Propagate the data_changed event. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _get_axis_index(self): return {'x':0, 'y':1, 'z':2}[self.axis] def _update_extents(self): inp = self.plane.input extents = list(_get_extent(inp)) pos = self.position axis = self._get_axis_index() extents[2*axis] = pos extents[2*axis+1] = pos try: self.plane.set_extent(extents) except AttributeError: self.plane.extent = extents def _update_limits(self): extents = _get_extent(self.plane.input) axis = self._get_axis_index() pos = min(self.position, extents[2*axis+1]) self._high = extents[2*axis+1] return pos def _axis_changed(self, val): if len(self.inputs) == 0: return pos = self._update_limits() if self.position == pos: self._update_extents() self.data_changed = True else: self.position = pos def _position_changed(self, val): if len(self.inputs) == 0: return self._update_extents() self.data_changed = True mayavi-4.1.0/mayavi/components/custom_grid_plane.py0000644000175100001440000001463111674464502023524 0ustar ischnellusers00000000000000"""A grid plane component. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Int, Range from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports. from mayavi.core.component import Component from mayavi.core.common import error ###################################################################### # `CustomGridPlane` class. ###################################################################### class CustomGridPlane(Component): # The version of this class. Used for persistence. __version__ = 0 # The TVTK object that extracts the grid plane. This is created # dynamically based on the input data type. plane = Instance(tvtk.Object) # Minimum x value. x_min = Range(value=0, low='_x_low', high='_x_high', enter_set=True, auto_set=False, desc='minimum x value of the domain') # Maximum x value. x_max = Range(value=10000, low='_x_low', high='_x_high', enter_set=True, auto_set=False, desc='maximum x value of the domain') # Minimum y value. y_min = Range(value=0, low='_y_low', high='_y_high', enter_set=True, auto_set=False, desc='minimum y value of the domain') # Maximum y value. y_max = Range(value=10000, low='_y_low', high='_y_high', enter_set=True, auto_set=False, desc='maximum y value of the domain') # Minimum z value. z_min = Range(value=0, low='_z_low', high='_z_high', enter_set=True, auto_set=False, desc='minimum z value of the domain') # Maximum z value. z_max = Range(value=10000, low='_z_low', high='_z_high', enter_set=True, auto_set=False, desc='maximum z value of the domain') ######################################## # Private traits. # Determines the lower/upper limit of the axes for the sliders. _x_low = Int(0) _x_high = Int(10000) _y_low = Int(0) _y_high = Int(10000) _z_low = Int(0) _z_high = Int(10000) ######################################## # View related traits. # The View for this object. view = View(Group(Item(name='x_min'), Item(name='x_max'), Item(name='y_min'), Item(name='y_max'), Item(name='z_min'), Item(name='z_max'), ), resizable=True ) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(CustomGridPlane, self).__get_pure_state__() # These traits are dynamically created. for axis in ('x', 'y', 'z'): for name in ('_min', '_max'): d.pop(axis + name, None) d.pop('_' + axis + '_low', None) d.pop('_' + axis + '_high', None) d.pop('plane', None) return d ###################################################################### # `Component` interface ###################################################################### def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ if len(self.inputs) == 0: return input = self.inputs[0].outputs[0] plane = None if input.is_a('vtkStructuredGrid'): plane = tvtk.StructuredGridGeometryFilter() elif input.is_a('vtkStructuredPoints') or input.is_a('vtkImageData'): plane = tvtk.ImageDataGeometryFilter () elif input.is_a('vtkRectilinearGrid'): plane = tvtk.RectilinearGridGeometryFilter () else: msg = "The GridPlane component does not support the %s dataset."\ %(input.class_name) error(msg) raise TypeError, msg plane.input = input self.plane = plane self._update_limits() self._update_voi() self.outputs = [plane.output] def update_data(self): """Override this method to do what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self._update_limits() self._update_voi() # Propagate the data_changed event. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _update_limits(self): extents = self.plane.input.whole_extent self._x_low, self._x_high = extents[:2] self._y_low, self._y_high = extents[2:4] self._z_low, self._z_high = extents[4:] def _x_min_changed(self, val): if val > self.x_max: self.x_max = val else: self._update_voi() def _x_max_changed(self, val): if val < self.x_min: self.x_min = val else: self._update_voi() def _y_min_changed(self, val): if val > self.y_max: self.y_max = val else: self._update_voi() def _y_max_changed(self, val): if val < self.y_min: self.y_min = val else: self._update_voi() def _z_min_changed(self, val): if val > self.z_max: self.z_max = val else: self._update_voi() def _z_max_changed(self, val): if val < self.z_min: self.z_min = val else: self._update_voi() def _update_voi(self): if len(self.inputs) == 0: return plane = self.plane extents = (self.x_min, self.x_max, self.y_min, self.y_max, self.z_min, self.z_max) try: plane.set_extent(extents) except AttributeError: plane.extent = tuple(extents) plane.update_whole_extent() plane.update() self.data_changed = True mayavi-4.1.0/mayavi/components/__init__.py0000644000175100001440000000013211674464502021554 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/components/glyph.py0000644000175100001440000002501111674464502021143 0ustar ischnellusers00000000000000"""A component that allows one to place colored and scaled glyphs at input point data. """ # Author: Prabhu Ramachandran # KK Rai (kk.rai [at] iitb.ac.in) # R. Ambareesha (ambareesha [at] iitb.ac.in) # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Trait, Bool from traits.api import Enum from traitsui.api import View, Group, Item from tvtk.api import tvtk from tvtk.tvtk_base import TraitRevPrefixMap # Local imports. from mayavi.core.component import Component from mayavi.core.module import Module from mayavi.components import glyph_source ###################################################################### # `Glyph` class. ###################################################################### class Glyph(Component): # The version of this class. Used for persistence. __version__ = 0 # Type of Glyph: 'tensor' or 'vector' glyph_type = Enum('vector', 'tensor', desc = 'if the glyph is vector or tensor') # The scaling mode to use when scaling the glyphs. We could have # used the glyph's own scale mode but it allows users to set the # mode to use vector components for the scaling which I'd like to # disallow. scale_mode = Trait('scale_by_scalar', TraitRevPrefixMap({'scale_by_vector': 1, 'scale_by_vector_components': 2, 'data_scaling_off': 3, 'scale_by_scalar': 0}), desc="if scaling is done using scalar or vector/normal magnitude" ) # The color mode to use when coloring the glyphs. We could have # used the glyph's own color_mode trait but it allows users to set # the mode to use vector components for the scaling which I'd # like to disallow. color_mode = Trait('color_by_scalar', TraitRevPrefixMap({'color_by_vector': 2, 'color_by_scalar': 1, 'no_coloring': 0}), desc="if coloring is done by scalar or vector/normal magnitude" ) color_mode_tensor = Trait('scalar', TraitRevPrefixMap({'scalars': 1, 'eigenvalues':2, 'no_coloring': 0}), desc="if coloring is done by scalar or eigenvalues" ) # Specify if the input points must be masked. By mask we mean # that only a subset of the input points must be displayed. mask_input_points = Bool(False, desc="if input points are masked") # The MaskPoints filter. mask_points = Instance(tvtk.MaskPoints, args=(), kw={'random_mode': True}, record=True) # The Glyph3D instance. glyph = Instance(tvtk.Object, allow_none=False, record=True) # The Source to use for the glyph. This is chosen from # `self._glyph_list` or `self.glyph_dict`. glyph_source = Instance(glyph_source.GlyphSource, allow_none=False, record=True) # The module associated with this component. This is used to get # the data range of the glyph when the scale mode changes. This # *must* be set if this module is to work correctly. module = Instance(Module) # Should we show the GUI option for changing the scalar mode or # not? This is useful for vector glyphing modules where there it # does not make sense to scale the data based on scalars. show_scale_mode = Bool(True) ######################################## # Private traits. # Used for optimization. _updating = Bool(False) ######################################## # View related traits. view = View(Group(Item(name='mask_input_points'), Group(Item(name='mask_points', enabled_when='object.mask_input_points', style='custom', resizable=True), show_labels=False, ), label='Masking', ), Group(Group(Item(name='scale_mode', enabled_when='show_scale_mode', visible_when='show_scale_mode'), Item(name='color_mode', enabled_when= 'glyph_type == "vector"', visible_when= 'glyph_type == "vector"'), Item(name='color_mode_tensor', enabled_when= 'glyph_type == "tensor"', visible_when= 'glyph_type == "tensor"'), ), Group(Item(name='glyph', style='custom', resizable=True), show_labels=False), label='Glyph', selected=True, ), Group(Item(name='glyph_source', style='custom', resizable=True), show_labels=False, label='Glyph Source', ), resizable=True ) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(Glyph, self).__get_pure_state__() for attr in ('module', '_updating'): d.pop(attr, None) return d ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ self._glyph_type_changed(self.glyph_type) self.glyph_source = glyph_source.GlyphSource() # Handlers to setup our source when the sources pipeline changes. self.glyph_source.on_trait_change(self._update_source, 'pipeline_changed') self.mask_points.on_trait_change(self.render) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ if ((len(self.inputs) == 0) or (len(self.inputs[0].outputs) == 0)): return self._mask_input_points_changed(self.mask_input_points) if self.glyph_type == 'vector': self._color_mode_changed(self.color_mode) else: self._color_mode_tensor_changed(self.color_mode_tensor) self._scale_mode_changed(self.scale_mode) # Set our output. self.outputs = [self.glyph.output] self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self._scale_mode_changed(self.scale_mode) self.data_changed = True def render(self): if not self._updating: super(Glyph, self).render() def start(self): """Overridden method. """ if self.running: return self.glyph_source.start() super(Glyph, self).start() def stop(self): if not self.running: return self.glyph_source.stop() super(Glyph, self).stop() ###################################################################### # Non-public methods. ###################################################################### def _update_source(self): self.glyph.source = self.glyph_source.outputs[0] def _glyph_source_changed(self, value): self.glyph.source = value.outputs[0] def _color_mode_changed(self, value): if len(self.inputs) == 0: return if value != 'no_coloring': self.glyph.color_mode = value def _color_mode_tensor_changed(self, value): if len(self.inputs) == 0: return self._updating = True if value != 'no_coloring': self.glyph.color_mode = value self.glyph.color_glyphs = True else: self.glyph.color_glyphs = False self._updating = False self.render() def _scale_mode_changed(self, value): if (self.module is None) or (len(self.inputs) == 0)\ or self.glyph_type == 'tensor': return self._updating = True try: glyph = self.glyph glyph.scale_mode = value mm = self.module.module_manager if glyph.scale_mode == 'scale_by_scalar': glyph.range = tuple(mm.scalar_lut_manager.data_range) else: glyph.range = tuple(mm.vector_lut_manager.data_range) finally: self._updating = False self.render() def _mask_input_points_changed(self, value): inputs = self.inputs if len(inputs) == 0: return if value: mask = self.mask_points mask.input = inputs[0].outputs[0] self.glyph.input = mask.output else: self.glyph.input = inputs[0].outputs[0] def _glyph_type_changed(self, value): if self.glyph_type == 'vector': self.glyph = tvtk.Glyph3D(clamping=True) else: self.glyph = tvtk.TensorGlyph(scale_factor=0.1) self.show_scale_mode = False self.glyph.on_trait_change(self.render) def _scene_changed(self, old, new): super(Glyph, self)._scene_changed(old, new) self.glyph_source.scene = new mayavi-4.1.0/mayavi/scripts/0000755000175100001440000000000011674464502016751 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/scripts/util.py0000644000175100001440000000163111674464502020301 0ustar ischnellusers00000000000000""" This module collects utility code that does not import any significant envisage/mayavi code but is useful for scripts that use mayavi. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. import sys from os.path import join, abspath, dirname, isdir def get_data_dir( example_filename ): """ Get the data directory while running an example script. Parameters: ----------- example_filename: Path absolute path of the example script """ if 'mayavi2' in sys.argv[0]: if isdir('data'): return 'data' filename = sys.argv[-1] dir_name = join(dirname(abspath(filename)), 'data') if isdir(dir_name): return dir_name raise Exception('Run example from the example directory') else: return join(dirname(example_filename), 'data') mayavi-4.1.0/mayavi/scripts/mayavi20000755000175100001440000000011111674464502020240 0ustar ischnellusers00000000000000#!/usr/bin/env python from mayavi.scripts import mayavi2 mayavi2.main() mayavi-4.1.0/mayavi/scripts/mayavi2.py0000644000175100001440000005140411674464502020677 0ustar ischnellusers00000000000000#!/usr/bin/env python """The Mayavi application standalone script. This script parses the command line arguments and launches Mayavi2 suitably. It is meant to be used by those using Mayavi2 as a standalone application. Mayavi2 wiki page: http://svn.enthought.com/enthought/wiki/MayaVi Author: Prabhu Ramachandran """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. import sys import types import getopt import logging from os.path import splitext, exists, join, abspath # Local imports. from mayavi.__version__ import __version__ from mayavi.scripts.util import get_data_dir # Globals. OFFSCREEN = False # A global mayavi instance so we can close it correctly. mayavi = None ########################################################################### # Utility functions. ########################################################################### def usage (): msg="""Usage:\n\nmayavi2 [options] ... [arg1] [arg2] ... Where arg1, arg2 ... are optional file names that correspond to saved Mayavi2 visualizations (file.mv2) or Mayavi2 scripts (file.py) or any data files supported by Mayavi. Valid options are one or more of the following: -d datafile.ext --data datafile.ext datafile.ext can be any of the supported data file formats. This includes VTK file formats (*.vtk, *.xml, *.vt[i,p,r,s,u], *.pvt[i,p,r,s,u]), VRML2 (*.wrl), 3D Studio (*.3ds), PLOT3D (*.xyz), STL, BYU, RAW, PLY, PDB, SLC, FACET, OBJ, AVSUCD (*.inp), GAMBIT (*.neu), Exodus (*.exii), PNG, JPEG, BMP, PNM, DCM, DEM, MHA, MHD, MINC, XIMG, TIFF, and various others that are supported. datafile.ext can also be a source object not associated with a file, for example ParametricSurface or PointLoad will load the corresponding data sources into Mayavi. --filter filter-name -f filter-name The passed filter name is loaded with respect to the current source/filter object. The filter name must be a valid one if not you will get an error message. If the filter is specified as 'package.sub.filter.SomeFilter' then the filter (`SomeFilter`) is imported from 'package.sub.filter'. Standard modules provided with mayavi2 do not need the full path specification. Example:: mayavi2 -d data.vtk -f ExtractVectorNorm -f m2_user_filters.TestFilter In this example 'ExtractVectorNorm' is a standard filter and 'm2_user_filters.TestFilter' is some user defined filter. -m module-name --module module-name The passed module name is loaded in the current ModuleManager. The module name must be a valid one if not you will get an error message. If a module is specified as 'package.sub.module.SomeModule' then the module (`SomeModule`) is imported from 'package.sub.module'. Standard modules provided with mayavi2 do not need the full path specification. Example:: mayavi2 -d data.vtk -m Outline -m m2_user_modules.TestModule In this example 'Outline' is a standard module and 'm2_user_modules.TestModule' is some user defined module. -M --module-mgr Starts up a new module manager on the Mayavi pipeline. -n --new-scene Creates a new TVTK scene. Any options passed after this will apply to this newly created scene. -o --offscreen Run Mayavi in offscreen mode without any graphical user interface. This is most useful for scripts that need to render images offscreen. -x script-file --exec script-file This executes the given script in a namespace where we guarantee that the name 'mayavi' is Mayavi's script instance -- just like in the embedded Python interpreter. **WARNING**: Note that this uses `execfile`, so please note that this can be dangerous if the script does something nasty! -s python-expression --set python-expression Execute the expression on the last created object. For example, lets say the previous object was a module. If you want to set the color of that object and save the scene you may do:: $ mayavi2 [...] -m Outline -s"actor.property.color = (1,0,0)" \ -s "scene.save('test.png', size=(800, 800))" You should use quotes for the expression. **WARNING**: Note that this uses `exec`, so please note that this can be dangerous! -z saved-visualization-file --viz saved-visualization-file --visualization saved-visualization-file Loads a previously saved Mayavi2 visualization file passed as the argument. -t --test Runs the mayavi2 test suite and exits. If run as such, this runs both the TVTK and Mayavi2 unittests. If any additional arguments are passed they are passed along to the test runner. So this may be used to run other tests as well. For example:: mayavi2 -t apptools.persistence This will run just the tests inside the apptools.persistence package. You can also specify a directory with test files to run with this, for example:: mayavi2 -t relative_path_to/integrationtests/mayavi will run the integration tests from the mayavi sources. -v --verbose Prints verbose logs on the console. -V --version Prints the Mayavi version. -h --help Prints this help message. Examples:: mayavi2 test.mv2 (or mayavi2 -z test.mv2) mayavi2 test.py (or mayavi2 -x test.py) mayavi2 test.mv2 other.mv2 (or mayavi2 -z test.mv2 -z other.mv2) mayavi2 -d test.vtk -m Axes -m GridPlane \\ -f Threshold -m IsoSurface \\ -n -d heart.vtk -m Outline -m ContourGridPlane """ return msg def parse_cmd_line(arguments): """Returns the options and arguments parsed via getopts. Due to the special needs (order of args is important and repeated options will be supplied) of this command line parser we must use getopts and not optparse. Input Arguments: arguments -- This can be either a list of arguments as in sys.argv[1:] or a string that is similar to the one passed on the command line. If it is a string the string is split to create a list of arguments. """ if type(arguments) in types.StringTypes: arguments = arguments.split() options = "d:m:f:z:x:s:nMvo" long_opts = ['data=', 'module=', 'filter=', 'visualization=', 'viz=', 'exec=', 'set=', 'verbose', 'module-mgr', 'new-scene', 'offscreen'] try: opts, args = getopt.getopt (arguments, options, long_opts) except getopt.error, msg: print msg print usage () print '-'*70 print msg sys.exit (1) return opts, args def _get_non_file_sources(): """Returns a dict indexed on the name of non-file related sources ids with the value being the corresponding metadata object. """ from mayavi.core.registry import registry data = {} for src in registry.sources: if len(src.extensions) == 0: name = src.id[:-6] data[name] = src return data def process_cmd_line(app, opts, args): """ Processes the passed command line arguments. Input Arguments: app -- A Mayavi application instance. opts -- The list of options returned by getopt. args -- The remaining arguments returned by getopt. """ from mayavi.core.common import error, exception from tvtk.common import camel2enthought sources = _get_non_file_sources() script = app.script last_obj = None # Start a new scene by default if there is none currently and none # was specified at the start of the command line arguments. if script.engine.current_scene is None: new_scene = False if len(opts) == 0: if len(args) == 0: new_scene = True elif (opts[0][0] not in ('-n', '--new-scene', '-z', '--visualization', '--viz', '-x', '--exec')): new_scene = True if new_scene: last_obj = script.new_scene() for o, a in opts: if o in ('-d', '--data'): base, ext = splitext(a) if exists(a): last_obj = script.open(a) elif a in sources: md = sources[a] src = md.get_callable()() script.add_source(src) last_obj = src else: error("File/Source %s does not exist!"%a) return if o in ('-m', '--module'): if '.' in a: idx = a.rfind('.') modname = a[:idx] classname = a[idx+1:] else: modname = 'mayavi.modules.%s'%camel2enthought(a) classname = a try: mod = __import__(modname, globals(), locals(), [classname]) except ImportError, msg: exception(str(msg)) return else: m = getattr(mod, classname)() if classname == 'Labels': m.object = script.engine.current_object script.add_module(m) last_obj = m if o in ('-f', '--filter'): if '.' in a: idx = a.rfind('.') modname = a[:idx] classname = a[idx+1:] else: if a[:12] == 'UserDefined:': modname = 'mayavi.filters.user_defined' classname = 'UserDefined' # Create the wrapped filter. fname = a[12:] from tvtk.api import tvtk try: extra = getattr(tvtk, fname)() except (AttributeError, TypeError): # Don't worry about errors. extra = None else: modname = 'mayavi.filters.%s'%camel2enthought(a) classname = a extra = None try: mod = __import__(modname, globals(), locals(), [classname]) except ImportError, msg: exception(str(msg)) return else: klass = getattr(mod, classname) if classname != 'UserDefined': f = klass() else: if extra is not None: f = klass(filter=extra) else: f = klass() f.setup_filter() script.add_filter(f) last_obj = f if o in ('-M', '--module-mgr'): from mayavi.core.module_manager \ import ModuleManager mm = ModuleManager() script.add_filter(mm) last_obj = mm if o in ('-n', '--new-scene'): script.new_scene() e = script.engine s = e.scenes[-1] e.set(current_scene=s, current_object=s) last_obj = s if o in ('-x', '--exec' ): err = run_script(script, a) if err: # stop processing options. return if o in ('-s', '--set'): try: stmt = 'last_obj.' + a exec stmt in locals(), globals() except Exception, msg: exception(str(msg)) if o in ('-z', '--visualization', '--viz'): script.load_visualization(a) # for remaining arguments simply load saved visualizations. for arg in args: base, ext = splitext (arg) if ext == '.mv2': script.load_visualization(arg) elif ext == '.py': err = run_script(script, arg) if err: # stop processing arguments. return else: script.open(arg) def run_script(mayavi, script_name): """Execfiles a given script. The name `mayavi` is bound to the mayavi script instance just like in the embedded interpreter. `script_name` is the name of the script to execute. Note that this function uses `execfile`. You should be careful when using this. It returns `False` if everything was OK and `True` if not. """ from mayavi.core.common import exception g = sys.modules['__main__'].__dict__ if 'mayavi' not in g: g['mayavi'] = mayavi g['engine'] = mayavi.engine error = False # Do execfile try: # If we don't pass globals twice we get NameErrors and nope, # using exec open(script_name).read() does not fix it. execfile(script_name, g, g) except Exception, msg: exception(str(msg)) error = True return error # This runs the runtests script and sends any args to it. if ('-t' in sys.argv[1:]) or ('--test' in sys.argv[1:]): from mayavi.tests import runtests for arg in ('-t', '--test'): if arg in sys.argv[1:]: sys.argv.remove(arg) runtests.main() # If the user just wants help messages. Print them before importing # any of the big modules. if ('-h' in sys.argv[1:]) or ('--help' in sys.argv[1:]): print usage() sys.exit(0) if ('-V' in sys.argv[1:]) or ('--version' in sys.argv[1:]): print 'Mayavi %s'%__version__ sys.exit(0) for opt, arg in parse_cmd_line(sys.argv[1:])[0]: if opt in ('-o', '--offscreen'): OFFSCREEN = True break # Create opt and arg to be able to delete them even if the previous loop # is empty. opt, arg = None, None del opt, arg # The vtk module is imported by the engine, so mayavi is never going to # start without it. Let us capture the error early, and give a meaningful # error message try: import vtk except ImportError, m: msg = '%s\n%s\nDo you have vtk installed properly?\n' \ 'VTK (and build instructions) can be obtained from http://www.vtk.org\n' \ % (m, '_'*80) raise ImportError(msg) # Try forcing the use of wx 2.8 before any other import. import sys if not 'wx' in sys.modules: try: # Try forcing the use of wx 2.8 from traits.etsconfig.api import ETSConfig if ETSConfig.toolkit in ('wx', ''): import wxversion wxversion.ensureMinimal('2.8') except ImportError: """ wxversion not installed """ # Importing here to avoid time-consuming import when user only wanted # version/help information. try: from mayavi.plugins.app import Mayavi, setup_logger except ImportError, m: msg = '''%s %s Could not load envisage. You might have a missing dependency. Do you have the EnvisageCore and EnvisagePlugins installed? If you installed Mayavi with easy_install, try 'easy_install '. 'easy_install Mayavi[app]' will also work. If you performed a source checkout and installed via 'python setup.py develop', be sure to run the same command in the EnvisageCore and EnvisagePlugins folders. If these packages appear to be installed, check that your numpy and configobj are installed and working. If you need numpy, 'easy_install numpy' will install numpy. Similarly, 'easy_install configobj' will install configobj. ''' % (m, '_'*80) raise ImportError(msg) ########################################################################## # `MayaviApp` class ########################################################################## class MayaviApp(Mayavi): def parse_command_line(self, argv): """Parse command line options.""" # Parse and store command line options to process once app has # started in `run`. options, args = parse_cmd_line(argv) self.cmd_line_opts = (options, args) # If the verbose option is set, change the log mode. for opts, args in options: if opts in ('-v', '--verbose'): self.log_mode = logging.DEBUG break def run(self): """Process the command line options and setup mayavi as per the users needs. """ options, args = self.cmd_line_opts # Process the options. process_cmd_line(self, options, args) ################################################################################ # `MayaviOffscreen` class. ################################################################################ class MayaviOffscreen(MayaviApp): """ The mayavi application used for offscreen rendering. """ def _script_default(self): from mayavi.plugins.script import Script from mayavi.core.off_screen_engine import OffScreenEngine engine = OffScreenEngine() engine.start() s = Script(engine=engine) return s def setup_logger(self): from traits.etsconfig.api import ETSConfig path = join(ETSConfig.application_data, 'mayavi_e3', 'mayavi.log') path = abspath(path) logger = logging.getLogger() setup_logger(logger, path, mode=self.log_mode) def main(self, argv=None): if argv is None: argv = [] self.parse_command_line(argv) self.setup_logger() self.run() ########################################################################## # Helper functions ########################################################################## def get_mayavi_script_instance(): """Return the mayavi Script instance from the first available set of envisage engines registered in the registry. """ from mayavi.core.registry import registry from mayavi.plugins.envisage_engine import EnvisageEngine from mayavi.plugins.script import Script for name, engine in registry.engines.iteritems(): if isinstance(engine, EnvisageEngine): return engine.window.get_service(Script) return def contains_mayavi(namespace): """Returns if the given namespace contains a 'mayavi' name bound to a mayavi script instance. """ from mayavi.plugins.script import Script if 'mayavi' in namespace: if isinstance(namespace.get('mayavi'), Script): return True return False def standalone(func): """A decorator to run a function from within mayavi. This lets users write a normal Python function and have that run from within mayavi. It implicitly assumes that the name 'mayavi' refers the the Script instance and will overwrite it if not. """ def wrapper(*args, **kw): script = get_mayavi_script_instance() if script is None and mayavi is not None: script = mayavi.script if script is None: def caller(script): """Callback that runs the function inside the mayavi app.""" # Bind the 'mayavi' name to the script instance func.func_globals['mayavi'] = script # Run the function in the event loop. g = script.window.application.gui g.invoke_later(func, *args, **kw) # Start up mayavi and invoke caller when the script instance # is available. m = Mayavi() m.on_trait_change(caller, 'script') # Run the mayavi app. m.main() else: ns = func.func_globals if not contains_mayavi(ns): # Bind the 'mayavi' name to the script instance ns['mayavi'] = script # Now run the function. func(*args, **kw) return wrapper def main(): """This starts up the mayavi2 application. """ global mayavi # Make sure '.' is in sys.path if '' not in sys.path: sys.path.insert(0, '') # Start the app. if OFFSCREEN: mayavi = MayaviOffscreen() else: from traits.etsconfig.api import ETSConfig # Check that we have a traits backend installed from traitsui.toolkit import toolkit toolkit() # This forces the selection of a toolkit. if ETSConfig.toolkit in ('null', ''): raise ImportError, '''Could not import backend for traits ________________________________________________________________________________ Make sure that you have either the TraitsBackendWx or the TraitsBackendQt projects installed. If you installed Mayavi with easy_install, try easy_install . easy_install Mayavi[app] will also work. If you performed a source checkout, be sure to run 'python setup.py install' in Traits, TraitsGUI, and the Traits backend of your choice. Also make sure that either wxPython or PyQT is installed. wxPython: http://www.wxpython.org/ PyQT: http://www.riverbankcomputing.co.uk/software/pyqt/intro ''' mayavi = MayaviApp() mayavi.main(sys.argv[1:]) def close(): """ This closes the mayavi2 application. """ if mayavi is not None and not OFFSCREEN: mayavi.window.close() if __name__ == '__main__': main() mayavi-4.1.0/mayavi/scripts/__init__.py0000644000175100001440000000013711674464502021063 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/action/0000755000175100001440000000000011674464502016537 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/action/modules.py0000644000175100001440000000474211674464502020570 0ustar ischnellusers00000000000000"""Actions to start various modules. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. import new # Local imports. from mayavi.core.registry import registry from mayavi.core.metadata import ModuleMetadata from mayavi.core.pipeline_info import PipelineInfo from mayavi.action.filters import FilterAction ###################################################################### # `ModuleAction` class. ###################################################################### class ModuleAction(FilterAction): ########################################################################### # 'Action' interface. ########################################################################### def perform(self, event): """ Performs the action. """ callable = self.metadata.get_callable() obj = callable() mv = self.mayavi mv.add_module(obj) mv.engine.current_selection = obj ###################################################################### # `AddModuleManager` class. ###################################################################### class AddModuleManager(ModuleAction): """ An action that adds a ModuleManager to the tree. """ tooltip = "Add a ModuleManager to the current source/filter" description = "Add a ModuleManager to the current source/filter" metadata = ModuleMetadata(id="AddModuleManager", class_name="mayavi.core.module_manager.ModuleManager", menu_name="&Add ModuleManager", tooltip="Add a ModuleManager to the current source/filter", description="Add a ModuleManager to the current source/filter", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) def perform(self, event): """ Performs the action. """ from mayavi.core.module_manager import ModuleManager mm = ModuleManager() mv = self.mayavi mv.add_module(mm) mv.engine.current_selection = mm ###################################################################### # Creating the module actions automatically. for module in registry.modules: d = {'tooltip': module.tooltip, 'description': module.desc, 'metadata': module} action = new.classobj(module.id, (ModuleAction,), d) globals()[module.id] = action mayavi-4.1.0/mayavi/action/filters.py0000644000175100001440000000431311674464502020562 0ustar ischnellusers00000000000000"""Actions to start various filters. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. import new from pyface.action.api import Action from traits.api import Instance from mayavi.plugins.script import get_imayavi from mayavi.core.registry import registry from mayavi.core.metadata import Metadata from mayavi.core.pipeline_base import PipelineBase ###################################################################### # `FilterAction` class. ###################################################################### class FilterAction(Action): # The Metadata associated with this particular action. metadata = Instance(Metadata) mayavi = Instance('mayavi.plugins.script.Script') # We disable the actions by default since these are dynamically # enabled depending on the current selection or object. enabled = False def __init__(self, **traits): super(FilterAction, self).__init__(**traits) self.mayavi.engine.on_trait_change(self._update_enabled, ['current_selection', 'current_object']) ########################################################################### # 'Action' interface. ########################################################################### def perform(self, event): """ Performs the action. """ callable = self.metadata.get_callable() obj = callable() mv = self.mayavi mv.add_filter(obj) mv.engine.current_selection = obj def _update_enabled(self, obj): if isinstance(obj, PipelineBase): e = obj.menu_helper.check_active(self.metadata) self.enabled = e else: self.enabled = False def _mayavi_default(self): return get_imayavi(self.window) ###################################################################### # Creating the filter actions automatically. for filter in registry.filters: d = {'tooltip': filter.tooltip, 'description': filter.desc, 'metadata': filter} action = new.classobj(filter.id, (FilterAction,), d) globals()[filter.id] = action mayavi-4.1.0/mayavi/action/help.py0000644000175100001440000000654011674464502020046 0ustar ischnellusers00000000000000"""Actions for the help menu. """ # Authors: Gael Varoquaux # Prabhu Ramachandran # Copyright (c) 2007-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os import path import os import sys from os.path import join, dirname # Enthought library imports. from pyface.action.api import Action from traitsui.api import auto_close_message # Local imports import mayavi.api from mayavi.core.common import error from mayavi.preferences.api import preference_manager # To find the html documentation directory, first look under the # standard place. If that directory doesn't exist, assume you # are running from the source. local_dir = dirname(mayavi.api.__file__) HTML_DIR = join(local_dir, 'html') if not path.exists(HTML_DIR): HTML_DIR = join(dirname(dirname(local_dir)), 'build', 'docs', 'html', 'mayavi') if not path.exists(HTML_DIR): HTML_DIR = None def browser_open(url): if sys.platform == 'darwin': os.system('open %s &' % url) else: import webbrowser if webbrowser._iscommand('firefox') and \ preference_manager.root.open_help_in_light_browser: # Firefox is installed, let's use it, we know how to make it # chromeless. firefox = webbrowser.get('firefox') firefox._invoke(['-chrome', url], remote=False, autoraise=True) else: webbrowser.open(url, autoraise=1) def open_help_index(): """ Open the mayavi user manual index in a browser. """ # If the HTML_DIR was found, bring up the documentation in a # web browser. Otherwise, bring up an error message. if HTML_DIR: auto_close_message("Opening help in web browser...") browser_open(join(HTML_DIR, 'index.html')) else: error("Could not find the user guide in your installation " \ "or the source tree.") def open_tvtk_docs(): """ Open the TVTK class browser. """ from tvtk.tools.tvtk_doc import TVTKClassChooser TVTKClassChooser().edit_traits() ###################################################################### # `HelpIndex` class. ###################################################################### class HelpIndex(Action): """ An action that pop up the help in a browser. """ tooltip = "The Mayavi2 user guide" description = "The Mayavi2 user guide" ########################################################################### # 'Action' interface. ########################################################################### def perform(self, event): """ Performs the action. """ open_help_index() ###################################################################### # `TVTKClassBrowser` class. ###################################################################### class TVTKClassBrowser(Action): """ An action that opens the tvtk interactive class browser. """ tooltip = "The TVTK interactive class browser" description = "The TVTK interactive class browser" ########################################################################### # 'Action' interface. ########################################################################### def perform(self, event): """ Performs the action. """ open_tvtk_docs() mayavi-4.1.0/mayavi/action/sources.py0000644000175100001440000000717411674464502020605 0ustar ischnellusers00000000000000"""An action to open various source files. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import isfile import new # Enthought library imports. from traits.api import Instance, Str from pyface.api import FileDialog, OK from pyface.action.api import Action # Local imports from mayavi.plugins.script import get_imayavi from mayavi.core.common import error from mayavi.core.metadata import Metadata from mayavi.core.registry import registry ###################################################################### # Utility functions ###################################################################### def get_scene(mayavi): """Given a mayavi script instance, get the current scene. If none is available create a new one. """ s = mayavi.engine.current_scene if s is None: mayavi.engine.new_scene() s = mayavi.engine.current_scene return s ###################################################################### # `OpenFile` class. ###################################################################### class OpenFile(Action): """ An action that opens a data file depending on the supported extensions. """ tooltip = "Open a supported data file" description = "Open any supported data file" path = Str("MenuBar/File/LoadDataMenu") ########################################################################### # 'Action' interface. ########################################################################### def perform(self, event): """ Performs the action. """ mv = get_imayavi(self.window) s = get_scene(mv) if s is None: return wildcard = 'All files (*.*)|*.*' for src in registry.sources: if len(src.extensions) > 0: if wildcard.endswith('|') or \ src.wildcard.startswith('|'): wildcard += src.wildcard else: wildcard += '|' + src.wildcard parent = self.window.control dialog = FileDialog(parent=parent, title='Open supported data file', action='open', wildcard=wildcard ) if dialog.open() == OK: if not isfile(dialog.path): error("File '%s' does not exist!"%dialog.path, parent) return # FIXME: Ask for user input if a filetype is unknown and # choose appropriate reader. src = mv.open(dialog.path) if src is not None: mv.engine.current_selection = src ###################################################################### # `SourceAction` class. ###################################################################### class SourceAction(Action): # The Metadata associated with this particular action. metadata = Instance(Metadata) def perform(self, event): mv = get_imayavi(self.window) s = get_scene(mv) if s is None: return callable = self.metadata.get_callable() obj = callable() mv.add_source(obj) mv.engine.current_selection = obj ###################################################################### # Creating the source actions automatically. for src in registry.sources: if len(src.extensions) == 0: d = {'tooltip': src.tooltip, 'description': src.desc, 'metadata': src} action = new.classobj(src.id, (SourceAction,), d) globals()[src.id] = action mayavi-4.1.0/mayavi/action/__init__.py0000644000175100001440000000013211674464502020644 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/action/save_load.py0000644000175100001440000001053711674464502021054 0ustar ischnellusers00000000000000"""Actions to save and load a MayaVi2 visualization file. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. import sys from os.path import isfile # Enthought library imports. from pyface.api import FileDialog, OK from pyface.action.api import Action # Local imports from mayavi.plugins.script import get_imayavi from mayavi.core.common import error, exception ###################################################################### # `SaveVisualization` class. ###################################################################### class SaveVisualization(Action): """ An action that saves the current visualization. """ tooltip = "Save current visualization" description = "Save current visualization to a MayaVi2 file" ########################################################################### # 'Action' interface. ########################################################################### def perform(self, event): """ Performs the action. """ wildcard = 'MayaVi2 files (*.mv2)|*.mv2|' + FileDialog.WILDCARD_ALL dialog = FileDialog(parent=self.window.control, title='Save MayaVi2 file', action='save as', wildcard=wildcard ) if dialog.open() == OK: mv = get_imayavi(self.window) mv.save_visualization(dialog.path) ###################################################################### # `LoadVisualization` class. ###################################################################### class LoadVisualization(Action): """ An action that loads a visualization from file. """ tooltip = "Load saved visualization" description = "Load saved visualization from a MayaVi2 file" ########################################################################### # 'Action' interface. ########################################################################### def perform(self, event): """ Performs the action. """ wildcard = 'MayaVi2 files (*.mv2)|*.mv2|' + FileDialog.WILDCARD_ALL parent = self.window.control dialog = FileDialog(parent=parent, title='Open MayaVi2 file', action='open', wildcard=wildcard ) if dialog.open() == OK: if not isfile(dialog.path): error("File '%s' does not exist"%dialog.path, parent) return mv = get_imayavi(self.window) mv.load_visualization(dialog.path) ###################################################################### # `RunScript` class. ###################################################################### class RunScript(Action): """ An action that runs a mayavi script. WARNING: this can be dangerous since the file runs execfile! """ tooltip = "Execute a Python script (typically a Mayavi script)" description = "Execute a Python script (typically a Mayavi script)" ########################################################################### # 'Action' interface. ########################################################################### def perform(self, event): """ Performs the action. """ wildcard = 'Python files (*.py)|*.py' parent = self.window.control dialog = FileDialog(parent=parent, title='Open Python file', action='open', wildcard=wildcard ) if dialog.open() == OK: if not isfile(dialog.path): error("File '%s' does not exist"%dialog.path, parent) return # Get the globals. # The following code is taken from scripts/mayavi2.py. g = sys.modules['__main__'].__dict__ if 'mayavi' not in g: mv = get_imayavi(self.window) g['mayavi'] = mv g['engine'] = mv.engine # Do execfile try: # If we don't pass globals twice we get NameErrors and nope, # using exec open(script_name).read() does not fix it. execfile(dialog.path, g, g) except Exception, msg: exception(str(msg)) mayavi-4.1.0/mayavi/version.py0000644000175100001440000000047211674464502017324 0ustar ischnellusers00000000000000# Wrapped in a try/except in those situations where someone hasn't installed # as an egg. What do we do then? For now, we just punt since we don't want # to define the version number in two places. try: import pkg_resources version = pkg_resources.require('Mayavi')[0].version except: version = '' mayavi-4.1.0/mayavi/modules/0000755000175100001440000000000011674464502016732 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/modules/slice_unstructured_grid.py0000644000175100001440000001415011674464502024240 0ustar ischnellusers00000000000000"""This module takes a slice of the unstructured grid data and shows the cells that intersect or touch the slice.""" # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.core.module import Module from mayavi.core.pipeline_info import PipelineInfo from mayavi.components.implicit_plane import ImplicitPlane from mayavi.components.actor import Actor from mayavi.core.common import error ###################################################################### # `MyModule` class. ###################################################################### class SliceUnstructuredGrid(Module): """This module takes a slice of the unstructured grid data and shows the cells that intersect or touch the slice.""" # The version of this class. Used for persistence. __version__ = 0 # The implicit plane widget. implicit_plane = Instance(ImplicitPlane, allow_none=False, record=True) # Extract the cells to display. extract_geometry = Instance(tvtk.ExtractGeometry, allow_none=False, record=True) # The geometry filter. geom_filter = Instance(tvtk.GeometryFilter, allow_none=False, record=True) # The actor component that represents the visualization. actor = Instance(Actor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['any']) ######################################## # View related code. view = View(Group(Item(name='implicit_plane', style='custom'), label='ImplicitPlane', show_labels=False), Group(Item(name='extract_geometry', style='custom', resizable=True), label='Extract Geometry', show_labels=False), Group(Item(name='geom_filter', style='custom', resizable=True), label='Geometry Filter', show_labels=False), Group(Item(name='actor', style='custom'), label='Actor', show_labels=False) ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the components and set them up. self.implicit_plane = ImplicitPlane() ex = tvtk.ExtractGeometry(extract_only_boundary_cells=1, extract_boundary_cells=1) self.extract_geometry = ex self.geom_filter = tvtk.GeometryFilter() # Setup the actor suitably for this module. self.actor = Actor() self.actor.property.representation = 'w' def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mod_mgr = self.module_manager if mod_mgr is None: return # Data is available, so set the input for the grid plane. input = mod_mgr.source.outputs[0] if not input.is_a('vtkUnstructuredGrid'): error('SliceUnstructuredGrid only works with input '\ 'unstructured grids') self.implicit_plane.inputs = [mod_mgr.source] self.extract_geometry.input = self.module_manager.source.outputs[0] # Set the LUT for the mapper. self.actor.set_lut(self.module_manager.scalar_lut_manager.lut) # Now flush the pipeline self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the components should do the rest if # they are connected. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _implicit_plane_changed(self, old, new): ex = self.extract_geometry if ex is not None: ex.implicit_function = new.plane self._change_components(old, new) def _extract_geometry_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) mm = self.module_manager if mm is not None: new.input = mm.source.outputs[0] ip = self.implicit_plane if ip is not None: new.implicit_function = ip.plane gf = self.geom_filter if gf is not None: gf.input = new.output new.on_trait_change(self.render) self.update_pipeline() def _geom_filter_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) ex = self.extract_geometry if ex is not None: new.input = ex.output new.on_trait_change(self.render) self.outputs = [new.output] def _actor_changed(self, old, new): new.scene = self.scene new.inputs = [self] self._change_components(old, new) mayavi-4.1.0/mayavi/modules/ui/0000755000175100001440000000000011674464502017347 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/modules/ui/iso_surface.py0000644000175100001440000000365111674464502022230 0ustar ischnellusers00000000000000""" Traits View definition file. The view trait of the parent class has been extracted from the model definition file. This file can either be exec()ed or imported. See core/base.py:Base.trait_view() for what is currently used. Using exec() allows view changes without needing to restart Mayavi, but is slower than importing. """ # Authors: Prabhu Ramachandran # Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. from traitsui.api import Item, Group, View, InstanceEditor from mayavi.components.ui.actor import actor_view, texture_view view = View(Group( Group( Item( name = 'contour', style = 'custom' ), show_labels = False, show_border = True, label = 'Contours' ), Group( Item( name = 'compute_normals' ), Item( name = 'normals', style = 'custom', show_label = False, enabled_when = 'compute_normals' ), show_border = True, label = 'Normals' ), label='Contours', ), Group(Item('actor', resizable=True, style='custom', editor=InstanceEditor(view=actor_view) ), label='Actor', show_labels=False, ), Group(Item('actor', resizable=True, style='custom', editor=InstanceEditor(view=texture_view) ), label='Texturing', show_labels=False, ), resizable=True ) mayavi-4.1.0/mayavi/modules/ui/surface.py0000644000175100001440000000313711674464502021355 0ustar ischnellusers00000000000000""" Traits View definition file. The view trait of the parent class is extracted from the model definition file. This file can either be exec()ed or imported. See core/base.py:Base.trait_view() for what is currently used. Using exec() allows view changes without needing to restart Mayavi, but is slower than importing. """ # Authors: Prabhu Ramachandran # Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. from traitsui.api import Item, Group, View, InstanceEditor from mayavi.components.ui.actor import actor_view, texture_view view = View( Group(Item(name='enable_contours', label='Enable Contours'), Group(Item(name='contour', style='custom', enabled_when='object.enable_contours' ), show_labels=False, ), show_labels=True, label='Contours' ), Group(Item('actor', resizable=True, style='custom', editor=InstanceEditor(view=actor_view) ), label='Actor', show_labels=False, ), Group(Item('actor', resizable=True, style='custom', editor=InstanceEditor(view=texture_view) ), label='Texturing', show_labels=False, ), ) mayavi-4.1.0/mayavi/modules/ui/__init__.py0000644000175100001440000000013211674464502021454 0ustar ischnellusers00000000000000# Author: Judah De Paula # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/modules/outline.py0000644000175100001440000001564711674464502021000 0ustar ischnellusers00000000000000""" A module that displays the outline for the given data, either as a box, or the corners of the bounding box. """ # Authors: Prabhu Ramachandran # KK Rai (kk.rai [at] iitb.ac.in) # R. Ambareesha (ambareesha [at] iitb.ac.in) # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Enum, Property, Bool, \ DelegatesTo from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.core.module import Module from mayavi.components.actor import Actor from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `Outline` class. ###################################################################### class Outline(Module): # The version of this class. Used for persistence. __version__ = 0 # The `Outline` filter which can either be an instance of # `OutlineFilter` or `OutlineCornerFilter`. The `ObjectBase` class # is the superclass of both the `OutlineFilter` and the # `OutlineCornerFilter`. outline_filter = Property(Instance(tvtk.ObjectBase, allow_none=False), record=True) # Enum to set the outline type. outline_mode = Enum('full', 'cornered', desc='if outline mode is "full" or "cornered"') actor = Instance(Actor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) # An outline source, optionally used to choose the bounds of the # outline. outline_source = Instance(tvtk.OutlineSource, ()) bounds = DelegatesTo('outline_source', desc="the bounds of the outline: xmin, xmax, ymin, ymax") manual_bounds = Bool( desc="whether the bounds are automatically inferred from " "the data source") # Create the UI for the traits. # The controls for the outline_filter should be enabled only when the # Cornered Outline Filter is selected. view = View(Group( Group( Item(name='outline_mode'), Item(name='outline_filter', style='custom', enabled_when='outline_mode == "cornered"', visible_when='outline_mode == "cornered"', resizable=True, show_label=False), label='Outline', show_labels=False), Group('manual_bounds', Item('bounds', enabled_when='manual_bounds'), label='Bounds', ), Item(name='actor', style='custom'), layout='tabbed', show_labels=False), resizable=True) ######################################## # Private traits. # We make these private traits and cache them because if we create # these anew each time the `outline_mode` changes, then we loose # any settings the user changed on the previous mode. _full_outline = Instance(tvtk.OutlineFilter, args=(), allow_none=False) _cornered_outline = Instance(tvtk.OutlineCornerFilter, args=(), allow_none=False) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # When any trait on the outline filters change call the render # method. self._full_outline.on_trait_change(self.render) self._cornered_outline.on_trait_change(self.render) self.actor = Actor() def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return self._outline_mode_changed(self.outline_mode) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the component should do the rest. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _outline_mode_changed(self, value): """This method is invoked (automatically) when the 'outline_mode' attribute is changed. """ # Properties don't fire events, so we fire an event here so UI # elements and any listners can update due to the changed mode. new = self.outline_filter old = self._cornered_outline if new is self._full_outline: old = self._cornered_outline self.trait_property_changed('outline_filter', old, new) mm = self.module_manager if mm is None: return # Set the input of the filter. self._manual_bounds_changed() # The module has a list of outputs, but at this stage, # the output of the newly instantiated filter will be its only output. self.outputs = [self.outline_filter.output] def _get_outline_filter(self): if self.outline_mode == 'full': return self._full_outline else: return self._cornered_outline def _actor_changed(self, old, new): new.scene = self.scene new.inputs = [self] self._change_components(old, new) def _manual_bounds_changed(self): if self.manual_bounds: self.outline_filter.input = self.outline_source.output else: # Set the input of the filter. mm = self.module_manager self.outline_filter.input = mm.source.outputs[0] def _bounds_changed(self): self.pipeline_changed = True mayavi-4.1.0/mayavi/modules/skeleton_module.py0000644000175100001440000000540611674464502022502 0ustar ischnellusers00000000000000"""Module documentation goes here.""" # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.core.module import Module from mayavi.components.actor import Actor ###################################################################### # `MyModule` class. ###################################################################### class MyModule(Module): # The version of this class. Used for persistence. __version__ = 0 # The actor component that represents the visualization. actor = Instance(Actor) ######################################## # View related code. ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the components and set them up. # Setup the actor suitably for this module. # Setup the components, actors and widgets. (sample code) #self.components.extend([your_components, ...]) #self.actors.append(your_actor) # Note that self.actor.actor need not be added. #self.widgets.append(your_widget) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ # Data is available, so set the input for the grid plane. # Do your stuff here! # Now flush the pipeline self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the components should do the rest if # they are connected. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### mayavi-4.1.0/mayavi/modules/hyper_streamline.py0000644000175100001440000001305211674464502022657 0ustar ischnellusers00000000000000""" A module that integrates through a tensor field to generate a hyperstreamline. The integration is along the maximum eigenvector and the cross section of the hyperstreamline is defined by the two other eigenvectors. Thus the shape of the hyperstreamline is "tube-like", with the cross section being elliptical. Hyperstreamlines are used to visualize tensor fields. """ # Authors: KK Rai (kk.rai [at] iitb.ac.in) # R. Ambareesha (ambareesha [at] iitb.ac.in) # Prabhu Ramachandran # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.core.module import Module from mayavi.core.pipeline_info import PipelineInfo from mayavi.components.actor import Actor ###################################################################### # `HyperStreamline` class. ###################################################################### class HyperStreamline(Module): # The version of this class. Used for persistence. __version__ = 0 # The hyper streamline object. streamline = Instance(tvtk.HyperStreamline, allow_none=False, record=True) # The actor for the streamlines. actor = Instance(Actor, allow_none=False, record=True) # A point widget widget = Instance(tvtk.PointWidget, args=(), kw={'outline': False, 'x_shadows': False, 'y_shadows': False, 'z_shadows': False}, allow_none=False) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['tensors']) # Create the UI for the traits. view = View(Group(Item(name='actor', style='custom'), show_labels=False, label='Actor'), Group(Item(name='widget', style='custom', resizable=True), show_labels=False, label='PointWidget'), Group(Item(name='streamline', style='custom', resizable=True), label='Streamline', show_labels=False), resizable=True ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ self.widget.on_trait_change(self._start_position_changed) self.streamline = tvtk.HyperStreamline() self.streamline.start_position = self.widget.position self.streamline.integrate_minor_eigenvector() self.streamline.maximum_propagation_distance = 10.0 self.streamline.integration_step_length =0.1 self.streamline.step_length = 0.01 self.streamline.radius = 0.25 self.streamline.number_of_sides = 18 self.streamline.integration_direction = 2 #integrate both direction self.streamline.on_trait_change(self.render) self.actor = Actor() self.widgets.append(self.widget) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return src = mm.source.outputs[0] self.streamline.input = src w = self.widget old_inp = w.input w.input = src if old_inp is None or src != old_inp: w.place_widget() self.streamline.update() self.outputs = [self.streamline.output] self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the component should do the rest. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _streamline_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) mm = self.module_manager if mm is not None: new.input = mm.source.outputs[0] # A default output so there are no pipeline errors. The # update_pipeline call corrects this if needed. self.outputs = [new.output] self.update_pipeline() def _start_position_changed(self, value): self.streamline.start_position = self.widget.position def _actor_changed(self, old, new): new.scene = self.scene new.inputs = [self] self._change_components(old, new) mayavi-4.1.0/mayavi/modules/api.py0000644000175100001440000000207111674464502020055 0ustar ischnellusers00000000000000""" Defines the publicly accessible MayaVi2 modules. """ # Author: Frederic Petit # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. from axes import Axes from contour_grid_plane import ContourGridPlane from custom_grid_plane import CustomGridPlane from generic_module import GenericModule from glyph import Glyph from grid_plane import GridPlane from hyper_streamline import HyperStreamline from image_actor import ImageActor from image_plane_widget import ImagePlaneWidget from iso_surface import IsoSurface from labels import Labels from orientation_axes import OrientationAxes from outline import Outline from scalar_cut_plane import ScalarCutPlane from slice_unstructured_grid import SliceUnstructuredGrid from streamline import Streamline from structured_grid_outline import StructuredGridOutline from surface import Surface from text import Text from text3d import Text3D from tensor_glyph import TensorGlyph from vector_cut_plane import VectorCutPlane from vectors import Vectors from volume import Volume from warp_vector_cut_plane import WarpVectorCutPlane mayavi-4.1.0/mayavi/modules/volume.py0000644000175100001440000004461711674464502020627 0ustar ischnellusers00000000000000"""The Volume module visualizes scalar fields using volumetric visualization techniques. This supports ImageData and UnstructuredGrid data. It also supports the FixedPointRenderer for ImageData. However, the performance is slow so your best bet is probably with the ImageData based renderers. """ # Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Standard imports from math import cos, sqrt, pi from vtk.util import vtkConstants # Enthought library imports. from traits.api import Instance, Property, List, ReadOnly, \ Str, Button, Tuple from traitsui.api import View, Group, Item, InstanceEditor, CustomEditor from tvtk.api import tvtk from tvtk.util.gradient_editor import hsva_to_rgba, GradientTable from tvtk.util.ctf import save_ctfs, load_ctfs, \ rescale_ctfs, set_lut, PiecewiseFunction, ColorTransferFunction from apptools.persistence import state_pickler # Local imports from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.module import Module from mayavi.core.common import error from mayavi.core.trait_defs import DEnum from mayavi.core.lut_manager import LUTManager ###################################################################### # Utility functions. ###################################################################### def gradient_editor_factory(wx_parent, trait_editor): """A simple wrapper to the wx specific function to avoid any UI toolkit imports. """ from tvtk.util import wx_gradient_editor as wxge return wxge.gradient_editor_factory(wx_parent, trait_editor) def is_volume_pro_available(): """Returns `True` if there is a volume pro card available. """ try: map = tvtk.VolumeProMapper() except AttributeError: return False else: return map.number_of_boards > 0 def find_volume_mappers(): res = [] for name in dir(tvtk): if 'Volume' in name and 'Mapper' in name and 'OpenGL' not in name: try: klass = getattr(tvtk, name) inst = klass() except TypeError: pass else: res.append(name) ignores = ['VolumeTextureMapper3D', 'VolumeProMapper'] for name in ignores: if name in res: res.remove(name) return res def default_OTF(x1, x2): """Creates a default opacity transfer function. """ maxs = max(x1, x2) mins = min(x1, x2) otf = PiecewiseFunction() otf.add_point(mins, 0.0) otf.add_point(maxs, 0.2) return otf def make_CTF(x1, x2, hue_range=(2.0/3.0, 0.0), sat_range=(1.0, 1.0), val_range=(1.0, 1.0), n=10, mode='sqrt'): """Creates a CTF as per given arguments. Lets one set a hue, saturation and value range. The mode can be one of 'sqrt', or 'linear'. The idea of this function is to create a CTF that is similar to the LUT being used. 's_curve' is not implemented. Patches welcome. """ maxs = max(x1, x2) mins = min(x1, x2) ds = maxs - mins dhue = hue_range[1] - hue_range[0] dsat = sat_range[1] - sat_range[0] dval = val_range[1] - val_range[0] ctf = ColorTransferFunction() try: ctf.range = (mins, maxs) except Exception: # VTK versions < 5.2 don't seem to need this. pass if mode == 'sqrt': for i in range(n+1): # Generate x in [0, 1] x = 0.5*(1.0 + cos((n-i)*pi/n)) # Chebyshev nodes. h = hue_range[0] + dhue*x s = sat_range[0] + dsat*x v = val_range[0] + dval*x r, g, b, a = [sqrt(c) for c in hsva_to_rgba(h, s, v, 1.0)] ctf.add_rgb_point(mins+x*ds, r, g, b) elif mode == 'linear': for i in range(n+1): # Generate x in [0, 1] x = float(i)/n # Uniform nodes. h = hue_range[0] + dhue*x s = sat_range[0] + dsat*x v = val_range[0] + dval*x r, g, b, a = hsva_to_rgba(h, s, v, 1.0) ctf.add_rgb_point(mins+x*ds, r, g, b) return ctf def default_CTF(x1, x2): """Creates a default RGB color transfer function. In this case we default to a red-blue one with the 'sqrt' mode. """ return make_CTF(x1, x2, hue_range=(2.0/3.0, 0.0), sat_range=(1.0, 1.0), val_range=(1.0, 1.0), n=10, mode='sqrt') def load_volume_prop_from_grad(grad_file_name, volume_prop, scalar_range=(0, 255)): """Load a ``*.grad`` file (*grad_file_name*) and set the given volume property (*volume_prop*) given the *scalar_range*. """ gt = GradientTable(300) gt.load(grad_file_name) gt.store_to_vtk_volume_prop(volume_prop, scalar_range) def save_volume_prop_to_grad(volume_prop, grad_file_name): """Save the given volume property (*volume_prop*) to a ``*.grad`` file given as *grad_file_name*. """ gt = GradientTable(300) gt.load_from_vtk_volume_prop(volume_prop) gt.save(grad_file_name) ###################################################################### # `VolumeLutManager` class. ###################################################################### class VolumeLUTManager(LUTManager): """Just has a different view than the LUTManager. """ view = View(Group(Item(name='show_scalar_bar'), Item(name='number_of_labels'), Item(name='shadow'), Item(name='use_default_name'), Item(name='data_name'), label='Scalar Bar', ), Group(Item(name='_title_text_property', style='custom', resizable=True), show_labels=False, label='Title'), Group(Item(name='_label_text_property', style='custom', resizable=True), show_labels=False, label='Labels'), resizable=True ) ###################################################################### # `Volume` class. ###################################################################### class Volume(Module): """The Volume module visualizes scalar fields using volumetric visualization techniques. This supports ImageData and UnstructuredGrid data. It also supports the FixedPointRenderer for ImageData. However, the performance is slow so your best bet is probably with the ImageData based renderers. """ # The version of this class. Used for persistence. __version__ = 0 volume_mapper_type = DEnum(values_name='_mapper_types', desc='volume mapper to use') ray_cast_function_type = DEnum(values_name='_ray_cast_functions', desc='Ray cast function to use') volume = ReadOnly volume_mapper = Property(record=True) volume_property = Property(record=True) ray_cast_function = Property(record=True) lut_manager = Instance(VolumeLUTManager, args=(), allow_none=False, record=True) input_info = PipelineInfo(datasets=['image_data', 'unstructured_grid'], attribute_types=['any'], attributes=['scalars']) ######################################## # View related code. update_ctf = Button('Update CTF') view = View(Group(Item(name='_volume_property', style='custom', editor=CustomEditor(gradient_editor_factory), resizable=True), Item(name='update_ctf'), label='CTF', show_labels=False), Group(Item(name='volume_mapper_type'), Group(Item(name='_volume_mapper', style='custom', resizable=True), show_labels=False ), Item(name='ray_cast_function_type'), Group(Item(name='_ray_cast_function', enabled_when='len(_ray_cast_functions) > 0', style='custom', resizable=True), show_labels=False), label='Mapper', ), Group(Item(name='_volume_property', style='custom', resizable=True), label='Property', show_labels=False), Group(Item(name='volume', style='custom', editor=InstanceEditor(), resizable=True), label='Volume', show_labels=False), Group(Item(name='lut_manager', style='custom', resizable=True), label='Legend', show_labels=False), resizable=True ) ######################################## # Private traits _volume_mapper = Instance(tvtk.AbstractVolumeMapper) _volume_property = Instance(tvtk.VolumeProperty) _ray_cast_function = Instance(tvtk.Object) _mapper_types = List(Str, ['TextureMapper2D', 'RayCastMapper', ]) _available_mapper_types = List(Str) _ray_cast_functions = List(Str) current_range = Tuple # The color transfer function. _ctf = Instance(ColorTransferFunction) # The opacity values. _otf = Instance(PiecewiseFunction) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(Volume, self).__get_pure_state__() d['ctf_state'] = save_ctfs(self._volume_property) for name in ('current_range', '_ctf', '_otf'): d.pop(name, None) return d def __set_pure_state__(self, state): self.volume_mapper_type = state['_volume_mapper_type'] state_pickler.set_state(self, state, ignore=['ctf_state']) ctf_state = state['ctf_state'] ctf, otf = load_ctfs(ctf_state, self._volume_property) self._ctf = ctf self._otf = otf self._update_ctf_fired() ###################################################################### # `Module` interface ###################################################################### def start(self): super(Volume, self).start() self.lut_manager.start() def stop(self): super(Volume, self).stop() self.lut_manager.stop() def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. """ v = self.volume = tvtk.Volume() vp = self._volume_property = tvtk.VolumeProperty() self._ctf = ctf = default_CTF(0, 255) self._otf = otf = default_OTF(0, 255) vp.set_color(ctf) vp.set_scalar_opacity(otf) vp.shade = True vp.interpolation_type = 'linear' v.property = vp v.on_trait_change(self.render) vp.on_trait_change(self.render) available_mappers = find_volume_mappers() if is_volume_pro_available(): self._mapper_types.append('VolumeProMapper') available_mappers.append('VolumeProMapper') self._available_mapper_types = available_mappers if 'FixedPointVolumeRayCastMapper' in available_mappers: self._mapper_types.append('FixedPointVolumeRayCastMapper') self.actors.append(v) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return input = mm.source.outputs[0] ug = hasattr(tvtk, 'UnstructuredGridVolumeMapper') if ug: if not input.is_a('vtkImageData') \ and not input.is_a('vtkUnstructuredGrid'): error('Volume rendering only works with '\ 'StructuredPoints/ImageData/UnstructuredGrid datasets') return elif not input.is_a('vtkImageData'): error('Volume rendering only works with '\ 'StructuredPoints/ImageData datasets') return self._setup_mapper_types() self._setup_current_range() self._volume_mapper_type_changed(self.volume_mapper_type) self._update_ctf_fired() self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self._setup_mapper_types() self._setup_current_range() self._update_ctf_fired() self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _setup_mapper_types(self): """Sets up the mapper based on input data types. """ input = self.module_manager.source.outputs[0] if input.is_a('vtkUnstructuredGrid'): if hasattr(tvtk, 'UnstructuredGridVolumeMapper'): check = ['UnstructuredGridVolumeZSweepMapper', 'UnstructuredGridVolumeRayCastMapper', ] mapper_types = [] for mapper in check: if mapper in self._available_mapper_types: mapper_types.append(mapper) if len(mapper_types) == 0: mapper_types = [''] self._mapper_types = mapper_types return else: if input.point_data.scalars.data_type not in \ [vtkConstants.VTK_UNSIGNED_CHAR, vtkConstants.VTK_UNSIGNED_SHORT]: if 'FixedPointVolumeRayCastMapper' \ in self._available_mapper_types: self._mapper_types = ['FixedPointVolumeRayCastMapper'] else: error('Available volume mappers only work with \ unsigned_char or unsigned_short datatypes') else: mapper_types = ['TextureMapper2D', 'RayCastMapper'] check = ['FixedPointVolumeRayCastMapper', 'VolumeProMapper' ] for mapper in check: if mapper in self._available_mapper_types: mapper_types.append(mapper) self._mapper_types = mapper_types def _setup_current_range(self): mm = self.module_manager # Set the default name and range for our lut. lm = self.lut_manager slm = mm.scalar_lut_manager lm.set(default_data_name=slm.default_data_name, default_data_range=slm.default_data_range) # Set the current range. input = mm.source.outputs[0] sc = input.point_data.scalars if sc is not None: rng = sc.range else: error('No scalars in input data!') rng = (0, 255) if self.current_range != rng: self.current_range = rng def _get_volume_mapper(self): return self._volume_mapper def _get_volume_property(self): return self._volume_property def _get_ray_cast_function(self): return self._ray_cast_function def _volume_mapper_type_changed(self, value): mm = self.module_manager if mm is None: return old_vm = self._volume_mapper if old_vm is not None: old_vm.on_trait_change(self.render, remove=True) if value == 'RayCastMapper': new_vm = tvtk.VolumeRayCastMapper() self._volume_mapper = new_vm self._ray_cast_functions = ['RayCastCompositeFunction', 'RayCastMIPFunction', 'RayCastIsosurfaceFunction'] new_vm.volume_ray_cast_function = tvtk.VolumeRayCastCompositeFunction() elif value == 'TextureMapper2D': new_vm = tvtk.VolumeTextureMapper2D() self._volume_mapper = new_vm self._ray_cast_functions = [''] elif value == 'VolumeProMapper': new_vm = tvtk.VolumeProMapper() self._volume_mapper = new_vm self._ray_cast_functions = [''] elif value == 'FixedPointVolumeRayCastMapper': new_vm = tvtk.FixedPointVolumeRayCastMapper() self._volume_mapper = new_vm self._ray_cast_functions = [''] elif value == 'UnstructuredGridVolumeRayCastMapper': new_vm = tvtk.UnstructuredGridVolumeRayCastMapper() self._volume_mapper = new_vm self._ray_cast_functions = [''] elif value == 'UnstructuredGridVolumeZSweepMapper': new_vm = tvtk.UnstructuredGridVolumeZSweepMapper() self._volume_mapper = new_vm self._ray_cast_functions = [''] new_vm.input = mm.source.outputs[0] self.volume.mapper = new_vm new_vm.on_trait_change(self.render) def _update_ctf_fired(self): set_lut(self.lut_manager.lut, self._volume_property) self.render() def _current_range_changed(self, old, new): rescale_ctfs(self._volume_property, new) self.render() def _ray_cast_function_type_changed(self, old, new): rcf = self.ray_cast_function if len(old) > 0: rcf.on_trait_change(self.render, remove=True) if len(new) > 0: new_rcf = getattr(tvtk, 'Volume%s'%new)() new_rcf.on_trait_change(self.render) self._volume_mapper.volume_ray_cast_function = new_rcf self._ray_cast_function = new_rcf else: self._ray_cast_function = None self.render() def _scene_changed(self, old, new): super(Volume, self)._scene_changed(old, new) self.lut_manager.scene = new mayavi-4.1.0/mayavi/modules/iso_surface.py0000644000175100001440000001317311674464502021613 0ustar ischnellusers00000000000000"""An IsoSurface module that allows the user to make contours of input point data. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool # Local imports from mayavi.core.module import Module from mayavi.components.contour import Contour from mayavi.components.poly_data_normals import PolyDataNormals from mayavi.components.actor import Actor from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `IsoSurface` class. ###################################################################### class IsoSurface(Module): # The version of this class. Used for persistence. __version__ = 0 # The contour component. contour = Instance(Contour, record=True) # Specify if normals are to be computed to make a smoother surface. compute_normals = Bool(True, desc='if normals are to be computed '\ 'to make the iso-surface smoother') # The component that computes the normals. normals = Instance(PolyDataNormals, record=True) # The actor component that represents the iso-surface. actor = Instance(Actor, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['scalars']) ######################################## # The view of this object. # Commented out, since we are now using the iso_surface_view.py version. #view = View([Group( # Item( name = 'contour', # style = 'custom' ), # show_labels = False, # show_border = True, # label = 'Contours' ), # Group( # Item( name = 'compute_normals' ), # '_', # Item( name = 'normals', # style = 'custom', # show_label = False, # enabled_when = 'compute_normals' ), # show_border = True, # label = 'Normals' ), # Group( # Item( name = 'actor', # style = 'custom' ), # show_labels = False ) # ] # ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the components self.contour = Contour(show_filled_contours=False) self.normals = PolyDataNormals() self.actor = Actor() # Setup the actor suitably for this module. self.actor.mapper.scalar_visibility = 1 def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return # Data is available, so set the input for the grid plane. self.contour.inputs = [mm.source] # Force the normals setting to be noted. self._compute_normals_changed(self.compute_normals) # Set the LUT for the mapper. self.actor.set_lut(mm.scalar_lut_manager.lut) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the component should do the rest. self.data_changed = True ###################################################################### # Non-public interface. ###################################################################### def _compute_normals_changed(self, value): if self.module_manager is None: return actor = self.actor if value: if actor: actor.inputs = [self.normals] else: if actor: actor.inputs = [self.contour] self.render() def _contour_changed(self, old, new): normals = self.normals if normals is not None: normals.inputs = [new] self._change_components(old, new) def _normals_changed(self, old, new): contour = self.contour if contour is not None: new.inputs = [contour] self._change_components(old, new) def _actor_changed(self, old, new): # Here we set the inputs in any case to avoid VTK pipeline # errors. The pipeline is corrected when update_pipeline is # called anyway. contour = self.contour if contour is not None: new.inputs = [contour] self._change_components(old, new) mayavi-4.1.0/mayavi/modules/image_actor.py0000644000175100001440000000756111674464502021567 0ustar ischnellusers00000000000000"""Displays ImageData efficiently. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool, on_trait_change, \ Property from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.core.module import Module from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ImageActor` class ###################################################################### class ImageActor(Module): # An image actor. actor = Instance(tvtk.ImageActor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) # An ImageMapToColors TVTK filter to adapt datasets without color # information image_map_to_color = Instance(tvtk.ImageMapToColors, (), allow_none=False, record=True) map_scalars_to_color = Bool _force_map_scalars_to_color = Property(depends_on='module_manager.source') ######################################## # The view of this module. view = View(Group(Item(name='actor', style='custom', resizable=True), show_labels=False, label='Actor'), Group( Group(Item('map_scalars_to_color', enabled_when='not _force_map_scalars_to_color')), Item('image_map_to_color', style='custom', enabled_when='map_scalars_to_color', show_label=False), label='Map Scalars', ), width=500, height=600, resizable=True) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): self.actor = tvtk.ImageActor() @on_trait_change('map_scalars_to_color,image_map_to_color.[output_format,pass_alpha_to_output]') def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. """ mm = self.module_manager if mm is None: return src = mm.source if self._force_map_scalars_to_color: self.set(map_scalars_to_color=True, trait_change_notify=False) if self.map_scalars_to_color: self.image_map_to_color.input = src.outputs[0] self.image_map_to_color.lookup_table = mm.scalar_lut_manager.lut self.actor.input = self.image_map_to_color.output else: self.actor.input = src.outputs[0] self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. """ # Just set data_changed, the component should do the rest. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _actor_changed(self, old, new): if old is not None: self.actors.remove(old) old.on_trait_change(self.render, remove=True) self.actors.append(new) new.on_trait_change(self.render) def _get__force_map_scalars_to_color(self): mm = self.module_manager if mm is None: return False src = mm.source return not isinstance(src.outputs[0].point_data.scalars, tvtk.UnsignedCharArray) mayavi-4.1.0/mayavi/modules/labels.py0000644000175100001440000001730511674464502020554 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. # Enthought library imports. from traits.api import Int, Instance, Str, TraitError from traitsui.api import View, Group, Item from tvtk.api import tvtk from apptools.persistence import state_pickler # Local imports. from mayavi.core.common import error from mayavi.core.pipeline_base import PipelineBase from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.module import Module from mayavi.filters.optional import Optional from mayavi.filters.mask_points import MaskPoints from mayavi.filters.user_defined import UserDefined from mayavi.components.actor2d import Actor2D from mayavi.core.common import handle_children_state ################################################################################ # `Labels` class. ################################################################################ class Labels(Module): """ Allows a user to label the current dataset or the current actor of the active module. """ # Used for persistence. __version__ = 0 # The object which we are labeling. object = Instance(PipelineBase, record=False) # The label format string. label_format = Str('', enter_set=True, auto_set=False, desc='the label format string') # Number of points to label. number_of_labels = Int(25, enter_set=True, auto_set=False, desc='the number of points to label') # The filter used for masking of the points. mask = Instance(MaskPoints, record=True) # Filter to select visible points. visible_points = Instance(Optional, record=True) # The 2D actor for the labels. actor = Instance(Actor2D, record=True) # The text property of the labels. property = Instance(tvtk.TextProperty, record=True) # The mapper for the labels. mapper = Instance(tvtk.LabeledDataMapper, args=(), record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ######################################## # Private traits. # The input used for the labeling. input = Instance(PipelineBase) # The id of the object in the modulemanager only used for # persistence. object_id = Int(-2) ######################################## # View related traits. view = View(Group(Item(name='number_of_labels'), Item(name='label_format'), Item(name='mapper', style='custom', show_label=False, resizable=True), Item(name='mask', style='custom', resizable=True, show_label=False), label='Labels' ), Group( Item(name='visible_points', style='custom', resizable=True, show_label=False), label='VisiblePoints' ), Group(Item(name='property', style='custom', show_label=False, resizable=True), label='TextProperty' ), resizable=True ) ###################################################################### # `object` interface. ###################################################################### def __get_pure_state__(self): self._compute_object_id() d = super(Labels, self).__get_pure_state__() for name in ('object', 'mapper', 'input'): d.pop(name, None) # Must pickle the components. d['components'] = self.components return d def __set_pure_state__(self, state): handle_children_state(self.components, state.components) state_pickler.set_state(self, state) self.update_pipeline() ###################################################################### # `Module` interface. ###################################################################### def setup_pipeline(self): mask = MaskPoints() mask.filter.set(generate_vertices=True, random_mode=True) self.mask = mask v = UserDefined(filter=tvtk.SelectVisiblePoints(), name='VisiblePoints') self.visible_points = Optional(filter=v, enabled=False) mapper = tvtk.LabeledDataMapper() self.mapper = mapper self.actor = Actor2D(mapper=mapper) self.property = mapper.label_text_property self.property.on_trait_change(self.render) self.components = [self.mask, self.visible_points, self.actor] def update_pipeline(self): mm = self.module_manager if mm is None: return self._find_input() # Calculates self.input self.mask.inputs = [self.input] self.visible_points.inputs = [self.mask] self.actor.inputs = [self.visible_points] self._number_of_labels_changed(self.number_of_labels) self._label_format_changed(self.label_format) ###################################################################### # Non-public interface. ###################################################################### def _find_input(self): mm = self.module_manager if self.object is None: if self.object_id == -1: self.input = mm.source elif self.object_id > -1: obj = mm.children[self.object_id] if hasattr(obj, 'actor'): self.set(object=obj, trait_change_notify=False) self.input = obj.actor.inputs[0] else: self.input = mm.source else: o = self.object if hasattr(o, 'module_manager'): # A module. if hasattr(o, 'actor'): self.input = o.actor.inputs[0] else: self.input = o.module_manager.source if self.input is None: if self.object_id == -2: self.input = mm.source else: error('No object to label!') return def _number_of_labels_changed(self, value): if self.input is None: return f = self.mask.filter inp = self.input.outputs[0] inp.update() npts = inp.number_of_points typ = type(f.on_ratio) f.on_ratio = typ(max(npts/value, 1)) if self.mask.running: f.update() self.mask.data_changed = True def _label_format_changed(self, value): if len(value) > 0: self.mapper.label_format = value self.render() else: try: self.mapper.label_format = None except TraitError: self.mapper.label_format = '%g' self.render() def _object_changed(self, value): self.update_pipeline() def _compute_object_id(self): mm = self.module_manager input = self.input self.object_id = -2 if input is mm.source: self.object_id = -1 return for id, child in enumerate(mm.children): if child is self.object: self.object_id = id return def _scene_changed(self, old, new): self.visible_points.filter.filter.renderer = new.renderer super(Labels, self)._scene_changed(old, new) mayavi-4.1.0/mayavi/modules/contour_grid_plane.py0000644000175100001440000001443211674464502023165 0ustar ischnellusers00000000000000"""A contour grid plane module. This module lets one take a slice of input grid data and view contours of the data. The module only works for structured points, rectilinear grid and structured grid input. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool from traitsui.api import View, Group, Item # Local imports from mayavi.core.module import Module from mayavi.components.grid_plane import GridPlane from mayavi.components.contour import Contour from mayavi.components.actor import Actor from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ContourGridPlane` class. ###################################################################### class ContourGridPlane(Module): # The version of this class. Used for persistence. __version__ = 0 # The grid plane component. grid_plane = Instance(GridPlane, allow_none=False, record=True) # Specifies if contouring is to be done or not. enable_contours = Bool(True, desc='if contours are generated') # The contour component that contours the data. contour = Instance(Contour, allow_none=False, record=True) # The actor component that represents the visualization. actor = Instance(Actor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['image_data', 'structured_grid', 'rectilinear_grid'], attribute_types=['any'], attributes=['any']) view = View([Group(Item(name='grid_plane', style='custom'), show_labels=False), Group(Item(name='enable_contours')), Group(Item(name='contour', style='custom', enabled_when='object.enable_contours'), Item(name='actor', style='custom'), show_labels=False) ] ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the components self.grid_plane = GridPlane() self.contour = Contour(auto_contours=True, number_of_contours=10) self.actor = Actor() def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return # Data is available, so set the input for the grid plane. self.grid_plane.inputs = [mm.source] # This makes sure that any changes made to enable_contours # when the module is not running are updated when it is # started. self._enable_contours_changed(self.enable_contours) # Set the LUT for the mapper. self.actor.set_lut(mm.scalar_lut_manager.lut) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the components should do the rest if # they are connected. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _filled_contours_changed(self, value): """When filled contours are enabled, the mapper should use the the cell data, otherwise it should use the default scalar mode. """ if value: self.actor.mapper.scalar_mode = 'use_cell_data' else: self.actor.mapper.scalar_mode = 'default' self.render() def _enable_contours_changed(self, value): """Turns on and off the contours.""" if self.module_manager is None: return if value: self.actor.inputs = [self.contour] if self.contour.filled_contours: self.actor.mapper.scalar_mode = 'use_cell_data' else: self.actor.inputs = [self.grid_plane] self.actor.mapper.scalar_mode = 'default' self.render() def _grid_plane_changed(self, old, new): cont = self.contour if cont is not None: cont.inputs = [new] self._change_components(old, new) def _contour_changed(self, old, new): if old is not None: old.on_trait_change(self._filled_contours_changed, 'filled_contours', remove=True) new.on_trait_change(self._filled_contours_changed, 'filled_contours') # Setup the contours input. gp = self.grid_plane if gp is not None: new.inputs = [gp] # Setup the actor. actor = self.actor if actor is not None: actor.inputs = [new] self._change_components(old, new) def _actor_changed(self, old, new): if old is None: # First time this is set. new.property.set(line_width=2.0) # Set the actors scene and input. new.scene = self.scene cont = self.contour if cont is not None: new.inputs = [cont] self._change_components(old, new) mayavi-4.1.0/mayavi/modules/warp_vector_cut_plane.py0000644000175100001440000001632411674464502023677 0ustar ischnellusers00000000000000# -*- coding: UTF-8 -*- """Takes an arbitrary slice of the input data using an implicit cut plane and warps it according to the vector field data. The scalars are displayed on the warped surface as colors. """ # Authors: Fr�d�ric Petit and Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool from traitsui.api import View, Group, Item, InstanceEditor from tvtk.api import tvtk # Local imports from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.module import Module from mayavi.components.implicit_plane import ImplicitPlane from mayavi.components.cutter import Cutter from mayavi.filters.warp_vector import WarpVector from mayavi.components.poly_data_normals import PolyDataNormals from mayavi.components.actor import Actor ###################################################################### # `VectorCutPlane` class. ###################################################################### class WarpVectorCutPlane(Module): # The version of this class. Used for persistence. __version__ = 0 # The implicit plane widget used to place the implicit function. implicit_plane = Instance(ImplicitPlane, allow_none=False, record=True) # The cutter. Takes a cut of the data on the implicit plane. cutter = Instance(Cutter, allow_none=False, record=True) # The WarpVectorCutPlane component that warps the data. warp_vector = Instance(WarpVector, allow_none=False, record=True) # Specify if vector normals are to be computed to make a smoother surface. compute_normals = Bool(False, desc='if normals are to be computed '\ 'to make the warped surface smoother') # The component that computes the normals. normals = Instance(PolyDataNormals, record=True) # The Actor component. actor = Instance(Actor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']) ######################################## # View related traits. _warp_group = Group(Item(name='filter', style='custom', editor=\ InstanceEditor(view= View(Item('scale_factor')))), show_labels=False) view = View(Group(Item(name='implicit_plane', style='custom'), label='ImplicitPlane', show_labels=False), Group(Group(Item(name='warp_vector', style='custom', resizable=True, show_label=False, editor=InstanceEditor(view=View(_warp_group)) ), ), Item(name='_'), Item(name='compute_normals'), Group(Item(name='normals', style='custom', show_label=False, enabled_when = 'compute_normals'), ), label='WarpVector', show_labels=True), Group(Item(name='actor', style='custom'), label='Actor', show_labels=False), resizable=True, ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the objects and set them up. self.implicit_plane = ImplicitPlane() self.cutter = Cutter() self.warp_vector = WarpVector() self.normals = PolyDataNormals() actor = self.actor = Actor() actor.mapper.scalar_visibility = 1 def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return self.implicit_plane.inputs = [mm.source] # Force the vector normals setting to be noted. self._compute_normals_changed(self.compute_normals) # Set the LUT for the mapper. self.actor.set_lut(mm.scalar_lut_manager.lut) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the other components should do the rest. self.data_changed = True ###################################################################### # Non-public traits. ###################################################################### def _compute_normals_changed(self, value): if self.module_manager is None: return actor = self.actor if actor is not None: if value: actor.inputs = [self.normals] else: actor.inputs = [self.warp_vector] self.render() def _normals_changed(self, old, new): warp_vector = self.warp_vector compute_normals = self.compute_normals if compute_normals is not None: new.inputs = [warp_vector] self._compute_normals_changed(self.compute_normals) self._change_components(old, new) def _implicit_plane_changed(self, old, new): cutter = self.cutter if cutter is not None: cutter.cut_function = new.plane cutter.inputs = [new] self._change_components(old, new) def _warp_vector_changed(self, old, new): cutter = self.cutter if cutter is not None: new.inputs = [cutter] self._compute_normals_changed(self.compute_normals) self._change_components(old, new) def _cutter_changed(self, old, new): ip = self.implicit_plane if ip is not None: new.cut_function = ip.plane new.inputs = [ip] w = self.warp_vector if w is not None: w.inputs = [new] self._change_components(old, new) def _actor_changed(self, old, new): self._compute_normals_changed(self.compute_normals) self._change_components(old, new) mayavi-4.1.0/mayavi/modules/vectors.py0000644000175100001440000000174611674464502021001 0ustar ischnellusers00000000000000"""Displays different types of glyphs oriented and colored as per vector data at the input points. This is merely a convenience module that is entirely based on the Glyph module. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Local imports from mayavi.modules.glyph import Glyph ###################################################################### # `Vectors` class. ###################################################################### class Vectors(Glyph): ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): super(Vectors, self).setup_pipeline() self.glyph.set(scale_mode='scale_by_vector', color_mode='color_by_vector', show_scale_mode=False) self.glyph.glyph_source.glyph_position='tail' mayavi-4.1.0/mayavi/modules/scalar_cut_plane.py0000644000175100001440000002650311674464502022611 0ustar ischnellusers00000000000000"""Takes a cut plane of any input data set using an implicit plane and plots the data with optional contouring and scalar warping. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool from traitsui.api import View, Group, Item, InstanceEditor # Local imports from mayavi.core.module import Module from mayavi.core.pipeline_info import PipelineInfo from mayavi.components.implicit_plane import ImplicitPlane from mayavi.components.cutter import Cutter from mayavi.filters.warp_scalar import WarpScalar from mayavi.components.poly_data_normals import PolyDataNormals from mayavi.components.contour import Contour from mayavi.components.actor import Actor ###################################################################### # `ScalarCutPlane` class. ###################################################################### class ScalarCutPlane(Module): # The version of this class. Used for persistence. __version__ = 0 # The implicit plane widget used to place the implicit function. implicit_plane = Instance(ImplicitPlane, allow_none=False, record=True) # The cutter. Takes a cut of the data on the implicit plane. cutter = Instance(Cutter, allow_none=False, record=True) # Specifies if contouring is to be done or not. enable_contours = Bool(False, desc='if contours are generated') # The Contour component that contours the data. contour = Instance(Contour, allow_none=False, record=True) # Specifies if scalar warping is to be done or not. enable_warp_scalar = Bool(False, desc='if scalar warping is enabled') # The WarpScalarCutPlane component that warps the data. warp_scalar = Instance(WarpScalar, allow_none=False, record=True) # Specify if scalar normals are to be computed to make a smoother surface. compute_normals = Bool(False, desc='if normals are to be computed '\ 'to make the warped scalar surface smoother') # The component that computes the scalar normals. normals = Instance(PolyDataNormals, allow_none=False, record=True) # The actor component that represents the visualization. actor = Instance(Actor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['scalars']) ######################################## # View related code. _warp_group = Group(Item(name='filter', style='custom', editor=\ InstanceEditor(view= View(Item('scale_factor')))), show_labels=False) view = View(Group(Item(name='implicit_plane', style='custom'), label='ImplicitPlane', show_labels=False), Group(Group(Item(name='enable_contours')), Group(Item(name='contour', style='custom', enabled_when='object.enable_contours'), show_labels=False), label='Contours', show_labels=False), Group(Item(name='enable_warp_scalar'), Group(Item(name='warp_scalar', enabled_when='enable_warp_scalar', style='custom', editor=InstanceEditor(view= View(_warp_group)) ), show_labels=False, ), Item(name='_'), Item(name='compute_normals', enabled_when='enable_warp_scalar'), Item(name='normals', style='custom', show_label=False, enabled_when='compute_normals and enable_warp_scalar'), label='WarpScalar', show_labels=True), Group(Item(name='actor', style='custom'), label='Actor', show_labels=False) ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the objects. self.implicit_plane = ImplicitPlane() self.cutter = Cutter() self.contour = Contour(auto_contours=True, number_of_contours=10) self.warp_scalar = WarpScalar() self.normals = PolyDataNormals() self.actor = Actor() # Setup the actor suitably for this module. prop = self.actor.property prop.line_width = 2.0 def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return # Data is available, so set the input for the grid plane. self.implicit_plane.inputs = [mm.source] # Ensure that the warped scalar surface's normal is setup right. self.warp_scalar.filter.normal = self.implicit_plane.normal # This makes sure that any changes made to enable_warp when # the module is not running are updated when it is started -- # this in turn calls the other functions (normals and # contours) internally. self._enable_warp_scalar_changed(self.enable_warp_scalar) # Set the LUT for the mapper. self.actor.set_lut(mm.scalar_lut_manager.lut) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the components should do the rest if # they are connected. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _get_warp_output(self): """Helper function to return the warped (or not) output depending on settings. """ if self.enable_warp_scalar: if self.compute_normals: return self.normals else: return self.warp_scalar else: return self.cutter def _get_contour_output(self): """Helper function to return the contoured (and warped (or not)) output depending on settings. """ if self.enable_contours: return self.contour else: return self._get_warp_output() def _filled_contours_changed_for_contour(self, value): """When filled contours are enabled, the mapper should use the the cell data, otherwise it should use the default scalar mode. """ if value: self.actor.mapper.scalar_mode = 'use_cell_data' else: self.actor.mapper.scalar_mode = 'default' self.render() def _enable_warp_scalar_changed(self, value): """Turns on and off the scalar warping.""" if self.module_manager is None: return if value: self.warp_scalar.inputs = [self.cutter] else: self.warp_scalar.inputs = [] self._compute_normals_changed(self.compute_normals) self.render() def _compute_normals_changed(self, value): if self.module_manager is None: return if self.enable_warp_scalar: normals = self.normals if value: normals.inputs = [self.warp_scalar] else: normals.inputs = [] self._enable_contours_changed(self.enable_contours) self.render() def _enable_contours_changed(self, value): """Turns on and off the contours.""" if self.module_manager is None: return actor = self.actor if value: self.contour.inputs = [self._get_warp_output()] actor.inputs = [self._get_contour_output()] if self.contour.filled_contours: actor.mapper.scalar_mode = 'use_cell_data' else: self.contour.inputs = [] actor.inputs = [self._get_warp_output()] actor.mapper.scalar_mode = 'default' self.render() def _normals_changed(self, old, new): warp_scalar = self.warp_scalar if warp_scalar is not None: new.inputs = [warp_scalar] self._compute_normals_changed(self.compute_normals) self._change_components(old, new) def _implicit_plane_changed(self, old, new): cutter = self.cutter if cutter is not None: cutter.cut_function = new.plane cutter.inputs = [new] # Update the pipeline. self._enable_warp_scalar_changed(self.enable_warp_scalar) # Hook up events to set the normals of the warp filter. if old is not None: old.widget.on_trait_change(self._update_normal, 'normal', remove=True) new.widget.on_trait_change(self._update_normal, 'normal') self._change_components(old, new) def _cutter_changed(self, old, new): ip = self.implicit_plane if ip is not None: new.cut_function = ip.plane new.inputs = [ip] # Update the pipeline. self._enable_warp_scalar_changed(self.enable_warp_scalar) self._change_components(old, new) def _contour_changed(self, old, new): # Update the pipeline. self._enable_contours_changed(self.enable_contours) self._change_components(old, new) def _warp_scalar_changed(self, old, new): # Update the pipeline. self._enable_warp_scalar_changed(self.enable_warp_scalar) self._change_components(old, new) def _actor_changed(self, old, new): # Update the pipeline. self._enable_contours_changed(self.enable_contours) self._change_components(old, new) def _update_normal(self): """Invoked when the orientation of the implicit plane changes. """ ws = self.warp_scalar if ws is not None: ws.filter.normal = self.implicit_plane.widget.normal mayavi-4.1.0/mayavi/modules/image_plane_widget.py0000644000175100001440000001074111674464502023113 0ustar ischnellusers00000000000000"""A simple ImagePlaneWidget module to view image data. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool, on_trait_change from traitsui.api import View, Group, Item from tvtk.api import tvtk # Local imports from mayavi.core.module import Module from mayavi.core.common import error from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `ImagePlaneWidget` class. ###################################################################### class ImagePlaneWidget(Module): # The version of this class. Used for persistence. __version__ = 0 ipw = Instance(tvtk.ImagePlaneWidget, allow_none=False, record=True) use_lookup_table = Bool(True, help='Use a lookup table to map input scalars to colors') input_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['scalars']) view = View(Group(Item(name='ipw', style='custom', resizable=True), show_labels=False ), width=600, height=600, resizable=True, scrollable=True ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the various objects for this module. self.ipw = tvtk.ImagePlaneWidget(display_text=1, key_press_activation=0, left_button_action=1, middle_button_action=0, user_controlled_lookup_table=True) self.setup_lut() def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mod_mgr = self.module_manager if mod_mgr is None: return # Data is available, so set the input for the IPW. input = mod_mgr.source.outputs[0] if not (input.is_a('vtkStructuredPoints') \ or input.is_a('vtkImageData')): msg = 'ImagePlaneWidget only supports structured points or '\ 'image data.' error(msg) raise TypeError, msg self.ipw.input = input self.setup_lut() def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the component should do the rest. self.data_changed = True @on_trait_change('use_lookup_table') def setup_lut(self): # Set the LUT for the IPW. if self.use_lookup_table: if self.module_manager is not None: self.ipw.lookup_table = \ self.module_manager.scalar_lut_manager.lut else: self.ipw.color_map.lookup_table = None self.render() ###################################################################### # Non-public methods. ###################################################################### def _ipw_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) self.widgets.remove(old) new.on_trait_change(self.render) self.widgets.append(new) if old is not None: self.update_pipeline() self.pipeline_changed = True mayavi-4.1.0/mayavi/modules/surface.py0000644000175100001440000001216411674464502020740 0ustar ischnellusers00000000000000"""Draws a surface for any input dataset with optional contouring. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Bool from tvtk.api import tvtk # Local imports from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.module import Module from mayavi.components.contour import Contour from mayavi.components.actor import Actor ###################################################################### # `Surface` class. ###################################################################### class Surface(Module): # The version of this class. Used for persistence. __version__ = 0 # Specifies if contouring is to be done or not. enable_contours = Bool(False, desc='if contours are generated') # The contour component that contours the data. contour = Instance(Contour, allow_none=False, record=True) # The actor component that represents the visualization. actor = Instance(Actor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Setup the objects. self.contour = Contour(auto_contours=True, number_of_contours=10) self.actor = Actor() # Setup the actor suitably for this module. self.actor.property.line_width = 2.0 def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return # This makes sure that any changes made to enable_contours # when the module is not running are updated when it is # started. Also sets up the pipeline and inputs correctly. self._enable_contours_changed(self.enable_contours) # Set the LUT for the mapper. self.actor.set_lut(mm.scalar_lut_manager.lut) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the components should do the rest if # they are connected. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _filled_contours_changed(self, value): """When filled contours are enabled, the mapper should use the the cell data, otherwise it should use the default scalar mode. """ if value: self.actor.mapper.scalar_mode = 'use_cell_data' else: self.actor.mapper.scalar_mode = 'default' self.render() def _enable_contours_changed(self, value): """Turns on and off the contours.""" if self.module_manager is None: return if value: self.contour.inputs = [self.module_manager.source] self.actor.inputs = [self.contour] if self.contour.filled_contours: self.actor.mapper.scalar_mode = 'use_cell_data' else: old_inputs = self.actor.inputs self.actor.inputs = [self.module_manager.source] self.actor.mapper.scalar_mode = 'default' self.render() def _contour_changed(self, old, new): if old is not None: old.on_trait_change(self._filled_contours_changed, 'filled_contours', remove=True) new.on_trait_change(self._filled_contours_changed, 'filled_contours') self._change_components(old, new) def _actor_changed(self, old, new): if old is None: # First time the actor is set. new.mapper = tvtk.DataSetMapper(use_lookup_table_scalar_range=1) new.scene = self.scene mm = self.module_manager if mm is not None: new.inputs = [mm.source] self._change_components(old, new) mayavi-4.1.0/mayavi/modules/generic_module.py0000644000175100001440000002273711674464502022300 0ustar ischnellusers00000000000000""" Defines a GenericModule which is a collection of mayavi filters/components put together. This is very convenient and useful to create new modules. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Bool, Enum, Instance from traitsui.api import Item, Group, View, ListEditor from apptools.persistence import state_pickler # Local imports. from mayavi.core.module import Module from mayavi.core.common import handle_children_state from mayavi.components.actor import Actor ################################################################################ # Utility function. ################################################################################ def find_object_given_state(needle, haystack, object): """ Find the object which corrsponds to given state instance (`needle`) in the given state (`haystack`) and object representing that haystack. Parameters ---------- `needle` -- The `State` instance to find haystack -- The source State in which we are to find the state `object` -- the object corresponding to the `haystack` """ if needle is haystack: return object if hasattr(object, 'filter'): return find_object_given_state(needle, haystack.filter, object.filter) elif hasattr(object, 'filters'): for h, obj in zip(haystack.filters, object.filters): r = find_object_given_state(needle, h, obj) if r is not None: return r return None ################################################################################ # `GenericModule` class. ################################################################################ class GenericModule(Module): """ Defines a GenericModule which is a collection of mayavi filters/components put together. This is very convenient and useful to create new modules. Note that all components including the actor must be passed as a list to set the components trait. """ # The *optional* Contour component to which we must listen to if # any. This is needed for modules that use a contour component # because when we turn on filled contours the mapper must switch to # use cell data. contour = Instance('mayavi.components.contour.Contour', allow_none=True) # The *optional* Actor component for which the LUT must be set. If # None is specified here, we will attempt to automatically determine # it. actor = Instance(Actor, allow_none=True) # Should we use the scalar LUT or the vector LUT? lut_mode = Enum('scalar', 'vector') ######################################## # Private traits. # Is the pipeline ready? Used internally. _pipeline_ready = Bool(False) ###################################################################### # `object` interface. ###################################################################### def __get_pure_state__(self): # Need to pickle the components. d = super(GenericModule, self).__get_pure_state__() d['components'] = self.components d.pop('_pipeline_ready', None) return d def __set_pure_state__(self, state): # If we are already running, there is a problem since the # components will be started automatically in the module's # handle_components even though their state is not yet set call # so we disable it here and restart it later. running = self.running self.running = False # Remove the actor states since we don't want these unpickled. actor_st = state.pop('actor', None) contour_st = state.pop('contour', None) # Create and set the components. handle_children_state(self.components, state.components) components = self.components # Restore our state using set_state. state_pickler.set_state(self, state) # Now set our actor and component by finding the right one to get from # the state. if actor_st is not None: for cst, c in zip(state.components, components): actor = find_object_given_state(actor_st, cst, c) if actor is not None: self.actor = actor break if contour_st is not None: for cst, c in zip(state.components, components): contour = find_object_given_state(contour_st, cst, c) if contour is not None: self.contour = contour break # Now start all components if needed. self._start_components() self.running = running ###################################################################### # `HasTraits` interface. ###################################################################### def default_traits_view(self): """Returns the default traits view for this object.""" le = ListEditor(use_notebook=True, deletable=False, export='DockWindowShell', page_name='.name') view = View(Group(Item(name='components', style='custom', show_label=False, editor=le, resizable=True), show_labels=False), resizable=True) return view ###################################################################### # `Module` interface. ###################################################################### def setup_pipeline(self): """Setup the pipeline.""" # Needed because a user may have setup the components by setting # the default value of the trait in the subclass in which case # the components_changed handler will never be called leading to # problems. if len(self.components) > 0 and not self._pipeline_ready: self._components_changed([], self.components) def update_pipeline(self): """This method *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when the input fires a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return self._setup_pipeline() # Propagate the event. self.pipeline_changed = True def update_data(self): """This method does what is necessary when upstream data changes. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Propagate the data_changed event. self.data_changed = True ###################################################################### # Private interface. ###################################################################### def _setup_pipeline(self): """Sets up the objects in the pipeline.""" mm = self.module_manager if mm is None or len(self.components) == 0: return # Our input. my_input = mm.source components = self.components if not self._pipeline_ready: # Hook up our first component. first = self.components[0] first.inputs = [my_input] # Hook up the others to each other. for i in range(1, len(components)): component = components[i] component.inputs = [components[i-1]] self._pipeline_ready = True # Start components. self._start_components() # Setup the LUT of any actors. self._lut_mode_changed(self.lut_mode) def _handle_components(self, removed, added): super(GenericModule, self)._handle_components(removed, added) for component in added: if len(component.name) == 0: component.name = component.__class__.__name__ if self.actor is None: if isinstance(component, Actor): self.actor = component if len(self.components) == 0: self.input_info.datasets = ['none'] else: self.input_info.copy_traits(self.components[0].input_info) self._pipeline_ready = False self._setup_pipeline() def _lut_mode_changed(self, value): """Static traits listener.""" mm = self.module_manager if mm is None: return lm = mm.scalar_lut_manager if value == 'vector': lm = mm.vector_lut_manager if self.actor is not None: self.actor.set_lut(lm.lut) def _actor_changed(self, old, new): self._lut_mode_changed(self.lut_mode) def _filled_contours_changed_for_contour(self, value): """When filled contours are enabled, the mapper should use the the cell data, otherwise it should use the default scalar mode. """ if self.actor is None: return if value: self.actor.mapper.scalar_mode = 'use_cell_data' else: self.actor.mapper.scalar_mode = 'default' self.render() def _start_components(self): for component in self.components: if len(component.inputs) > 0 and \ len(component.inputs[0].outputs) > 0: component.start() mayavi-4.1.0/mayavi/modules/metadata.py0000644000175100001440000003360511674464502021073 0ustar ischnellusers00000000000000""" Metadata for all modules. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Prabhu Ramachandran Enthought, Inc. # License: BSD Style. # Local imports. from mayavi.core.metadata import ModuleMetadata from mayavi.core.pipeline_info import PipelineInfo BASE = 'mayavi.modules' ################################################################################ # Metadata. axes_module = ModuleMetadata( id = "AxesModule", menu_name = "&Axes", class_name = BASE + '.axes.Axes', desc = "Draw cubical axes on the outline for given input", tooltip = "Draw axes on the outline of input data", help = "Draw axes on the outline of input data", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) contour_grid_plane_module = ModuleMetadata( id = "ContourGridPlaneModule", menu_name = "&ContourGridPlane", class_name = BASE + '.contour_grid_plane.ContourGridPlane', desc = "Shows a contour grid plane for the given input", tooltip = "Shows a contour grid plane for the given input", help = "Shows a contour grid plane for the given input", input_info = PipelineInfo(datasets=['image_data', 'structured_grid', 'rectilinear_grid'], attribute_types=['any'], attributes=['any']) ) custom_grid_plane_module = ModuleMetadata( id = "CustomGridPlaneModule", menu_name = "CustomGridPlane", class_name = BASE + '.custom_grid_plane.CustomGridPlane', desc = "Creates a highly customizable grid plane for given input", tooltip = "Creates a highly customizable grid plane for given input", help = "Creates a highly customizable grid plane for given input", input_info = PipelineInfo(datasets=['image_data', 'structured_grid', 'rectilinear_grid'], attribute_types=['any'], attributes=['any']) ) glyph_module = ModuleMetadata( id = "GlyphModule", menu_name = "Gl&yph", class_name = BASE + '.glyph.Glyph', desc = "Creates colored and scaled glyphs at at input points", tooltip = "Creates colored and scaled glyphs at at input points", help = "Creates colored and scaled glyphs at at input points", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) grid_plane_module = ModuleMetadata( id = "GridPlaneModule", menu_name = "&GridPlane", class_name = BASE + '.grid_plane.GridPlane', desc = "Shows a grid plane for the given input", tooltip = "Shows a grid plane for the given input", help = "Shows a grid plane for the given input", input_info = PipelineInfo(datasets=['image_data', 'structured_grid', 'rectilinear_grid'], attribute_types=['any'], attributes=['any']) ) hyper_streamline_module = ModuleMetadata( id = "HyperStreamlineModule", menu_name = "&HyperStreamline", class_name = BASE + '.hyper_streamline.HyperStreamline', desc = "Shows hyper streamlines for tensor data", tooltip = "Shows hyper streamlines for tensor data", help = "Shows hyper streamlines for tensor data", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['tensors']) ) image_actor_module = ModuleMetadata( id = "ImageActorModule", menu_name = "&ImageActor", class_name = BASE + '.image_actor.ImageActor', desc = "Shows an image actor for image data", tooltip = "Shows an image actor for image data", help = "Shows an image actor for image data", input_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['any']) ) image_plane_widget_module = ModuleMetadata( id = "ImagePlaneWidgetModule", menu_name = "I&magePlaneWidget", class_name = BASE + '.image_plane_widget.ImagePlaneWidget', desc = "Shows an image plane widget for image data", tooltip = "Shows an image plane widget for image data", help = "Shows an image plane widget for image data", input_info = PipelineInfo(datasets=['image_data'], attribute_types=['any'], attributes=['scalars']) ) isosurface_module = ModuleMetadata( id = "IsoSurfaceModule", menu_name = "&IsoSurface", class_name = BASE + '.iso_surface.IsoSurface', desc = "Creates an iso-surface for the given input", tooltip = "Creates an iso-surface for the given input", help = "Creates an iso-surface for the given input", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['scalars']) ) labels_module = ModuleMetadata( id = "LabelsModule", menu_name = "&Labels", class_name = BASE + '.labels.Labels', desc = "Display labels for active dataset or active module", tooltip = "Display labels for active dataset or active module", help = "Display labels for active dataset or active module", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) orientation_axes_module = ModuleMetadata( id = "OrientationAxesModule", menu_name = "Orientation A&xes", class_name = BASE + '.orientation_axes.OrientationAxes', desc = "Show an axes indicating the current orientation", tooltip = "Show an axes indicating the current orientation", help = "Show an axes indicating the current orientation", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) outline_module = ModuleMetadata( id = "OutlineModule", menu_name = "&Outline", class_name = BASE + '.outline.Outline', desc = "Draw an outline for given input", tooltip = "Draw an outline for given input", help = "Draw an outline for given input", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) scalar_cut_plane_module = ModuleMetadata( id = "ScalarCutPlaneModule", menu_name = "Scalar Cut &Plane", class_name = BASE + '.scalar_cut_plane.ScalarCutPlane', desc = "Slice through the data with optional contours", tooltip = "Slice through the data with optional contours", help = "Slice through the data with optional contours", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['scalars']) ) slice_ug_module = ModuleMetadata( id = "SliceUnstructuredGridModule", menu_name = "Slice &Unstructured Grid", class_name = BASE + '.slice_unstructured_grid.SliceUnstructuredGrid', desc = "Slice an unstructured grid to show cells", tooltip = "Slice an unstructured grid to show cells", help = "Slice an unstructured grid to show cells", input_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['any']) ) sgrid_outline_module = ModuleMetadata( id = "StructuredGridOutlineModule", menu_name = "StructuredGridOutline", class_name = BASE + '.structured_grid_outline.StructuredGridOutline', desc = "Draw a grid-conforming outline for structured grids", tooltip = "Draw a grid-conforming outline for structured grids", help = "Draw a grid-conforming outline for structured grids", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) streamline_module = ModuleMetadata( id = "StreamlineModule", menu_name = "Stream&line", class_name = BASE + '.streamline.Streamline', desc = "Generate streamlines for the vectors", tooltip = "Generate streamlines for the vectors", help = "Generate streamlines for the vectors", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']) ) surface_module = ModuleMetadata( id = "SurfaceModule", menu_name = "&Surface", class_name = BASE + '.surface.Surface', desc = "Creates a surface for the given input", tooltip = "Creates a surface for the given input", help = "Creates a surface for the given input", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) tensor_glyph_module = ModuleMetadata( id = "TensorGlyphModule", menu_name = "Te&nsorGlyph", class_name = BASE + '.tensor_glyph.TensorGlyph', desc = "Displays glyphs scaled and oriented as per tensor data", tooltip = "Displays glyphs scaled and oriented as per tensor data", help = "Displays glyphs scaled and oriented as per tensor data", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['tensors']) ) text_module = ModuleMetadata( id = "TextModule", menu_name = "&Text", class_name = BASE + '.text.Text', desc = "Displays user specified text on screen", tooltip = "Displays text on screen", help = "Displays text on screen", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) text3d_module = ModuleMetadata( id = "Text3DModule", menu_name = "&Text3D", class_name = BASE + '.text3d.Text3D', desc = "Displays user-specified text at a 3D location in the scene", tooltip = "Displays user-specified text at a 3D location in the scene", help = "Displays user-specified text at a 3D location in the scene", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) vector_cut_plane_module = ModuleMetadata( id = "VectorCutPlaneModule", menu_name = "&VectorCutPlane", class_name = BASE + '.vector_cut_plane.VectorCutPlane', desc = "Display vectors along a cut plane", tooltip = "Display vectors along a cut plane", help = "Display vectors along a cut plane", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']) ) vectors_module = ModuleMetadata( id = "VectorsModule", menu_name = "Vecto&rs", class_name = BASE + '.vectors.Vectors', desc = "Display input vectors using arrows or other glyphs", tooltip = "Display input vectors using arrows or other glyphs", help = "Display input vectors using arrows or other glyphs", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ) volume_module = ModuleMetadata( id = "VolumeModule", menu_name = "Volum&e", class_name = BASE + '.volume.Volume', desc = "Use volume rendering to view the scalar field", tooltip = "Use volume rendering to view the scalar field", help = "Use volume rendering to view the scalar field", input_info = PipelineInfo(datasets=['image_data', 'unstructured_grid'], attribute_types=['any'], attributes=['scalars']) ) warp_vector_cut_plane_module = ModuleMetadata( id = "WarpVectorCutPlaneModule", menu_name = "&WarpVectorCutPlane", class_name = BASE + '.warp_vector_cut_plane.WarpVectorCutPlane', desc = "Warp cut plane along scaled input vectors", tooltip = "Warp cut plane along scaled input vectors", help = "Warp cut plane along scaled input vectors", input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']) ) # Now collect all the modules for the mayavi registry. modules = [axes_module, contour_grid_plane_module, custom_grid_plane_module, glyph_module, grid_plane_module, hyper_streamline_module, image_actor_module, image_plane_widget_module, isosurface_module, labels_module, orientation_axes_module, outline_module, scalar_cut_plane_module, slice_ug_module, sgrid_outline_module, streamline_module, surface_module, tensor_glyph_module, text_module, text3d_module, vector_cut_plane_module, vectors_module, volume_module, warp_vector_cut_plane_module, ] mayavi-4.1.0/mayavi/modules/axes.py0000644000175100001440000002221411674464502020245 0ustar ischnellusers00000000000000"""Draws a simple axes using tvtk.CubeAxesActor2D. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Property, true from traitsui.api import View, Group, HGroup, \ Item, BooleanEditor from tvtk.api import tvtk from apptools.persistence import state_pickler # Local imports from mayavi.core.module import Module from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `CubeAxesActor2D` class. ###################################################################### class CubeAxesActor2D(tvtk.CubeAxesActor2D): """ Just has a different view than the tvtk.CubesAxesActor2D, with an additional tick box. """ # Automaticaly fit the bounds of the axes to the data use_data_bounds = true input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ######################################## # The view of this object. traits_view = View(Group( Group( Item('visibility'), HGroup( Item('x_axis_visibility', label='X axis'), Item('y_axis_visibility', label='Y axis'), Item('z_axis_visibility', label='Z axis'), ), show_border=True, label='Visibity'), Group( Item('use_ranges'), HGroup( Item('ranges', enabled_when='use_ranges'), ), show_border=True), Group( Item('use_data_bounds'), HGroup( Item('bounds', enabled_when='not use_data_bounds'), ), show_border=True), Group( Item('x_label'), Item('y_label'), Item('z_label'), Item('label_format'), Item('number_of_labels'), Item('font_factor'), show_border=True), HGroup(Item('show_actual_bounds', label='Use size bigger than screen', editor=BooleanEditor())), Item('fly_mode'), Item('corner_offset'), Item('layer_number'), springy=True, ), scrollable=True, resizable=True, ) ###################################################################### # `Axes` class. ###################################################################### class Axes(Module): # The version of this class. Used for persistence. __version__ = 0 # The tvtk axes actor. axes = Instance(CubeAxesActor2D, allow_none=False, record=True) # The property of the axes (color etc.). property = Property(record=True) # The title text property of the axes. title_text_property = Property(record=True) # The label text property of the axes. label_text_property = Property(record=True) ######################################## # Private traits. _property = Instance(tvtk.Property2D) _title_text_property = Instance(tvtk.TextProperty) _label_text_property = Instance(tvtk.TextProperty) ######################################## # The view of this object. view = View(Group(Item(name='axes', style='custom', resizable=True), label='Axes', show_labels=False), Group(Item(name='_property', style='custom', resizable=True), label='Property', show_labels=False), Group(Item(name='_title_text_property', style='custom', resizable=True), label='Title Text', show_labels=False), Group(Item(name='_label_text_property', style='custom', resizable=True), label='Label Text', show_labels=False), scrollable=True, resizable=True, width=500, height=600 ) ###################################################################### # `object` interface ###################################################################### def __set_pure_state__(self, state): for prop in ['axes', '_property', '_title_text_property', '_label_text_property']: obj = getattr(self, prop) state_pickler.set_state(obj, state[prop]) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the axes and set things up. axes = CubeAxesActor2D(number_of_labels= 2, font_factor=1.5, fly_mode='outer_edges', corner_offset=0.0, scaling=False) axes.axis_title_text_property.shadow = False axes.axis_label_text_property.shadow = False # Set the axes. self.axes = axes def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None or not self.axes.use_data_bounds: self.axes.input = None return src = mm.source self.axes.input = src.outputs[0] self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the component should do the rest. self.data_changed = True ###################################################################### # Non-public interface ###################################################################### def _scene_changed(self, old, new): super(Axes, self)._scene_changed(old, new) self.axes.camera = new.camera self._foreground_changed_for_scene(None, new.foreground) def _foreground_changed_for_scene(self, old, new): # Change the default color for the actor. self.property.color = new self.label_text_property.color = new self.title_text_property.color = new self.render() def _axes_changed(self, old, new): if old is not None: for obj in (old, self._property, self._title_text_property, self._label_text_property): obj.on_trait_change(self.render, remove=True) self.actors.remove(old) # Setup the axes. scene = self.scene if scene is not None: new.camera = scene.camera # Setup the private traits. self._property = new.property for prop in ['_title_text_property', '_label_text_property']: setattr(self, prop, getattr(new, 'axis' + prop)) # The handlers. for obj in (new, self._property, self._title_text_property, self._label_text_property): obj.on_trait_change(self.render) self.actors.append(new) if old is not None: self.update_pipeline() def _get_property(self): return self._property def _get_title_text_property(self): return self._title_text_property def _get_label_text_property(self): return self._label_text_property def _use_data_bounds_changed_for_axes(self): """ Updating the pipeline for this module is inexpensive and fits the actor to the source (if any). We are using it here. """ self.update_pipeline() mayavi-4.1.0/mayavi/modules/grid_plane.py0000644000175100001440000000752311674464502021417 0ustar ischnellusers00000000000000"""A simple grid plane module. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item # Local imports from mayavi.core.module import Module from mayavi.components import grid_plane from mayavi.components.actor import Actor from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `GridPlane` class. ###################################################################### class GridPlane(Module): # The version of this class. Used for persistence. __version__ = 0 grid_plane = Instance(grid_plane.GridPlane, allow_none=False, record=True) actor = Instance(Actor, allow_non=False, record=True) input_info = PipelineInfo(datasets=['image_data', 'structured_grid', 'rectilinear_grid'], attribute_types=['any'], attributes=['any']) view = View(Group(Item(name='grid_plane', style='custom'), Item(name='actor', style='custom'), show_labels=False)) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the components self.grid_plane = grid_plane.GridPlane() self.actor = Actor() # Setup the actor suitably for this module. prop = self.actor.property prop.set(backface_culling=0, frontface_culling=0, representation='w') self.actor.mapper.scalar_visibility = 0 def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return # Data is available, so set the input for the grid plane. self.grid_plane.inputs = [mm.source] # Set the LUT for the mapper. self.actor.set_lut(mm.scalar_lut_manager.lut) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the component should do the rest. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _grid_plane_changed(self, old, new): actor = self.actor if actor is not None: actor.inputs = [new] self._change_components(old, new) def _actor_changed(self, old, new): new.scene = self.scene gp = self.grid_plane if gp is not None: new.inputs = [gp] self._change_components(old, new) mayavi-4.1.0/mayavi/modules/streamline.py0000644000175100001440000002275511674464502021462 0ustar ischnellusers00000000000000"""Allows the user to draw streamlines for given vector data. This supports various types of seed objects (line, sphere, plane and point seeds). It also allows the user to draw ribbons or tubes and further supports different types of interactive modes of calculating the streamlines. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. from math import sqrt # Enthought library imports. from traits.api import Instance, Bool, TraitPrefixList, Trait, \ Delegate, Button from traitsui.api import View, Group, Item, InstanceEditor from tvtk.api import tvtk # Local imports from mayavi.core.module import Module from mayavi.core.pipeline_info import PipelineInfo from mayavi.components.actor import Actor from mayavi.components.source_widget import SourceWidget ###################################################################### # `Streamline` class. ###################################################################### class Streamline(Module): # The version of this class. Used for persistence. __version__ = 0 # The streamline generator. stream_tracer = Instance(tvtk.StreamTracer, allow_none=False, record=True) # The seed for the streamlines. seed = Instance(SourceWidget, allow_none=False, record=True) # The update mode of the seed -- this is delegated to the # SourceWidget. update_mode = Delegate('seed', modify=True) # Determines if the streamlines are shown as lines or ribbons or # tubes. streamline_type = Trait('line', TraitPrefixList(['line', 'ribbon', 'tube']), desc='draw streamlines as lines/ribbons/tubes') # The ribbon filter. ribbon_filter = Instance(tvtk.RibbonFilter, allow_none=False, record=True) # The tube filter. tube_filter = Instance(tvtk.TubeFilter, allow_none=False, record=True) # The actor component that represents the visualization. actor = Instance(Actor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']) ######################################## # Private traits. _first = Bool(True) ######################################## # View related code. # A button to update the streamlines. update_streamlines = Button('Update Streamlines') _tube_group = Group(Item(name='capping'), Item(name='sides_share_vertices'), Item(name='vary_radius'), Item(name='number_of_sides'), Item(name='radius'), Item(name='radius_factor'), Item(name='offset'), Item(name='on_ratio') ) _ribbon_group = Group(Item(name='vary_width'), Item(name='width'), Item(name='width_factor'), Item(name='angle') ) view = View(Group(Group(Item(name='update_mode'), ), Group(Item(name='update_streamlines'), show_labels=False, ), Group(Item(name='streamline_type'), Item(name='ribbon_filter', style='custom', visible_when='object.streamline_type == "ribbon"', editor=InstanceEditor(view=View(_ribbon_group))), Item(name='tube_filter', style='custom', visible_when='object.streamline_type == "tube"', editor=InstanceEditor(view=View(_tube_group))), show_labels=False, label='Streamline' ), label='Streamline' ), Group(Item(name='seed', style='custom', resizable=True), label='Seed', show_labels=False), Group(Item(name='stream_tracer', style='custom', resizable=True), label='StreamTracer', show_labels=False), Group(Item(name='actor', style='custom'), label='Actor', show_labels=False), resizable=True ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create and setup the default objects. self.seed = SourceWidget() self.stream_tracer = tvtk.StreamTracer(maximum_propagation=50, integration_direction='forward', compute_vorticity=True, integrator_type='runge_kutta4', ) self.ribbon_filter = tvtk.RibbonFilter() self.tube_filter = tvtk.TubeFilter() self.actor = Actor() # Setup the actor suitably for this module. self.actor.property.line_width = 2.0 def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return src = mm.source self.stream_tracer.input = src.outputs[0] self.seed.inputs = [src] # Setup the radius/width of the tube/ribbon filters based on # given input. if self._first: b = src.outputs[0].bounds l = [(b[1]-b[0]), (b[3]-b[2]), (b[5]-b[4])] length = sqrt(l[0]*l[0] + l[1]*l[1] + l[2]*l[2]) self.ribbon_filter.width = length*0.0075 self.tube_filter.radius = length*0.0075 self._first = False self._streamline_type_changed(self.streamline_type) # Set the LUT for the mapper. self.actor.set_lut(mm.scalar_lut_manager.lut) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the components should do the rest if # they are connected. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _streamline_type_changed(self, value): if self.module_manager is None: return st = self.stream_tracer rf = self.ribbon_filter tf = self.tube_filter if value == 'line': self.outputs = [st.output] elif value == 'ribbon': rf.input = st.output self.outputs = [rf.output] elif value == 'tube': tf.input = st.output self.outputs = [tf.output] self.render() def _update_streamlines_fired(self): self.seed.update_poly_data() self.render() def _stream_tracer_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) seed = self.seed if seed is not None: new.source = seed.poly_data new.on_trait_change(self.render) mm = self.module_manager if mm is not None: new.input = mm.source.outputs[0] # A default output so there are no pipeline errors. The # update_pipeline call corrects this if needed. self.outputs = [new.output] self.update_pipeline() def _seed_changed(self, old, new): st = self.stream_tracer if st is not None: st.source = new.poly_data self._change_components(old, new) def _ribbon_filter_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) self._streamline_type_changed(self.streamline_type) def _tube_filter_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) self._streamline_type_changed(self.streamline_type) def _actor_changed(self, old, new): new.scene = self.scene new.inputs = [self] self._change_components(old, new) mayavi-4.1.0/mayavi/modules/text3d.py0000644000175100001440000001422011674464502020516 0ustar ischnellusers00000000000000""" This module allows the user to place text in 3D at a location on the scene. Unlike the 'Text' module, this module positions text in 3D in the scene, and in 2D on the screen. As a result the text resizes with the figure, and can be masked by objects in the foreground. """ # Author: Gael Varoquaux # Copyright (c) 2009, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Str, CArray, Bool from traitsui.api import View, Group, Item # Local imports from tvtk.api import tvtk from mayavi.core.module import Module from mayavi.core.pipeline_info import PipelineInfo from mayavi.components.actor import Actor ###################################################################### # `Text3D` class. ###################################################################### class Text3D(Module): # The version of this class. Used for persistence. __version__ = 0 # The Mayavi Actor. actor = Instance(Actor, allow_none=False, record=True) # And the text source vector_text = Instance(tvtk.VectorText, allow_none=False, record=True) # The text to be displayed. text = Str('Text', desc='the text to be displayed', enter_set=True, auto_set=False) # The position of the actor position = CArray(value=(0., 0., 0.), cols=3, desc='the world coordinates of the text', enter_set=True, auto_set=False) # The scale of the actor scale = CArray(value=(1., 1., 1.), cols=3, desc='the scale of the text', enter_set=True, auto_set=False) # The orientation of the actor orientation = CArray(value=(0., 0., 0.), cols=3, desc='the orientation angles of the text', enter_set=True, auto_set=False) # Orient actor to camera orient_to_camera = Bool(True, desc='if the text is kept facing the camera') input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ######################################## # The view of this object. view = View(Group(Item(name='text'), Group(Item(name='position'), show_labels=False, show_border=True, label='Position'), Group(Item(name='scale'), show_labels=False, show_border=True, label='Scale'), Group( Item(name='orient_to_camera'), Item(name='orientation', label='Angles'), show_border=True, label='Orientation'), label='Text', ), Group(Item(name='actor', style='custom', show_label=False), label='Actor'), ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ self.vector_text = tvtk.VectorText(text=self.text) self.outputs = [self.vector_text.output] self.actor = Actor() self._text_changed(self.text) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ self.pipeline_changed = True ###################################################################### # Non-public interface ###################################################################### def _text_changed(self, value): vector_text = self.vector_text if vector_text is None: return vector_text.text = str(value) self.render() def _actor_changed(self, old, new): new.scene = self.scene new.inputs = [self] self._change_components(old, new) old_actor = None if old is not None: old_actor = old.actor new.actor = self._get_actor_or_follower(old=old_actor) self.actors = new.actors self.render() def _orient_to_camera_changed(self): self.actor.actor = \ self._get_actor_or_follower(old=self.actor.actor) def _get_actor_or_follower(self, old=None): """ Get a tvtk.Actor or a tvtk.Follower for the actor of the object and wire the callbacks to it. If old is given, it is the old actor, to remove its callbacks. """ if self.orient_to_camera: new = tvtk.Follower() if self.scene is not None: new.camera = self.scene.camera else: new = tvtk.Actor() if old is not None: self.sync_trait('position', old, 'position', remove=True) self.sync_trait('scale', old, 'scale', remove=True) self.sync_trait('orientation', old, 'orientation', remove=True) self.sync_trait('position', new, 'position') self.sync_trait('scale', new, 'scale') self.sync_trait('orientation', new, 'orientation') return new def _scene_changed(self, old, new): super(Text3D, self)._scene_changed(old, new) if new is not None and self.orient_to_camera: self.actor.actor.camera = new.camera mayavi-4.1.0/mayavi/modules/tensor_glyph.py0000644000175100001440000000767111674464502022034 0ustar ischnellusers00000000000000""" Displays tensor glyphs oriented and colored as per scalar or vector data at the input points. """ # Authors: KK Rai (kk.rai [at] iitb.ac.in) # R. Ambareesha (ambareesha [at] iitb.ac.in) # Prabhu Ramachandran # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item # Local imports from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.module import Module from mayavi.components.actor import Actor from mayavi.components import glyph ###################################################################### # `TensorGlyph` class. ###################################################################### class TensorGlyph(Module): # The version of this class. Used for persistence. __version__ = 0 # The glyph component we use to do the actual glyphing. glyph = Instance(glyph.Glyph, allow_none=False, record=True) # The actor. actor = Instance(Actor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['tensors']) # Create the UI for the traits. view = View(Group(Item(name='actor', style='custom'), show_labels=False, label='Actor'), Group(Item(name='glyph', style='custom', resizable=True), label='Tensor Glyph', selected=True, show_labels=False )) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Setup the glyphs. self.glyph = glyph.Glyph(glyph_type='tensor') self.glyph.glyph_source.glyph_source = self.glyph.glyph_source.glyph_list[4] self.actor = Actor() def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return self.glyph.inputs = [mm.source] self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the component should do the rest. self.data_changed = True ###################################################################### # Non-public methods. ###################################################################### def _glyph_changed(self, old, new): # Set the glyph's module attribute -- this is important! new.module = self # Setup actors inputs. actor = self.actor if actor is not None: actor.inputs = [new] self._change_components(old, new) def _actor_changed(self, old, new): new.scene = self.scene #new.inputs = [self] g = self.glyph if g is not None: new.inputs = [g] self._change_components(old, new) mayavi-4.1.0/mayavi/modules/custom_grid_plane.py0000644000175100001440000000420211674464502023000 0ustar ischnellusers00000000000000"""A custom grid plane with a lot more flexibility than GridPlane. This also only works for non-unstructured/non-polygonal datasets. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item # Local imports from mayavi.components import custom_grid_plane from mayavi.modules.contour_grid_plane import \ ContourGridPlane, Contour, Actor ###################################################################### # `CustomGridPlane` class. ###################################################################### class CustomGridPlane(ContourGridPlane): grid_plane = Instance(custom_grid_plane.Component, allow_none=False, record=True) # Overriding the ContourGridPlane's default view. view = View(Group(Item(name='grid_plane', style='custom'), show_labels=False, label='GridPlane'), Group(Group(Item(name='enable_contours')), Group(Item(name='contour', style='custom', enabled_when='object.enable_contours'), show_labels=False, ), label='Contour', ), Group(Item(name='actor', style='custom'), label='Actor', show_labels=False) ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): # Note that we don't call the parent class method here. This # is intentional to avoid problems with the execution of the # VTK pipeline. # Create the components. self.grid_plane = custom_grid_plane.CustomGridPlane() self.contour = Contour(auto_contours=True, number_of_contours=10) self.actor = Actor() self.enable_contours = False self.actor.property.point_size = 2 mayavi-4.1.0/mayavi/modules/__init__.py0000644000175100001440000000013211674464502021037 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/modules/text.py0000644000175100001440000002637711674464502020307 0ustar ischnellusers00000000000000"""This module allows the user to place text on the screen. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Range, Str, Bool, Property, \ Float from traitsui.api import View, Group, Item, InstanceEditor from tvtk.api import tvtk from apptools.persistence import state_pickler # Local imports from mayavi.core.module import Module from mayavi.core.pipeline_info import PipelineInfo VTK_VER = float(tvtk.Version().vtk_version[:3]) ###################################################################### # `Text` class. ###################################################################### class Text(Module): # The version of this class. Used for persistence. __version__ = 0 # The tvtk TextActor. actor = Instance(tvtk.TextActor, allow_none=False, record=True) # The property of the axes (color etc.). property = Property(record=True) # The text to be displayed. Note that this should really be `Str` # but wxGTK only returns unicode. text = Str('Text', desc='the text to be displayed') # The x-position of this actor. x_position = Float(0.0, desc='the x-coordinate of the text') # The y-position of this actor. y_position = Float(0.0, desc='the y-coordinate of the text') # The z-position of this actor. z_position = Float(0.0, desc='the z-coordinate of the text') # Shadow the positions as ranges for 2D. Simply using a RangeEditor # does not work as it resets the 3D positions to 1 when the dialog is # loaded. _x_position_2d = Range(0., 1., 0., enter_set=True, auto_set=False, desc='the x-coordinate of the text') _y_position_2d = Range(0., 1., 0., enter_set=True, auto_set=False, desc='the y-coordinate of the text') # 3D position position_in_3d = Bool(False, desc='whether the position of the object is given in 2D or in 3D') # The width of the text. width = Range(0.0, 1.0, 0.4, enter_set=True, auto_set=False, desc='the width of the text as a fraction of the viewport') input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ######################################## # The view of this object. if VTK_VER > 5.1: _text_actor_group = Group(Item(name='visibility'), Item(name='text_scale_mode'), Item(name='alignment_point'), Item(name='minimum_size'), Item(name='maximum_line_height'), show_border=True, label='Text Actor') else: _text_actor_group = Group(Item(name='visibility'), Item(name='scaled_text'), Item(name='alignment_point'), Item(name='minimum_size'), Item(name='maximum_line_height'), show_border=True, label='Text Actor') _position_group_2d = Group(Item(name='_x_position_2d', label='X position'), Item(name='_y_position_2d', label='Y position'), visible_when='not position_in_3d') _position_group_3d = Group(Item(name='x_position', label='X', springy=True), Item(name='y_position', label='Y', springy=True), Item(name='z_position', label='Z', springy=True), show_border=True, label='Position', orientation='horizontal', visible_when='position_in_3d') view = View(Group(Group(Item(name='text'), Item(name='position_in_3d'), _position_group_2d, _position_group_3d, Item(name='width', enabled_when='object.actor.scaled_text'), ), Group(Item(name='actor', style='custom', editor=\ InstanceEditor(view=View(_text_actor_group)) ), show_labels=False), label='TextActor', show_labels=False ), Group(Item(name='_property', style='custom', resizable=True), label='TextProperty', show_labels=False), ) ######################################## # Private traits. _updating = Bool(False) _property = Instance(tvtk.TextProperty) ###################################################################### # `object` interface ###################################################################### def __set_pure_state__(self, state): self._updating = True state_pickler.set_state(self, state, first=['actor'], ignore=['_updating']) self._updating = False ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ actor = self.actor = tvtk.TextActor(input=str(self.text)) if VTK_VER > 5.1: actor.set(text_scale_mode='prop', width=0.4, height=1.0) else: actor.set(scaled_text=True, width=0.4, height=1.0) c = actor.position_coordinate c.set(coordinate_system='normalized_viewport', value=(self.x_position, self.y_position, 0.0)) c = actor.position2_coordinate c.set(coordinate_system='normalized_viewport') self._property.opacity = 1.0 self._text_changed(self.text) self._width_changed(self.width) self._shadow_positions(True) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the component should do the rest. self.data_changed = True ###################################################################### # Non-public interface ###################################################################### def _text_changed(self, value): actor = self.actor if actor is None: return if self._updating: return actor.input = str(value) self.render() def _shadow_positions(self, value): self.sync_trait('x_position', self, '_x_position_2d', remove=(not value)) self.sync_trait('y_position', self, '_y_position_2d', remove=(not value)) if not value: self._x_position_2d = self.x_position self._y_position_2d = self.y_position def _position_in_3d_changed(self, value): if value: self.actor.position_coordinate.coordinate_system='world' self.actor.position2_coordinate.coordinate_system='world' else: self.actor.position2_coordinate.coordinate_system=\ 'normalized_viewport' self.actor.position_coordinate.coordinate_system=\ 'normalized_viewport' x = self.x_position y = self.y_position if x < 0: x = 0 elif x > 1: x = 1 if y < 0: y = 0 elif y > 1: y = 1 self.set(x_position=x, y_position=y, trait_change_notify=False) self._shadow_positions(not value) self._change_position() self.actor._width_changed(self.width, self.width) self.pipeline_changed = True def _change_position(self): """ Callback for _x_position, _y_position, and z_position. """ actor = self.actor if actor is None: return if self._updating: return x = self.x_position y = self.y_position z = self.z_position if self.position_in_3d: actor.position_coordinate.value = x, y, z else: actor.position = x, y self.render() _x_position_changed = _change_position _y_position_changed = _change_position _z_position_changed = _change_position def _width_changed(self, value): actor = self.actor if actor is None: return if self._updating: return actor.width = value self.render() def _update_traits(self): self._updating = True try: actor = self.actor self.text = actor.input pos = actor.position self.x_position, self.y_position = pos self.width = actor.width finally: self._updating = False def _get_property(self): return self._property def _actor_changed(self, old, new): if old is not None: for obj in (old, self._property): obj.on_trait_change(self.render, remove=True) old.on_trait_change(self._update_traits, remove=True) self._property = new.text_property for obj in (new, self._property): obj.on_trait_change(self.render) new.on_trait_change(self._update_traits) self.actors = [new] self.render() def _foreground_changed_for_scene(self, old, new): # Change the default color for the actor. self.property.color = new self.render() def _scene_changed(self, old, new): super(Text, self)._scene_changed(old, new) self._foreground_changed_for_scene(None, new.foreground) mayavi-4.1.0/mayavi/modules/orientation_axes.py0000644000175100001440000001745211674464502022670 0ustar ischnellusers00000000000000"""Creates a small axes on the side that indicates the position of the co-ordinate axes and thereby marks the orientation of the scene. It uses the OrientationMarkerWidget which requires VTK-4.5 and above. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Prabhu Ramachandran, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance, Property from traitsui.api import View, Group, Item, InstanceEditor from tvtk.api import tvtk from apptools.persistence import state_pickler # Local imports from mayavi.core.module import Module from mayavi.core.common import error from mayavi.core.pipeline_info import PipelineInfo if not hasattr(tvtk, 'OrientationMarkerWidget'): msg = 'The OrientationAxes module requires VTK version >= 4.5' error(msg) raise ImportError, msg ###################################################################### # `OrientationAxes` class. ###################################################################### class OrientationAxes(Module): # The version of this class. Used for persistence. __version__ = 0 # The tvtk orientation marker widget. marker = Instance(tvtk.OrientationMarkerWidget, allow_none=False) # The tvtk axes that will be shown. axes = Instance(tvtk.AxesActor, allow_none=False, record=True) # The property of the axes (color etc.). text_property = Property(record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ######################################## # Private traits. _text_property = Instance(tvtk.TextProperty) ######################################## # The view of this object. _marker_group = Group(Item(name='enabled'), Item(name='interactive'), show_border=True, label='Widget') _axes_group = Group(Item(name='axis_labels'), Item(name='visibility'), Item(name='x_axis_label_text'), Item(name='y_axis_label_text'), Item(name='z_axis_label_text'), Item(name='cone_radius'), Item(name='cone_resolution'), Item(name='cylinder_radius'), Item(name='cylinder_resolution'), Item(name='normalized_label_position'), Item(name='normalized_shaft_length'), Item(name='normalized_tip_length'), Item(name='total_length'), show_border=True, label='Axes') view = View(Group(Item(name='marker', style='custom', editor=InstanceEditor(view=View(_marker_group))), Item(name='axes', style='custom', editor=InstanceEditor(view=View(_axes_group))), label='Widget/Axes', show_labels=False), Group(Item(name='_text_property', style='custom', resizable=True), label='Text Property', show_labels=False), ) ###################################################################### # `object` interface ###################################################################### def __set_pure_state__(self, state): for prop in ['axes', 'marker', '_text_property']: obj = getattr(self, prop) state_pickler.set_state(obj, state[prop]) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Setup the default objects. self.axes = tvtk.AxesActor(normalized_tip_length=(0.4, 0.4, 0.4), normalized_shaft_length=(0.6, 0.6, 0.6), shaft_type='cylinder') self.text_property.set(color=(1,1,1), shadow=False, italic=False) self.marker = tvtk.OrientationMarkerWidget(key_press_activation=False) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the component should do the rest. self.data_changed = True ###################################################################### # Non-public interface ###################################################################### def _marker_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) self.widgets.remove(old) axes = self.axes if axes is not None: new.orientation_marker = axes new.on_trait_change(self.render) self.widgets.append(new) self.render() def _axes_changed(self, old, new): if old is not None: old.on_trait_change(self.render, remove=True) self._text_property.on_trait_change(self.render, remove=True) marker = self.marker if marker is not None: marker.orientation_marker = new p = new.x_axis_caption_actor2d.caption_text_property new.y_axis_caption_actor2d.caption_text_property = p new.z_axis_caption_actor2d.caption_text_property = p self._text_property = p # XXX: The line of code below is a stop-gap solution. Without it, # Some observers in the AxesActor trigger a modification of the # font_size each time the mouse is moved over the OrientationAxes # (this can be seen when running the record mode, for instance), # and thus a render, which is very slow. On the other hand, font # size does not work for the AxesActor, with or without the # line of code below. So we probably haven't found the true # cause of the problem. p.teardown_observers() new.on_trait_change(self.render) p.on_trait_change(self.render) self.render() def _get_text_property(self): return self._text_property def _foreground_changed_for_scene(self, old, new): # Change the default color for the actor. self.text_property.color = new self.render() def _scene_changed(self, old, new): super(OrientationAxes, self)._scene_changed(old, new) self._foreground_changed_for_scene(None, new.foreground) self._visible_changed(self.visible) def _visible_changed(self, value): if self.scene is not None and self.marker.interactor: # Enabling an OrientationAxes without an interactor will # lead to a segfault super(OrientationAxes, self)._visible_changed(value) mayavi-4.1.0/mayavi/modules/vector_cut_plane.py0000644000175100001440000001444111674464502022644 0ustar ischnellusers00000000000000"""Takes an arbitrary slice of the input data using an implicit cut plane and places glyphs according to the vector field data. The glyphs may be colored using either the vector magnitude or the scalar attributes. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item # Local imports from mayavi.core.pipeline_info import PipelineInfo from mayavi.core.module import Module from mayavi.components.implicit_plane import ImplicitPlane from mayavi.components.cutter import Cutter from mayavi.components.glyph import Glyph from mayavi.components.actor import Actor ###################################################################### # `VectorCutPlane` class. ###################################################################### class VectorCutPlane(Module): # The version of this class. Used for persistence. __version__ = 0 # The implicit plane widget used to place the implicit function. implicit_plane = Instance(ImplicitPlane, allow_none=False, record=True) # The cutter. Takes a cut of the data on the implicit plane. cutter = Instance(Cutter, allow_none=False, record=True) # The Glyph component. glyph = Instance(Glyph, allow_none=False, record=True) # The Glyph component. actor = Instance(Actor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['vectors']) ######################################## # View related traits. view = View(Group(Item(name='implicit_plane', style='custom'), label='ImplicitPlane', show_labels=False), Group(Item(name='glyph', style='custom', resizable=True), label='Glyph', show_labels=False), Group(Item(name='actor', style='custom'), label='Actor', show_labels=False), ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the objects and set them up. self.implicit_plane = ImplicitPlane() self.cutter = Cutter() self.glyph = Glyph(module=self, scale_mode='scale_by_vector', color_mode='color_by_vector', show_scale_mode=False) self.glyph.glyph_source.glyph_position='tail' actor = self.actor = Actor() actor.mapper.scalar_visibility = 1 actor.property.set(line_width=2, backface_culling=False, frontface_culling=False) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return self.implicit_plane.inputs = [mm.source] # Set the LUT for the mapper. self._color_mode_changed(self.glyph.color_mode) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the other components should do the rest. self.data_changed = True ###################################################################### # Non-public traits. ###################################################################### def _color_mode_changed(self, value): # This is a listner for the glyph component's color_mode trait # so that the the lut can be changed when the a different # color mode is requested. actor = self.actor if value == 'color_by_scalar': actor.mapper.scalar_visibility = 1 lut_mgr = self.module_manager.scalar_lut_manager actor.set_lut(lut_mgr.lut) elif value == 'color_by_vector': lut_mgr = self.module_manager.vector_lut_manager actor.set_lut(lut_mgr.lut) else: actor.mapper.scalar_visibility = 0 self.render() def _implicit_plane_changed(self, old, new): cutter = self.cutter if cutter is not None: cutter.cut_function = new.plane cutter.inputs = [new] self._change_components(old, new) def _cutter_changed(self, old, new): ip = self.implicit_plane if ip is not None: new.cut_function = ip.plane new.inputs = [ip] g = self.glyph if g is not None: g.inputs = [new] self._change_components(old, new) def _glyph_changed(self, old, new): if old is not None: old.on_trait_change(self._color_mode_changed, 'color_mode', remove=True) new.module = self cutter = self.cutter if cutter: new.inputs = [cutter] new.on_trait_change(self._color_mode_changed, 'color_mode') self._change_components(old, new) def _actor_changed(self, old, new): new.scene = self.scene glyph = self.glyph if glyph is not None: new.inputs = [glyph] self._change_components(old, new) mayavi-4.1.0/mayavi/modules/glyph.py0000644000175100001440000001235511674464502020435 0ustar ischnellusers00000000000000"""Displays different types of glyphs oriented and colored as per scalar or vector data at the input points. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from traitsui.api import View, Group, Item # Local imports from mayavi.core.module import Module from mayavi.components import glyph from mayavi.components.actor import Actor from mayavi.core.pipeline_info import PipelineInfo ###################################################################### # `Glyph` class. ###################################################################### class Glyph(Module): # The version of this class. Used for persistence. __version__ = 0 # The Glyph component. glyph = Instance(glyph.Glyph, allow_none=False, record=True) # The Glyph component. actor = Instance(Actor, allow_none=False, record=True) input_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) ######################################## # View related traits. view = View(Group(Item(name='glyph', style='custom', resizable=True), label='Glyph', show_labels=False), Group(Item(name='actor', style='custom'), label='Actor', show_labels=False), ) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Setup the glyphs. self.glyph = glyph.Glyph(scale_mode='scale_by_scalar', color_mode='color_by_scalar', show_scale_mode=True) # Create the components actor = self.actor = Actor() actor.mapper.scalar_visibility = 1 actor.property.set(line_width=2, backface_culling=False, frontface_culling=False) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None: return self.glyph.inputs = [mm.source] # Set the LUT for the mapper. self._color_mode_changed(self.glyph.color_mode) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the other components should do the rest. self.data_changed = True ###################################################################### # Non-public traits. ###################################################################### def _color_mode_changed(self, value): # This is a listner for the glyph component's color_mode trait # so that the the lut can be changed when the a different # color mode is requested. if self.module_manager is None: return actor = self.actor if value == 'color_by_scalar': actor.mapper.scalar_visibility = 1 lut_mgr = self.module_manager.scalar_lut_manager actor.set_lut(lut_mgr.lut) elif value == 'color_by_vector': lut_mgr = self.module_manager.vector_lut_manager actor.set_lut(lut_mgr.lut) else: actor.mapper.scalar_visibility = 0 self.render() def _glyph_changed(self, old, new): # Hookup a callback to set the lut appropriately. if old is not None: old.on_trait_change(self._color_mode_changed, 'color_mode', remove=True) new.on_trait_change(self._color_mode_changed, 'color_mode') # Set the glyph's module attribute -- this is important! new.module = self # Setup actors inputs. actor = self.actor if actor is not None: actor.inputs = [new] self._change_components(old, new) def _actor_changed(self, old, new): # Setup the actors scene and inputs. new.scene = self.scene g = self.glyph if g is not None: new.inputs = [g] self._change_components(old, new) mayavi-4.1.0/mayavi/modules/structured_grid_outline.py0000644000175100001440000000164511674464502024262 0ustar ischnellusers00000000000000"""Draws a grid-conforming outline for structured grids. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Instance from tvtk.api import tvtk # Local imports. from mayavi.components.actor import Actor from mayavi.modules.outline import Outline ###################################################################### # `StructuredGridOutline` class. ###################################################################### class StructuredGridOutline(Outline): """Draws a grid-conforming outline for structured grids. """ # The outline filter. outline_filter = Instance(tvtk.StructuredGridOutlineFilter, allow_none = False, record=True) def setup_pipeline(self): self.outline_filter = tvtk.StructuredGridOutlineFilter() self.actor = Actor() mayavi-4.1.0/mayavi/plugins/0000755000175100001440000000000011674464502016743 5ustar ischnellusers00000000000000mayavi-4.1.0/mayavi/plugins/envisage_engine.py0000644000175100001440000001252311674464502022446 0ustar ischnellusers00000000000000"""The MayaVi Engine meant to be used with Envisage. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. # Standard library imports. import logging # Enthought library imports. from traits.api import Instance, on_trait_change, Str from tvtk.plugins.scene.i_scene_manager import \ ISceneManager from tvtk.plugins.scene.ui.actions import NewScene from tvtk.plugins.scene import scene_editor from pyface.api import GUI from pyface.workbench.api import WorkbenchWindow from apptools.scripting.api import recordable # Local imports. from mayavi.core.engine import Engine logger = logging.getLogger() ###################################################################### # `EnvisageEngine` class ###################################################################### class EnvisageEngine(Engine): # The version of this class. Used for persistence. __version__ = 0 # The envisage application. window = Instance(WorkbenchWindow) # Our name. name = Str('Mayavi Envisage Engine') ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(EnvisageEngine, self).__get_pure_state__() for x in ['window',]: d.pop(x, None) return d ###################################################################### # `Engine` interface ###################################################################### def start(self): """This starts the engine. Only after the engine starts is it possible to use mayavi. This particular method is called automatically when the window is opened.""" if self.running: return # Add all the existing scenes from the scene plugin. scene_manager = self.window.get_service(ISceneManager) for scene in scene_manager.scenes: self.add_scene(scene) # Setup a handler that is invoked when a new Scene is # added/removed. scene_manager.on_trait_change(self._scene_editors_changed, 'scenes_items') # Call the parent start method. super(EnvisageEngine, self).start() logger.debug ('--------- EnvisageEngine started ----------') def stop(self): # Call the parent stop method. super(EnvisageEngine, self).stop() @recordable def new_scene(self, name=None, **kwargs): """ Creates a new VTK scene window. For the time being the extra kwargs are ignored with the envisage engine. """ action = NewScene(window=self.window) editor = action.perform(None) if name is not None: editor.name = name # Flush the UI. GUI.process_events() return self.scenes[-1] @recordable def close_scene(self, scene): """Given a VTK scene instance, this method closes it. """ active_window = self.window s = scene.scene for editor in active_window.editors[:]: if isinstance(editor, scene_editor.SceneEditor): if id(editor.scene) == id(s): editor.close() break # Flush the UI. GUI.process_events() ###################################################################### # Non-public interface ###################################################################### def _scene_editors_changed(self, list_event): """This is called when the items of the editors trait of the SceneManager change. This is used to update `self.scenes`.""" # Remove any removed scenes. for scene in list_event.removed: self.remove_scene(scene) # Add any new scenes. for scene in list_event.added: self.add_scene(scene) @on_trait_change('window:opened') def _on_window_opened(self, obj, trait_name, old, new): """We start the engine when the window is opened.""" if trait_name == 'opened': self.start() @on_trait_change('window:closed') def _on_window_closed(self, obj, trait_name, old, new): """We stop the engine when the window is closed.""" if trait_name == 'closed': self.stop() def _window_changed(self, old, new): """Static trait handler.""" # This is needed since the service may be offered *after* the # window is opened in which case the _on_window_opened will do # nothing. sm = new.get_service(ISceneManager) if sm is not None: self.start() @on_trait_change('window:editors[]') def _sync_scene_editor_name(self, obj, trait_name, old, new): """Synchronize the Mayavi scene's name trait with that of the editor's name.""" if trait_name.startswith('editors'): scenes = list(self.scenes) scenes.reverse() for editor in new: if not hasattr(editor, 'scene'): continue for scene in scenes: if id(editor.scene) == id(scene.scene): editor.name = scene.name scene.sync_trait('name', editor, 'name') break mayavi-4.1.0/mayavi/plugins/app.py0000644000175100001440000002110511674464502020074 0ustar ischnellusers00000000000000"""The Mayavi Envisage application. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. import sys import os.path import logging # Enthought library imports. from apptools.logger.api import LogFileHandler, FORMATTER from traits.etsconfig.api import ETSConfig from traits.api import (HasTraits, Instance, Int, on_trait_change, Bool) # Local imports. from mayavi_workbench_application import MayaviWorkbenchApplication from mayavi.preferences.api import preference_manager from mayavi.core.customize import get_custom_plugins # GLOBALS logger = logging.getLogger() ###################################################################### # Useful functions. ###################################################################### def setup_logger(logger, fname, stream=True, mode=logging.ERROR): """Setup a log file and the logger. If the given file name is not absolute, put the log file in `ETSConfig.application_home`, if not it will create it where desired. Parameters: ----------- fname -- file name the logger should use. If this is an absolute path it will create the log file as specified, if not it will put it in `ETSConfig.application_home`. stream -- Add a stream handler. mode -- the logging mode of the stream handler. """ if not os.path.isabs(fname): path = os.path.join(ETSConfig.application_home, fname) else: path = fname # Check if we have already added a logger (can happen when the app # is started multiple number of times from ipython say). handlers = logger.handlers if len(handlers) > 1: h = handlers[0] if isinstance(h, LogFileHandler) and h.baseFilename == path: logger.info('Logging handlers already set! Not duplicating.') return logger.setLevel(logging.DEBUG) handler = LogFileHandler(path) handler.setLevel(logging.DEBUG) logger.addHandler(handler) if stream: s = logging.StreamHandler() s.setFormatter(FORMATTER) s.setLevel(mode) logger.addHandler(s) logger.info("*"*80) logger.info("logfile is: '%s'", os.path.abspath(path)) logger.info("*"*80) def get_non_gui_plugin_classes(): """Get list of basic mayavi plugin classes that do not add any views or actions.""" from envisage.core_plugin import CorePlugin from envisage.ui.workbench.workbench_plugin import WorkbenchPlugin from tvtk.plugins.scene.scene_plugin import ScenePlugin from mayavi.plugins.mayavi_plugin import MayaviPlugin plugins = [CorePlugin, WorkbenchPlugin, MayaviPlugin, ScenePlugin, ] return plugins def get_non_gui_plugins(): """Get list of basic mayavi plugins that do not add any views or actions.""" return [cls() for cls in get_non_gui_plugin_classes()] def get_plugin_classes(): """Get list of default plugin classes to use for Mayavi.""" # Force the selection of a toolkit: from traitsui.api import toolkit toolkit() from traits.etsconfig.api import ETSConfig try_use_ipython = preference_manager.root.use_ipython use_ipython = False if ETSConfig.toolkit == 'wx' and try_use_ipython: try: # If the right versions of IPython, EnvisagePlugins and # Pyface are not installed, this import will fail. from envisage.plugins.ipython_shell.view.ipython_shell_view \ import IPythonShellView use_ipython = True except: pass if use_ipython: from envisage.plugins.ipython_shell.ipython_shell_plugin import \ IPythonShellPlugin PythonShellPlugin = IPythonShellPlugin else: from envisage.plugins.python_shell.python_shell_plugin import PythonShellPlugin from envisage.plugins.text_editor.text_editor_plugin import TextEditorPlugin from apptools.logger.plugin.logger_plugin import LoggerPlugin from tvtk.plugins.scene.ui.scene_ui_plugin import SceneUIPlugin from mayavi.plugins.mayavi_ui_plugin import MayaviUIPlugin plugins = get_non_gui_plugin_classes() plugins.extend([ LoggerPlugin, MayaviUIPlugin, SceneUIPlugin, PythonShellPlugin, TextEditorPlugin, ]) return plugins def get_plugins(): """Get list of default plugins to use for Mayavi.""" return [cls() for cls in get_plugin_classes()] ########################################################################### # `Mayavi` class. ########################################################################### class Mayavi(HasTraits): """The Mayavi application class. This class may be easily subclassed to do something different. For example, one way to script MayaVi (as a standalone application and not interactively) is to subclass this and do the needful. """ # The main envisage application. application = Instance('envisage.ui.workbench.api.WorkbenchApplication') # Turn this off if you don't want the workbench to start the GUI # event loop. start_gui_event_loop = Bool(True, desc='start a GUI event loop') # The MayaVi Script instance. script = Instance('mayavi.plugins.script.Script') # The logging mode. log_mode = Int(logging.ERROR, desc='the logging mode to use') def main(self, argv=None, plugins=None): """The main application is created and launched here. Parameters ---------- argv : list of strings The list of command line arguments. The default is `None` where no command line arguments are parsed. To support command line arguments you can pass `sys.argv[1:]`. plugins : list of Plugin objects List of plugins to start. If none is provided it defaults to something meaningful. log_mode : The logging mode to use. """ # Parse any cmd line args. if argv is None: argv = [] self.parse_command_line(argv) if plugins is None: plugins = get_plugins() plugins += get_custom_plugins() # Create the application prefs = preference_manager.preferences app = MayaviWorkbenchApplication(plugins=plugins, preferences=prefs, start_gui_event_loop=self.start_gui_event_loop) self.application = app # Setup the logger. self.setup_logger() # Start the application. app.run() def setup_logger(self): """Setup logging for the application.""" setup_logger(logger, 'mayavi.log', mode=self.log_mode) def parse_command_line(self, argv): """Parse command line options. Parameters ---------- - argv : `list` of `strings` The list of command line arguments. """ from optparse import OptionParser usage = "usage: %prog [options]" parser = OptionParser(usage) (options, args) = parser.parse_args(argv) def run(self): """This function is called after the GUI has started. Override this to do whatever you want to do as a MayaVi script. If this is not overridden then an empty MayaVi application will be started. *Make sure all other MayaVi specific imports are made here!* If you import MayaVi related code earlier you will run into difficulties. Use 'self.script' to script the mayavi engine. """ pass ###################################################################### # Non-public interface. ###################################################################### @on_trait_change('application.gui:started') def _on_application_gui_started(self, obj, trait_name, old, new): """This is called as soon as the Envisage GUI starts up. The method is responsible for setting our script instance. """ if trait_name != 'started' or not new: return app = self.application from mayavi.plugins.script import Script window = app.workbench.active_window # Set our script instance. self.script = window.get_service(Script) # Call self.run from the GUI thread. app.gui.invoke_later(self.run) def main(argv=None): """Simple helper to start up the mayavi application. This returns the running application.""" m = Mayavi() m.main(argv) return m if __name__ == '__main__': main(sys.argv[1:]) mayavi-4.1.0/mayavi/plugins/mayavi_ui_plugin.py0000644000175100001440000001736111674464502022666 0ustar ischnellusers00000000000000"""The Mayavi UI plugin """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. import logging # Enthought library imports. from traits.api import List, on_trait_change from envisage.api import Plugin from pyface.workbench.api import Perspective, PerspectiveItem from traits.etsconfig.api import ETSConfig logger = logging.getLogger() # View IDs. ENGINE_VIEW = 'mayavi.core.ui.engine_view.EngineView' CURRENT_SELECTION_VIEW = 'mayavi.core.engine.Engine.current_selection' SHELL_VIEW = 'envisage.plugins.python_shell_view' LOGGER_VIEW = 'apptools.logger.plugin.view.logger_view.LoggerView' ############################################################################### # `MayaviPerspective` class. ############################################################################### class MayaviPerspective(Perspective): """ A default perspective for Mayavi. """ # The perspective's name. name = 'Mayavi' # Should this perspective be enabled or not? enabled = True # Should the editor area be shown in this perspective? show_editor_area = True # The contents of the perspective. contents = List() def _contents_default(self): contents = [ PerspectiveItem(id=ENGINE_VIEW, position='left'), PerspectiveItem(id=CURRENT_SELECTION_VIEW, position='bottom', relative_to=ENGINE_VIEW), PerspectiveItem(id=SHELL_VIEW, position='bottom'), ] show_logger = True if ETSConfig.toolkit == 'wx': # XXX: Bugware: avoid a crash in Wx with the logger import wx if wx.__version__.split('.')[:2] == ['2', '6']: show_logger = False if show_logger: contents.append(PerspectiveItem(id=LOGGER_VIEW, position='with', relative_to=SHELL_VIEW)) return contents ############################################################################### # `MayaviUIPlugin` class. ############################################################################### class MayaviUIPlugin(Plugin): # Extension point Ids. VIEWS = 'envisage.ui.workbench.views' PERSPECTIVES = 'envisage.ui.workbench.perspectives' PREFERENCES_PAGES = 'envisage.ui.workbench.preferences_pages' ACTION_SETS = 'envisage.ui.workbench.action_sets' BANNER = 'envisage.plugins.ipython_shell.banner' # The plugins name. name = 'Mayavi UI plugin' # Our ID. id = 'mayavi_ui' ###### Contributions to extension points made by this plugin ###### # Views. views = List(contributes_to=VIEWS) # Perspectives. perspectives = List(contributes_to=PERSPECTIVES) # Preferences pages. preferences_pages = List(contributes_to=PREFERENCES_PAGES) # Our action sets. action_sets = List(contributes_to=ACTION_SETS) # IPython banner banner = List(contributes_to=BANNER) def _views_default(self): """ Trait initializer. """ return [self._engine_view_factory, self._current_selection_view_factory] def _perspectives_default(self): """ Trait initializer. """ return [MayaviPerspective] def _preferences_pages_default(self): """ Trait initializer. """ from mayavi.preferences.mayavi_preferences_page import ( MayaviRootPreferencesPage, MayaviMlabPreferencesPage) return [MayaviRootPreferencesPage, MayaviMlabPreferencesPage] def _action_sets_default(self): """ Trait initializer. """ from mayavi.plugins.mayavi_ui_action_set import ( MayaviUIActionSet ) return [MayaviUIActionSet] def _banner_default(self): """Trait initializer """ return ["""Welcome to Mayavi, this is the interactive IPython shell. If this is your first time using Mayavi, take a quick look at the tutorial examples section of the user guide, accessible via the help menu. To use Mayavi, you need to load your data in "data sources" and apply "visualization modules" to it. """] ###################################################################### # Private methods. def _engine_view_factory(self, window, **traits): """ Factory method for engine views. """ from pyface.workbench.traits_ui_view import \ TraitsUIView from mayavi.core.ui.engine_view import \ EngineView engine_view = EngineView(engine=self._get_engine(window)) tui_engine_view = TraitsUIView(obj=engine_view, id=ENGINE_VIEW, name='Mayavi', window=window, position='left', **traits ) return tui_engine_view def _current_selection_view_factory(self, window, **traits): """ Factory method for the current selection of the engine. """ from pyface.workbench.traits_ui_view import \ TraitsUIView engine = self._get_engine(window) tui_engine_view = TraitsUIView(obj=engine, view='current_selection_view', id=CURRENT_SELECTION_VIEW, name='Mayavi object editor', window=window, position='bottom', relative_to=ENGINE_VIEW, **traits ) return tui_engine_view def _get_engine(self, window): """Return the Mayavi engine of the particular window.""" from mayavi.core.engine import Engine return window.get_service(Engine) def _get_script(self, window): """Return the `mayavi.plugins.script.Script` instance of the window.""" from mayavi.plugins.script import Script return window.get_service(Script) ###################################################################### # Trait handlers. @on_trait_change('application.gui:started') def _on_application_gui_started(self, obj, trait_name, old, new): """This is called when the application's GUI is started. The method binds the `Script` and `Engine` instance on the interpreter. """ # This is called when the application trait is set but we don't # want to do anything at that point. if trait_name != 'started' or not new: return # Get the script service. app = self.application window = app.workbench.active_window script = self._get_script(window) # Get a hold of the Python shell view. id = SHELL_VIEW py = window.get_view_by_id(id) if py is None: logger.warn('*'*80) logger.warn("Can't find the Python shell view to bind variables") return # Bind the script and engine instances to names on the # interpreter. try: py.bind('mayavi', script) py.bind('engine', script.engine) # The following will fail under Qt, as it needs the Pyface # Tree that has not been ported from Wx yet. from apptools.naming.ui.api import explore py.bind('explore', explore) except AttributeError, msg: # This can happen when the shell is not visible. # FIXME: fix this when the shell plugin is improved. logger.warn(msg) logger.warn("Can't find the Python shell to bind variables") mayavi-4.1.0/mayavi/plugins/mayavi_plugin.py0000644000175100001440000000323411674464502022163 0ustar ischnellusers00000000000000"""The Mayavi plugin. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. from traits.api import List from envisage.api import Plugin, ServiceOffer # This module's package. PKG = '.'.join(__name__.split('.')[:-1]) # The mayavi package ID. ID = 'mayavi' ############################################################################### # `MayaviPlugin` class. ############################################################################### class MayaviPlugin(Plugin): # Extension point Ids. SERVICE_OFFERS = 'envisage.ui.workbench.service_offers' PREFERENCES = 'envisage.preferences' # The plugins name. name = 'Mayavi plugin' # Our ID. id = ID ###### Contributions to extension points made by this plugin ###### # Services we contribute. service_offers = List(contributes_to=SERVICE_OFFERS) # Preferences. preferences = List(contributes_to=PREFERENCES) def _preferences_default(self): """ Trait initializer. """ return ['pkgfile://%s/preferences/preferences.ini' % ID] ###################################################################### # Private methods. def _service_offers_default(self): """ Trait initializer. """ engine_service_offer = ServiceOffer( protocol = 'mayavi.core.engine.Engine', factory = PKG + '.envisage_engine.EnvisageEngine' ) script_service_offer = ServiceOffer( protocol = 'mayavi.plugins.script.Script', factory = PKG + '.script.Script' ) return [engine_service_offer, script_service_offer] mayavi-4.1.0/mayavi/plugins/script.py0000644000175100001440000000740311674464502020625 0ustar ischnellusers00000000000000"""This represents the scripting API for MayaVi. The Script class provides a scriptable view of the MayaVi Engine. It is safe to instantiate as many Script instances as desired. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Enthought imports from traits.api import HasTraits, Instance # Local imports from mayavi.core.engine import Engine from mayavi.core.common import exception ############################################################################## # Utility functions. ############################################################################## def get_imayavi_engine(window): """Returns the MayaVi Engine given the Envisage worbench window. """ return window.get_service(Engine) def get_imayavi(window): """Given the Envisage workbench window, returns the mayavi.script.Script instance (registered as `mayavi.services.IMAYAVI`). """ return window.get_service(Script) ############################################################################## # `Script` class. ############################################################################## class Script(HasTraits): """This class basically presents a scriptable 'view' of the MayaVi Engine. It is registered as the IMayaVi service (via an ApplicationObject) because this is the interface users should be using when they script. """ # The workbench window we are associated with. window = Instance('pyface.workbench.api.WorkbenchWindow') # The MayaVi engine that we are managing. engine = Instance(Engine) ###################################################################### # `Script` interface ###################################################################### def add_source(self, src, scene=None): """Adds a given source to the MayaVi pipeline. """ try: self.engine.add_source(src, scene=scene) except: exception() def add_module(self, mod, obj=None): """Adds a given module to the MayaVi pipeline. Adds it to the selected object, or to an object passed thought the kwarg `obj`. """ try: self.engine.add_module(mod, obj=obj) except: exception() def add_filter(self, fil, obj=None): """Adds a given filter to the MayaVi pipeline. Adds it to the selected object, or to an object passed thought the kwarg `obj`. """ try: self.engine.add_filter(fil, obj=obj) except: exception() def new_scene(self): """Creates a new VTK scene window. """ return self.engine.new_scene() def load_visualization(self, fname): """Given a file/file name this loads the visualization. """ try: self.engine.load_visualization(fname) except: exception() def save_visualization(self, fname): """Given a file or a file name, this saves the current visualization to the file. """ try: self.engine.save_visualization(fname) except: exception() def get_active_window(self): """Get the currently active window.""" return self.window def open(self, filename): """Open a data file if possible. """ try: return self.engine.open(filename) except: exception() ###################################################################### # Non-public interface ###################################################################### def _window_changed(self, window): """Traits handler for changes to application. """ self.engine = get_imayavi_engine(window) mayavi-4.1.0/mayavi/plugins/__init__.py0000644000175100001440000000013211674464502021050 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/mayavi/plugins/mayavi_workbench_application.py0000644000175100001440000001036511674464502025235 0ustar ischnellusers00000000000000"""Mayavi specific workbench application. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. from os.path import dirname import logging # Enthought library imports. from traits.api import Bool from envisage.ui.workbench.api import WorkbenchApplication from pyface.api import AboutDialog, ImageResource, SplashScreen # Local imports. import mayavi.api from mayavi.preferences.api import preference_manager IMG_DIR = dirname(mayavi.api.__file__) logger = logging.getLogger(__name__) class MayaviWorkbenchApplication(WorkbenchApplication): """ The mayavi application. """ #### MayaviWorkbenchApplication interface ################################# # Turn this off if you don't want the workbench to start a GUI # event loop. start_gui_event_loop = Bool(True, desc='start a GUI event loop') #### 'IApplication' interface ############################################# # The application's globally unique Id. id = 'mayavi_e3' #### 'WorkbenchApplication' interface ##################################### # Branding information. # # The icon used on window title bars etc. icon = ImageResource('m2.ico', search_path=[IMG_DIR]) # The name of the application (also used on window title bars etc). name = 'Mayavi2 - The 3D data visualizer' ########################################################################### # 'WorkbenchApplication' interface. ########################################################################### def run(self): """ Run the application. This does the following: 1) Starts the application 2) Creates and opens a workbench window 3) Starts the GUI event loop (only if start_gui_event_loop is True) 4) When the event loop terminates, stops the application This particular method is overridden from the parent class to allow the user to not run the gui event loop as would be necessary when the loop is started elsewhere or when run fron IPython. """ logger.debug('---------- workbench application ----------') # Make sure the GUI has been created (so that, if required, the splash # screen is shown). gui = self.gui # Start the application. if self.start(): # Create and open the first workbench window. window = self.workbench.create_window( position=self.window_position, size=self.window_size ) window.open() # We stop the application when the workbench has exited. self.workbench.on_trait_change(self._on_workbench_exited, 'exited') # Start the GUI event loop if needed. if self.start_gui_event_loop: # THIS CALL DOES NOT RETURN UNTIL THE GUI IS CLOSED. gui.start_event_loop() return ###################################################################### # Non-public interface. ###################################################################### def _about_dialog_default(self): """ Trait initializer. """ from mayavi import api from vtk import vtkVersion vtk_version = vtkVersion().GetVTKVersion() about_dialog = AboutDialog( parent = self.workbench.active_window.control, image = ImageResource('m2_about.jpg', search_path=[IMG_DIR]), additions = ['Authors: Prabhu Ramachandran', 'and Gael Varoquaux', '', 'Mayavi version %s \t - \t VTK version %s' % (api.__version__, vtk_version)], ) return about_dialog def _splash_screen_default(self): """ Trait initializer. """ if preference_manager.root.show_splash_screen: splash_screen = SplashScreen( image = ImageResource('m2_about.jpg', search_path=[IMG_DIR]), show_log_messages = True, ) else: splash_screen = None return splash_screen mayavi-4.1.0/mayavi/plugins/mayavi_ui_action_set.py0000644000175100001440000001216311674464502023513 0ustar ischnellusers00000000000000"""Mayavi action set for menus and actions etc. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Enthought library imports. from envisage.ui.action.api import Action, ActionSet, Group, Menu from mayavi.core.registry import registry ######################################## # Groups file_group = Group( id='MayaviFileGroup', path='MenuBar/File', before='ExitGroup' ) visualize_group = Group( id='VisualizeGroup', path='MenuBar/VisualizeMenu', ) modules_group = Group( id='ModulesGroup', path='MenuBar/VisualizeMenu/ModulesMenu', ) filters_group = Group( id='FiltersGroup', path='MenuBar/VisualizeMenu/FiltersMenu', ) ######################################## # Menus open_menu = Menu( id = "LoadDataMenu", name = "&Load data", path = 'MenuBar/File', group='MayaviFileGroup' ) visualize_menu = Menu( id = "VisualizeMenu", name = "Visuali&ze", path = "MenuBar", before = "View" ) modules_menu = Menu( id = "ModulesMenu", name = "&Modules", path="MenuBar/VisualizeMenu", ) filters_menu = Menu( id = "FiltersMenu", name = "&Filters", path="MenuBar/VisualizeMenu", after="ModulesMenu", ) ######################################## # File menu items. ID = 'mayavi' #################### # Source actions. open_file = Action( id = "OpenFile", class_name = ID + ".action.sources.OpenFile", name = "&Open file ...", path = "MenuBar/File/LoadDataMenu" ) # Automatic source generation for non-open file related sources. SOURCE_ACTIONS = [open_file] for src in registry.sources: if len(src.extensions) == 0: action = Action(id=src.id, class_name=ID + ".action.sources." + src.id, name= src.menu_name, path="MenuBar/File/LoadDataMenu" ) SOURCE_ACTIONS.append(action) #################### # Save/load actions. save_viz = Action( id = "SaveVisualization", class_name = ID + ".action.save_load.SaveVisualization", name = "&Save Visualization", group = "MayaviFileGroup", path="MenuBar/File", ) load_viz = Action( id = "LoadVisualization", class_name = ID + ".action.save_load.LoadVisualization", name = "&Load Visualization", group = "MayaviFileGroup", path="MenuBar/File", ) run_script = Action( id = "RunScript", class_name = ID + ".action.save_load.RunScript", name = "&Run Python Script", group = "MayaviFileGroup", path="MenuBar/File", ) ######################################## # Visualize menu items. add_mm = Action( id = "AddModuleManager", class_name = ID + ".action.modules.AddModuleManager", name = "&Add ModuleManager", path="MenuBar/VisualizeMenu", after="FiltersMenu" ) ######################################## # Modules. MODULE_ACTIONS = [] for module in registry.modules: action = Action(id=module.id, class_name=ID + ".action.modules." + module.id, name= module.menu_name, group = "ModulesGroup", path="MenuBar/VisualizeMenu/ModulesMenu" ) MODULE_ACTIONS.append(action) ######################################## # Filter items. ######################################## FILTER_ACTIONS = [] for filter in registry.filters: action = Action(id=filter.id, class_name=ID + ".action.filters." + filter.id, name= filter.menu_name, group = "FiltersGroup", path="MenuBar/VisualizeMenu/FiltersMenu" ) FILTER_ACTIONS.append(action) ######################################## # Help menu items. help_index = Action( id = "HelpIndex", class_name = ID + ".action.help.HelpIndex", name = "&User Guide", path="MenuBar/Help" ) tvtk_class_browser = Action( id = "TVTKClassBrowser", class_name = ID + ".action.help.TVTKClassBrowser", name = "&VTK Class Browser", path = "MenuBar/Help", ) ################################################################################ # `MayaviUIActionSet` class. ################################################################################ class MayaviUIActionSet(ActionSet): """ The default action set for the mayavi UI plugin. """ groups = [file_group, visualize_group, modules_group, filters_group, ] menus = [open_menu, visualize_menu, modules_menu, filters_menu ] actions = SOURCE_ACTIONS + \ [save_viz, # Save load actions. load_viz, run_script, # Add module manager. add_mm, ] + \ MODULE_ACTIONS + \ FILTER_ACTIONS + \ [help_index, tvtk_class_browser, ] mayavi-4.1.0/mayavi/__init__.py0000644000175100001440000000137711674464502017403 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran, Gael Varoquaux # Copyright (c) 2004-2011, Enthought, Inc. # License: BSD Style. """ A tool for easy and interactive visualization of data. Part of the Mayavi project of the Enthought Tool Suite. """ __version__ = '4.1.0' __requires__ = [ 'apptools', 'traits', 'traitsui', ] __extras_require__ = { 'app': [ 'envisage', ], } # Try forcing the use of wx 2.8 before any other import. import sys if not 'wx' in sys.modules: try: # Try forcing the use of wx 2.8 from traits.etsconfig.api import ETSConfig if ETSConfig.toolkit in ('wx', ''): import wxversion wxversion.ensureMinimal('2.8') except ImportError: """ wxversion not installed """ mayavi-4.1.0/mayavi/mlab.py0000644000175100001440000000565511674464502016562 0ustar ischnellusers00000000000000""" mlab: a simple scripting interface to Mayavi2 for 3D plotting. Can be used inside Mayavi2 itself, in "ipython -wthread", or in any application with the WxWidget mainloop running. """ # Author: Prabhu Ramachandran # Gael Varoquaux # Copyright (c) 2007-2010, Enthought, Inc. # License: BSD Style. # Try forcing the use of wx 2.8 before any other import. import sys if not 'wx' in sys.modules: try: from traits.etsconfig.api import ETSConfig if ETSConfig.toolkit in ('wx', ''): import wxversion wxversion.ensureMinimal('2.8') except ImportError: """ wxversion not installed """ # Mayavi imports from mayavi.tools.camera import view, roll, yaw, pitch, move from mayavi.tools.figure import figure, clf, gcf, savefig, \ draw, sync_camera, close, screenshot from mayavi.tools.engine_manager import get_engine, show_pipeline, \ options, set_engine from mayavi.tools.show import show from mayavi.tools.animator import animate def show_engine(): """ This function is deprecated, please use show_pipeline. """ import warnings warnings.warn('The show_engine function is deprecated, please use' 'show_pipeline', stacklevel=2) return show_pipeline() from tools.helper_functions import contour3d, test_contour3d, \ quiver3d, test_quiver3d, test_quiver3d_2d_data, \ points3d, test_points3d, test_molecule, \ flow, test_flow, \ imshow, test_imshow, \ surf, test_surf, mesh, test_mesh, test_simple_surf, \ test_mesh_sphere, test_fancy_mesh,\ contour_surf, test_contour_surf, \ plot3d, test_plot3d, \ test_plot3d_anim, test_points3d_anim, test_contour3d_anim,\ test_simple_surf_anim, test_flow_anim, test_mesh_sphere_anim, \ triangular_mesh, test_triangular_mesh, barchart, \ test_barchart from tools.decorations import colorbar, scalarbar, vectorbar, \ outline, axes, xlabel, ylabel, zlabel, text, title, \ orientation_axes, text3d import tools.pipeline as pipeline from tools.tools import start_recording, stop_recording if __name__ == "__main__": import numpy n_mer, n_long = 6, 11 pi = numpy.pi dphi = pi/1000.0 phi = numpy.arange(0.0, 2*pi + 0.5*dphi, dphi, 'd') mu = phi*n_mer x = numpy.cos(mu)*(1+numpy.cos(n_long*mu/n_mer)*0.5) y = numpy.sin(mu)*(1+numpy.cos(n_long*mu/n_mer)*0.5) z = numpy.sin(n_long*mu/n_mer)*0.5 pl = plot3d(x, y, z, numpy.sin(mu), tube_radius=0.05, colormap='Spectral') colorbar(orientation='vertical') t = numpy.linspace(0, 4*numpy.pi, 100) cos = numpy.cos sin = numpy.sin x = sin(2*t) y = cos(t) z = sin(2*t) s = sin(t) pts = points3d(x, y, z, s, colormap="YlGnBu", scale_factor=0.1, extent=(-0.3,0.3, -0.3, 0.3, -0.2,0.2)) axes(xlabel='X', ylabel='Y', zlabel='Z') outline(pl) title('Mayavi rocks', height=0.85) mayavi-4.1.0/tvtk/0000755000175100001440000000000011674464665014776 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/class_tree.py0000644000175100001440000001751311674464502017471 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2004-2007, Enthought, Inc. # License: BSD Style. """This module generates the class hierarchy for any given Python modules. This can be used (among other things) to generate the traitified VTK classes in the correct order. """ import __builtin__ class TreeNode: """Represents a node in the class tree. Stores information on the sub and super classes of a particular class. It also stores the inheritance level of the class inside the inheritance tree (essentially how many levels of inheritance are there below this class). This inheritance level is computed when the `get_level` method is called. The `get_level` method works only when the parent information is complete. """ def __init__(self, klass): """Given a class, create a node in the tree. Parameters ---------- - klass : `class` The class which is represented as a node in the tree. """ self.klass = klass self.name = klass.__name__ self.children = [] self.parents = [] # Uninitialized level is set to None self.level = None def add_parent(self, parent): """Add a parent node.""" assert isinstance(parent, TreeNode) if parent not in self.parents: self.parents.append(parent) def add_child(self, child): """Add a child node. """ assert isinstance(child, TreeNode) if child not in self.children: self.children.append(child) def get_level(self): """Returns the inheritance level of the node (an int). If the level has not been set, the method computes it. Note however, that this computation will fail if the parent information is incorrect. """ if not self.level: if self.parents: self.level = max([x.get_level() for x in self.parents]) + 1 else: self.level = 0 return self.level def get_ancestors(self): """Returns a list of ancestor nodes from which this class has descended. """ def _get_ancestors(node, ancestors): ancestors.extend(node.parents) for p in node.parents: _get_ancestors(p, ancestors) ancestors = [] _get_ancestors(self, ancestors) return ancestors class ClassTree: """Contains and generates all the class tree information. On initialization of the instance, nothing is done. The classes are obtained using the list of modules (or a single module) that is used to initialize the instance. One must then call the `create` method to generate the tree structure. The instance of the class also can be treated as an iterator which iterates over the nodes of the tree. There are two ways in which the tree hierarchy is stored. A dictionary mapping class names to the tree node and a tree represented as a list of lists containing the nodes. The tree is organized based on a concept of an inheritance level. A class that has no parent classes (no base classes) is said to be at level zero. If a class inherits successively from 7 classes, it is at level 6. An example of inheritance for a vtkFoo class is given below: vtkFoo -> vtkBar -> vtkObject -> vtkObjectBase Here, vtkObjectBase has an inheritance level of 0 and vtkFoo a level of 3. One can traverse the tree by using the level as an index and find all the classes at a particular level. Here is some example usage of this class:: >>> import vtk >>> t = ClassTree(vtk) >>> t.create() >>> print t.get_node('vtkObject').name vtkObject >>> print t.get_node('vtkObject').parents[0].name vtkObjectBase >>> print len(t.tree[0]) 1 >>> t.tree[0][0].name vtkObjectBase """ def __init__(self, modules): """Initialize the instance with the given modules. Parameters ---------- - modules : `sequence` of modules or a module This is either a single module or a sequence of modules. The instance uses these list of modules to generate the class tree. """ self.nodes = {} self.tree = [[]] if not hasattr(modules, '__iter__'): self.modules = [modules] else: self.modules = modules def __iter__(self): return iter(self.nodes.values()) def _generate_hierarchy(self, klass): """Does the hard work of generating the class hierarchy.""" node = self.get_node(klass.__name__, create=1) for base in klass.__bases__: base_node = self.get_node_from_class(base, create=1) node.add_parent(base_node) base_node.add_child(node) self._generate_hierarchy(base) def get_class(self, name): """Given a class name in the given modules returns the class.""" klass = None for m in self.modules: if hasattr(m, name): return getattr(m, name) if hasattr(__builtin__, name): klass = getattr(__builtin__, name) if not klass: try: klass = self.nodes[name].klass except KeyError: raise KeyError, "Cannot find class of name %s"%name return klass def add_node(self, klass): """Create a node for the given class.""" name = klass.__name__ if not self.nodes.has_key(name): node = TreeNode(klass) self.nodes[name] = node return node def get_node(self, name, create=0): """Get a node of the given name. Parameters ---------- - name : `str` Name of the node to get. - create : `boolean` If True, a new node will be added if no node of the given name is available. Defaults to False. Returns ------- - `TreeNode` """ if self.nodes.has_key(name): return self.nodes[name] elif create: return self.add_node(self.get_class(name)) def get_node_from_class(self, cls, create=0): """Get a node of the given class. Parameters ---------- - cls : `class` Class of the node to get. - create : `boolean` If True, a new node will be added if no node of the given name is available. Defaults to False. Returns ------- - `TreeNode` """ name = cls.__name__ if self.nodes.has_key(name): return self.nodes[name] elif create: return self.add_node(cls) def create(self, class_names=None): """This method generates the class tree given an optional list of class names. Parameters ---------- - class_names - `list` of `str` An optional list of names of the classes to generate the tree for. Defaults to None where the class list is computed from the modules. """ if class_names is None: class_names = [] for m in self.modules: class_names.extend(dir(m)) # Generate the nodes. for name in class_names: klass = self.get_class(name) if klass and hasattr(klass, '__bases__'): self._generate_hierarchy(klass) # Compute the inheritance level and store the nodes in the tree. for node in self: d = node.get_level() while len(self.tree) <= d: self.tree.append([]) self.tree[d].append(node) # Sort the nodes alphabetically. def _comp(x, y): return cmp(x.name, y.name) for nodes in self.tree: nodes.sort(_comp) mayavi-4.1.0/tvtk/special_gen.py0000644000175100001440000004425611674464502017622 0ustar ischnellusers00000000000000"""This module defines classes used in tvtk code generation, `SpecialGenerator` defines methods that write out special code for some of the VTK classes. `HelperGenerator` helps generate the `tvtk_helper.py` class. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2007, Enthought, Inc. # License: BSD Style. import vtk # These are relative imports for good reason. import indenter from common import get_tvtk_name ###################################################################### # `SpecialGenerator` class. ###################################################################### class SpecialGenerator: """Generates special code for some of the TVTK classes. For example vtkMatrix4x4 objects can be pickled nicely if the elements of the matrix are stored and restored. So we define a `_write_Matrix4x4` method that generates the appropriate code. """ def __init__(self, indent): """`indent` is a reference to the `Indenter` instance of the WrapperGenerator. """ self.indent = indent ################################################################# # `SpecialGenerator` interface. ################################################################# def generate_code(self, node, out): """Write the code given the node in the class tree, `node`, and output file-like object, `out`. """ self._write_special(node.name, out) ################################################################# # Non-public interface. ################################################################# def _write_special(self, name, out): """Given the name of the class, call appropriate method, if available. """ tname = get_tvtk_name(name) writer = '_write_%s'%tname if hasattr(self, writer): getattr(self, writer)(out) def _write_InteractorEventRecorder(self, out): # This class is a pain because it must always take highest # priority, the default value is therefore set to a huge # number so that it catches all events first. code = ''' priority = traits.Trait(1.0, traits.Float, traits.Range(0.0, 1.0)) def _priority_changed(self, old_val, new_val): self._do_change(self._vtk_obj.SetPriority, self.priority) priority.help = \ """ Set/Get the priority at which events are processed. This is used when multiple interactor observers are used simultaneously. The default value is 0.0 (lowest priority.) Note that when multiple interactor observer have the same priority, then the last observer added will process the event first. (Note: once the set_interactor() method has been called, changing the priority does not effect event processing. You will have to set_interactor(_null), change priority, and then set_interactor(iren) to have the priority take effect.) """ ''' out.write(self.indent.format(code)) def _write_Matrix4x4(self, out): code = """ def __getstate__(self): d = tvtk_base.TVTKBase.__getstate__(self) obj = self._vtk_obj e = [obj.GetElement(i, j) for i in range(4) for j in range(4)] d['elements'] = e return d def __setstate__(self, dict): e = dict.pop('elements') tvtk_base.TVTKBase.__setstate__(self, dict) self._in_set = 1 obj = self._vtk_obj [obj.SetElement(i, j, e[4*i+j]) for i in range(4) for j in range(4)] self._in_set = 0 self.update_traits() def from_array(self, arr): '''Set the value of the matrix using the passed Numeric array or Python list. ''' obj = self._vtk_obj [obj.SetElement(i, j, arr[i,j]) for i in range(4) for j in range(4)] def to_array(self): '''Return the object as a numpy array.''' obj = self._vtk_obj e = [obj.GetElement(i, j) for i in range(4) for j in range(4)] arr = array_handler.numpy.array(e, dtype=float) arr.shape = (4,4) return arr """ out.write(self.indent.format(code)) def _write_Property(self, out): # Color is made from the other specified colors. code = """ def __getstate__(self): d = tvtk_base.TVTKBase.__getstate__(self) del d['color'] return d def __setstate__(self, dict): tvtk_base.TVTKBase.__setstate__(self, dict) self.update_traits() """ out.write(self.indent.format(code)) _write_Light = _write_Property def _write_Collection(self, out): code = """ def __len__(self): return self._vtk_obj.GetNumberOfItems() def __iter__(self): self._vtk_obj.InitTraversal() return self def next(self): try: val = self._vtk_obj.GetNextItem() except AttributeError: val = self._vtk_obj.GetNextProp() if val is None: raise StopIteration return wrap_vtk(val) def __getitem__(self, key): obj = self._vtk_obj if type(key) != type(1): raise TypeError, "Only integers are valid keys." ni = obj.GetNumberOfItems() if key < 0: key = ni + key ret = obj.GetItemAsObject(key) if ret is None: raise IndexError, "Index out of range." return wrap_vtk(ret) def __setitem__(self, key, val): obj = self._vtk_obj if type(key) != type(1): raise TypeError, "Only integers are valid key." ni = obj.GetNumberOfItems() if key < 0: key = ni + key if key < 0 or key >= ni: raise IndexError, "Index out of range." obj.ReplaceItem(key, deref_vtk(val)) def __delitem__(self, key): obj = self._vtk_obj if type(key) != type(1): raise TypeError, "Only integers are valid keys." ni = obj.GetNumberOfItems() if key < 0: key = ni + key if key < 0 or key >= ni: raise IndexError, "Index out of range." obj.RemoveItem(key) def __repr__(self): return repr([repr(x) for x in self]) def append(self, val): self._vtk_obj.AddItem(deref_vtk(val)) def extend(self, arr): obj = self._vtk_obj for i in arr: obj.AddItem(deref_vtk(i)) """ out.write(self.indent.format(code)) def _write_DataArray(self, out): code = """ def __len__(self): return self._vtk_obj.GetNumberOfTuples() def __iter__(self): obj = self._vtk_obj n = obj.GetNumberOfTuples() nc = obj.GetNumberOfComponents() if nc in [1,2,3,4,9]: meth = getattr(obj, 'GetTuple%d'%nc) for i in xrange(n): yield meth(i) else: for i in xrange(n): yield tuple([obj.GetComponent(i, x) for x in range(nc)]) def _check_key(self, key, n): if type(key) not in [int, long]: raise TypeError, "Only integers are valid keys." if key < 0: key = n + key if key < 0 or key >= n: raise IndexError, "Index out of range." return key def __getitem__(self, key): obj = self._vtk_obj n = obj.GetNumberOfTuples() key = self._check_key(key, n) nc = obj.GetNumberOfComponents() if nc in [1,2,3,4,9]: return getattr(obj, 'GetTuple%d'%nc)(key) else: return tuple([obj.GetComponent(key, x) for x in range(nc)]) def __setitem__(self, key, val): obj = self._vtk_obj n = obj.GetNumberOfTuples() key = self._check_key(key, n) nc = obj.GetNumberOfComponents() if nc == 1: obj.SetValue(key, val) elif nc in [2,3,4,9]: getattr(obj, 'SetTuple%d'%nc)(key, *val) else: assert len(val) == nc, \ 'length of %s != %s.'%(val, nc) for x in range(nc): obj.SetComponent(key, x, val[x]) def __repr__(self): obj = self._vtk_obj n = obj.GetNumberOfTuples() if n <= 10: return repr([x for x in self]) else: first, last = self[0], self[-1] return '[%s, ..., %s], length = %s'%(first, last, n) def append(self, val): obj = self._vtk_obj nc = obj.GetNumberOfComponents() if nc == 1: obj.InsertNextTuple1(val) elif nc in [2,3,4,9]: meth = getattr(obj, 'InsertNextTuple%d'%nc) meth(*val) else: n = obj.GetNumberOfTuples() for x in range(nc): obj.InsertComponent(n, x, val[x]) self.update_traits() def extend(self, arr): obj = self._vtk_obj nc = obj.GetNumberOfComponents() if nc == 1: for i in arr: obj.InsertNextTuple1(i) elif nc in [2,3,4,9]: meth = getattr(obj, 'InsertNextTuple%d'%nc) for i in arr: meth(*i) else: n = obj.GetNumberOfTuples() for i in range(len(arr)): for x in range(nc): obj.InsertComponent(n+i, x, arr[i][x]) self.update_traits() def from_array(self, arr): '''Set the value of the data array using the passed Numeric array or Python list. This is implemented efficiently. ''' array_handler.array2vtk(arr, self._vtk_obj) self.update_traits() def to_array(self): '''Return the object as a Numeric array.''' return array_handler.vtk2array(self._vtk_obj) """ out.write(self.indent.format(code)) def _write_Points(self, out): code = """ def __len__(self): return self._vtk_obj.GetNumberOfPoints() def __iter__(self): obj = self._vtk_obj n = obj.GetNumberOfPoints() for i in xrange(n): yield obj.GetPoint(i) def _check_key(self, key, n): if type(key) != type(1): raise TypeError, "Only integers are valid keys." if key < 0: key = n + key if key < 0 or key >= n: raise IndexError, "Index out of range." return key def __getitem__(self, key): obj = self._vtk_obj n = obj.GetNumberOfPoints() key = self._check_key(key, n) return obj.GetPoint(key) def __setitem__(self, key, val): obj = self._vtk_obj n = obj.GetNumberOfPoints() key = self._check_key(key, n) obj.SetPoint(key, val) def __repr__(self): obj = self._vtk_obj n = obj.GetNumberOfPoints() if n <= 10: return repr([x for x in self]) else: meth = obj.GetPoint return '[%s, ..., %s], length = %s'%(meth(0), meth(n-1), n) def append(self, val): self._vtk_obj.InsertNextPoint(val) self.update_traits() def extend(self, arr): obj = self._vtk_obj for i in arr: obj.InsertNextPoint(i) self.update_traits() def from_array(self, arr): '''Set the value of the data array using the passed Numeric array or Python list. This is implemented efficiently. ''' array_handler.array2vtkPoints(arr, self._vtk_obj) self.update_traits() def to_array(self): '''Return the object as a Numeric array.''' return array_handler.vtk2array(self._vtk_obj.GetData()) """ out.write(self.indent.format(code)) def _write_IdList(self, out): code = """ def __len__(self): return self._vtk_obj.GetNumberOfIds() def __iter__(self): obj = self._vtk_obj n = obj.GetNumberOfIds() for i in xrange(n): yield obj.GetId(i) def _check_key(self, key, n): if type(key) != type(1): raise TypeError, "Only integers are valid keys." if key < 0: key = n + key if key < 0 or key >= n: raise IndexError, "Index out of range." return key def __getitem__(self, key): obj = self._vtk_obj n = obj.GetNumberOfIds() key = self._check_key(key, n) return obj.GetId(key) def __setitem__(self, key, val): obj = self._vtk_obj n = obj.GetNumberOfIds() key = self._check_key(key, n) obj.SetId(key, val) def __repr__(self): obj = self._vtk_obj n = obj.GetNumberOfIds() if n <= 10: return repr([x for x in self]) else: meth = obj.GetId return '[%s, ..., %s], length = %s'%(meth(0), meth(n-1), n) def append(self, val): self._vtk_obj.InsertNextId(val) self.update_traits() def extend(self, arr): obj = self._vtk_obj for i in arr: obj.InsertNextId(i) self.update_traits() def from_array(self, arr): '''Set the value of the data array using the passed Numeric array or Python list. This is implemented efficiently. ''' array_handler.array2vtkIdList(arr, self._vtk_obj) self.update_traits() """ out.write(self.indent.format(code)) def _write_CellArray(self, out): code = """ def from_array(self, arr): '''Set the value of the data array using the passed Numeric array or Python list. This is implemented efficiently. ''' array_handler.array2vtkCellArray(arr, self._vtk_obj) self.update_traits() def to_array(self): '''Return the object as a Numeric array.''' return array_handler.vtk2array(self._vtk_obj.GetData()) """ out.write(self.indent.format(code)) ###################################################################### # `HelperGenerator` class. ###################################################################### class HelperGenerator: """Writes out the tvtk_helper.py file that makes it easy to use tvtk objects efficiently. """ def __init__(self): self.indent = indenter.Indent() ################################################################# # `HelperGenerator` interface. ################################################################# def write_prelims(self, out): """ Write out the preliminary data.""" indent = self.indent v = vtk.vtkVersion() vtk_version = v.GetVTKVersion()[:3] vtk_src_version = v.GetVTKSourceVersion() code = """ import vtk from tvtk import tvtk_base from tvtk.common import get_tvtk_name, camel2enthought # Caches all the classes. _cache = {} def set_ancestors(klass): tmp = klass.__bases__ if not tmp: return # Assuming a single inheritance. tmp = tmp[0] name = tmp.__name__ while not _cache.has_key(name) and \ name not in ['TVTKBase', 'object']: _cache[name] = tmp tmp = tmp.__bases__[0] name = tmp.__name__ def get_module(fname): try: mod = __import__('tvtk.custom.%%s'%%fname, globals(), locals(), [fname]) except ImportError: # This is a local import since the tvtk modules are all # inside the tvtk_classes ZIP file and are local to the # current module: tvtk_helper.py mod = __import__('tvtk.tvtk_classes.%%s'%%fname, globals(), locals(), [fname]) return mod def get_class(name): if _cache.has_key(name): return _cache[name] else: fname = camel2enthought(name) mod = get_module(fname) klass = getattr(mod, name) _cache[name] = klass set_ancestors(klass) return klass def wrap_vtk(obj): if isinstance(obj, tvtk_base.TVTKBase): return obj elif isinstance(obj, vtk.vtkObjectBase): cached_obj = tvtk_base.get_tvtk_object_from_cache(obj) if cached_obj is not None: return cached_obj cname = get_tvtk_name(obj.__class__.__name__) tvtk_class = get_class(cname) return tvtk_class(obj) else: return obj class TVTK(object): to_tvtk = staticmethod(wrap_vtk) to_vtk = staticmethod(tvtk_base.deref_vtk) """%locals() out.write(indent.format(code)) indent.incr() def add_class(self, name, out): """Add a tvtk class with name, `name` as a property to the helper class output file-like object, `out`. """ code = """ %(name)s = property(lambda self: get_class('%(name)s')) """%locals() out.write(self.indent.format(code)) mayavi-4.1.0/tvtk/tools/0000755000175100001440000000000011674464502016124 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/tools/images/0000755000175100001440000000000011674464502017371 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/tools/images/scene.ico0000644000175100001440000030717611674464502021200 0ustar ischnellusers00000000000000hf  00%@@(B.D(V( ecݒ]͕qetVPNœHXQBSOٕ(hmޘyq}!XYxk`epʇqbg,#4oyם{ߗc]MbߙfLD:zvx֙5UNߗdxdU?8sgBhcGS}n~b~lAWQ@Ek`Vߗe*C@Q7?gPt6"1T~xsvrSNsmPGMfQqdkog^2pq?1:+8@jRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRYYYYYYYYYYYYYYYYYYYK=4!OZF Ea6YYI:]LPWN'M_.YY+ `\TGYY *^B531YY/" > #UHYY;)8,@CS2YY&[D?(V$QYY9%7-J0li7YX5㴴( A(3[IJkaczlry]MY|p`gbVf;:5TEBV>T㴴ʧtjR UO`H`m/76#$)",#&LB\]Pl<;E[Xp$1H㴴ʛʺ|}_WY."@VY qjjkjkkkmlnnpopuN㴴=pkljlmlmnnmnnopuM㴴My4u.t.s.s.s-s-s/u0u0t0v1v1w0y2~9_㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴( @ 㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴 * ## % .!4+08-N/#0)+K/2/ 9C)'/%9 5) 㴴H.T7W<O8G-N8YNONKM?#K@m^hW]GY@WEdS^IO3E Q4P1QRim_MZN;(㴴Q;(|h,{`(se&wg$z71~w!{p0|'}-zmWaHI0ZJVBR=+u`H=}.wg3nG0fd+{o"r_gQ㴴W5FzU_rNWgwenzmlͳmto_hozueicelqT=B]9e㴴ҭѽغk<4) VVj\Up3OU'*!7+4162LFOgSrg[x24:`XpYHk6OO?HR_r㴴ὖ˪԰ٷ|{[TS/':E?Xx<)-  cmjcf]tyljge]yePpBXX㴴‘̢̤ͥͤܧȢzwt}3(8CC 4'uXg~`H=W;9\qrXUmaltDQS!2-㴴ˆΞ͞ϞΞΝ¢lxiPbsgzkeG.Š淉smcW")+-0G07M!-0㴴|ĖƔŔȔǔȔޗpxxhzG;Wx͘Ȕʔɖ՝ВYjQ/,"㴴uŽ‹ȎϊquipIŽđĐÐđďőқɋ㴴kćɁFHkn͒ń㴴a}|}|~yʼn~v㴴Xvvvvttvvuxwvxxwyx|{z}|}}~}l㴴Qpppnonpnpnoqprrqsrtvsuvuwud㴴Imkiikkkkkklllkmmlnooopoqso]㴴ޅEjghhgghgggihhjiiiijjlkmlnjW㴴?c`bbߗdb`bޗbdߗcߗcߙefdfcfeeeefihfU㴴|:ݔ_^ݕaޕaޓaޓ_ܕ`ݕ`ݕ`ݕ`ݓ`ݕ_ەaݕbݔbݖaޖdޕaߗcߗcߖaޘbdeffS㴴Ys/t1u2u2s2t0t1q/t0r.r-r/q.s0s2s1s3u1t1v3v5w7y4{6z7y7o#㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴(0` $㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴㴴 C0>3B7K85,4+@4>2DFB9=9GFI;@8K?MKPIMSMaJKM=Q:H2O:F6XC`GNCP?KASYMPG>N<N:K>QBWBP;PDI8 C.B.C1M:W?OAI>H6MBK@FEQSF@ QSZSO?QKVU]m _aVRXGT@RATC]PeP`K[FZH^MVFVHWGQCMH `U eWXGYQWH I2P;YAS>U:X>T=[BT>XH`L^U]R ]]KII9UDUHcNiQ_KXBYAV=Q@WG[L[K\JZCP5P5R9T6\K VXdj_WYKdVSD [GhTkVgLeNhUiWp`pa%yp&~p&t"{rykwjscudwenZlVqWhO aPjYk[dNcMlWiSl\n\p[ tdr\%yl#nla\oal\mYlW vpltqqfgeutpwzhntfik^qmT|uZooly}guuarrui|}OhkbtxK`^QfhB?:>=20,%@?9>5.C6-bVOqkg~Z]`]`ehvwV[]PFHRMMdhdXbaCPL cU\scfoY]N:>kZ\oZ_dQOudaZPNjahw}g`c>21wyVJPniitwtzZXYZX^urtohomnoRYX;=9),!ED:[UNme]GC8WZZlvxPQObcgFBKniq[ZU~~x~L`Z{mctMPUGIO_^fZXbE5I./0"3$DGPUMd]Tmeiw'-&!' WXf{tqME\WWt êԸxudVkbMB<+ !  "#$"755<;<<:CDIOKGS?LU:8)#%$&G?Hb_pYZhYXkBY[193&%+,"*( ("724632350ZUcdZt\Qgg_wUNcOM\b\w_WtQLcBQTAORGWZ ǦȩɨɨɨɨʪҰʬpp|SKMA845,',#70-A@APgnGZ[=:9)%!%*",&!-+"XYcwwmh_Tkb\rj`ye^wge|e^y`Us]PjMS^0J> Ȥʦʦʦ˦˦˧̦Ш̲{\X\4+%*"?HDOW]KAH6"*%&"!)#-#71(TO^c[whZqh^uuqskkf\WmNQ]U]gTfj;KN ˠ̣̣͢ΣΣΣϢϣӣw~w_YaD97HHB92-)-XD0~_ƥ}ղ׵z}jcVJX]Sfmh|}wpdb{HIR]dnOX`.;2 ˜̞̞͞͞͞͞͞Ξϟ̝|kWho`t[AQoQxhլ̜ݪڨ٧٧ݩҠѯ}ai`YMKODDIe`n]Vh\[k>DD>D@ ǖȘȘȘɘɘɘʗʘɘ͙輔}rcw}[9?zۤסӟϜ͛ΛϛϜϜѝ֠آ͘ǤxhKNB/-, %(&,$+.+ ÐēēĒŒŒŒŒŒƒŒʓٯ{{q}vi]odO͕ȔȔɔɔɕʕʕʕʖ˖˖˗Θӝ֟Őɠtdw[>ZI3 Žō}xO2B.L2O7L; dG\KUKOHM8N8NARBPASQTUME KP ]_URF9MBPVWS\cid_TSUWGO7SAS?Q?TD\M^GdN^I^I^I^GX>U?VFR@T>UIOL aS fX^PWHb[^WUGP; YEV=YES<V:[CV@[BX@Q>UI^J gZ_W_R ]Z ]cD>H9]GVEZIePlUjQ`KZEZA[@T<R@N=aOYNYK_O\I]DM2P8O5W?S7`E YT_d)kzdc ^U]Ol^XKTEQ7W>ZAY9L/T7N7T@aLcP_OcUhU lY k\ k^iUcQ]JcOaJZF`JbMYAbEcH`GbN\J]G`MaMaJ`JdNeIcJ[JX?fMcF`J\@eJ `Y ^\UOZJhU`MbK\E]DbZ}W~UrZ`\`T~SPmvkn`ZkkG_lS@xJK{s?k_9hX,RC2VIIso;cY2UBIgVRk\^~rd`xrb`adqjV~TuuX{{XS~MyIwJ{nWZfTVVADlWVsch[HMZJNZKNZMKktrO`WHGDhbigbdhaajffqjpyz}IA>A=?z|D54L>>QTY?.21E1)2!)++ @$!D/-qbbxhjxrqIE@VMPRNTIIHUSRD47K25O;:H77|ni^VZ>24H:7uhk{~iUWG5;g[ZrghsageWU_QN}omMDDrmx_\]* z_\eQJJuux~|zpy\][[^b{vaaehopZej270=GB{NX^%+#'$[a^;3-qjPRP)0-$*)df_TQFK:9.% TKG_YQxojzppzC<5ihkp|~ZTVjis\ZcZTOeke|CTHnyykfrR[[SXahittrsZTaC,D;3<$/$"4"BJMXWlPE][Qf>?@!*$0)^an{NE[RPiYYzwePXH8>;8BG6E69<+60:=>RG^M?Pd_yUMhHF\,"+:3?H>SGOWcia`G?YNKdccȯ˱ԽtqeR@<.'"  #%(%!(#11*723?9BIBODGMC@LJXW.1.:>@5<83+)' %*#'!$;15[UfpnTZddcyIPXA]\9C?(- (%*,!)'& +'"3102-*350885jdyaXq_UnbXnh`xXPgNK[_Zpb^w^YuYNkJM]C;20(*$+R?,sUǣ|ŖʜԤ˜}}j]TGQZPaeYnkbwzowqieMHXOOY_bp\_o)6,@DC ̞̞͟͟͟͞Ο͟͟ΟΟΞӡͮp\kdO`vkmZmL',}ZyY}_GqSǠw˛۩ک֥֥եץݪפѰvZ`WKNKRYYcTV^b_gxqg\tf]tjiADK:A9XY_ ɛʛɛʛʚ˛˛˛˛̚̚˛̛͛}yl`HY}wpawU% ܦעؤ֣ПΝϞϞООООҠףڤΚɧ|uVF<-*&540=@>7=65<40;/6=9+0&ƗƗƖƖǖȖǖȕȕȕȖȖȖʗŔ|{v{q}xaHX_D՞˖˗˗˗˘̙̙͙͙͘ΚΚϚΚϚќסآȖœrw]BH;&2*)"#!,-)621ēēĒĒđĒđőđĒĒŒőŒɒ|~wp_q~{wp]65̖ȓȓȓȓɓȔɔɕɕʕʕʕʕʕʕ˖̖̖Ι՝՝Ǔ٬|lz]@aK2G9'ŽŽŽŽŽŽŽŽŽŽŽǏԩ|}r_JWo?3ďĐĐĐďĐđĐŐđőőƑŒƒƒǒǓȓȓȓȓɓ˕ϘқϙΘËҩx}yyr>wRɓŽŽŽÏŽÎÏďďĐŐŐŐƑőƑŒȓӦ}[DN\HŽŽŽŽϡu}x}}rr}ܫݧ{||{{{{{{{zz{{{|{{|{|||}||}}||}~~~~~zxxxxxwwxwwwwwxwwxxyxxxxyxyyyzyzz{||}||}}}}}~~~~utttttttssrsttsstttttutuuuuvvvvvwwwwxyyyxyzyz{{{{{}~}}rrqqqqqqppopqpqppqpppqrrqrrrsssstststuvvvuuvwwwwwxyyzzpoooonmmmnnnnnnmmonmnmooooopopqppqqqqrssrrrsssttttvvvvmlmllkjjkkkklkkkkkkllmmlmmmmmmnmmnooopqqoppppqqqqqssstlkjijiihijjiiiiiiiiiijjjjjkklkkkklllmmnmmnnmnnoooppqopiigghggghhgghghhggghghhhhiiiiijiiijjjkklkkkllmmmmmmonmggfffeefffeeedffefefffgffgghhghhgggghhhiiiiiiiijjjklmldecccbcccdddccbcdcbdedeeߘdeeefeeeefefffffffgggghihhijjjb````ߖ`ߖ`ߖ`ߖaߕaޕ`ߕaߖ`ߖ`ߕ_ޕ`ޕ`ޖaޖaޕaޕbޖaޖaޖaޕaޕcޖaޖcޖbޖcޖbߖcߖcߖcߗbߗcccccdcbcedefffghhiߔ^ߔ]ޓ^ޔ\ޔ]ޔ^ݔ^ޔ^ޔ^ݓ^ܓ^ܓ^ݔ]ݓ]ݓ]ݓ^ݔ^ݓ]ݓ^ܓ^ܔ]ܓ^ܓ]ܔ^ܓ^ܓ_۔_ܔ_ݔ_ܔ`ܔ_ݔ`ݔ`ݕaݔ`ݕ`ޕ`ޕ`ޖaߖaߖ`ߖ`ߖ`ߖaߗaabddcdfffܒ[ܑ[ۑZܑZݒ\ܒ\ے]ܒ^ܒ]ے\ۑ\ۑ[ۑ[ے[ۑ\ۑ]ۑ[ڑZۑ[ڑ[ڑ[ۑ[ڑZڑ[ۑ\ڒ\ڒ\ے\ے^ے]ڒ]ۓ]ۓ]ے_ے^ܓ^ܓ^ܓ^ܓ^ݓ_ݓ_ݔ]ݔ^ݔ^ޔ_ޕ_ޖ`ߖ`ߖ`ߗ`ߗabdeېZڏXڏWڏXې[ۑZڑ[ې[ۑ[ڐ[ِYُZِZِYِZُZُYِYِXُXُY؏YُX؏XِYِZِZِYِ[ِ[ِ[ؐ[ِ[ِ\ڐ[ڑ[ڑ[ڑ\ۑ[ۑ\ے\ے[ے]ے]ܓ]ܓ]ܔ]ݔ]ݔ^ݔ^ݔ^ޕ`ޖbߗbsDrCrCsCsEsEsEsFsFsFsErErDrDrErCrCrCrCqCqCpBpCqCqCqCrCrCrDrDrDrFsErFsFsEsDsEsEsFsFsFsGsHtGtGuFuGvIvHvIvJwKwK (                                !!! iii|iii    TLTL [X eXQ:G7LCaLX64%/"><JIGD@2C;BDOK`L`H]=_O]IB(J1aX io]MU; l^ nl lgH= ULULN?M9MDHCD6 ]P`MU;V?B:?@D994>?;2IBGEN;=/F@FGHL ]^[TI<3 5-KH\XLM52^T cRR>C>DC IATHXRPUJX MWSHYO ]a X_JXVmT^ `e Y]B=RDYN_PaLM3N8L7<+V<[AN>OEG2F5WHjIhIeIV>H;I@aZTAY@YCO@[\ah \^EOUS TQ PNQFI7I9TCU>W@VBO>SENBUGWGV?^HT<P5S=D1M@ ZRbQSFI= TKYZID K<K<P7J-GDFK;/ RL WN\D iQF<9:<07*64)!HAKIR9R>RGHJKS eccZRB7"6'MC^_LO*%HBTGVJMD30.*UGk^ddBI?ERIeXaaM\B`PjN^ \a[[KDM;R<^QYCL/O8G8H=oS hOP@UKV=K8TGmShQt^XME@PL kmK>G1V@TGV\^mgc^\]f]gKKM@K<I=_OiQYBL<SE\OVIYI^OS?ZKbM WA aQM9 QGbYcQG?4&A3=;=: D+D+K4F4>04-0+/*>5V>T?A57%5%@1=1@/@->1K9N?F:E>>CGHB<>7HL TX \]XRC=A;H9=2G? dXJBD:]LgNeL dOXIN=dSZKHEKO N^\q ^cRPMLUI^KbKWDWA\BYAaEZ?IBL?W?\G^XYQ f_n`p]nZbMSGXP\NUAQCG>IFZh ^gVL]`aqTWRCM?RETH`O[NRL_O`NTJ[K[JUMXJnZxh jW_INID>UAWCQ<H5D3<* E*E*M=NCG5:%2"A1-#72ZBS5H.B2M@K4@2C6F5B:0&:0@3F= RYB7;/;E2< 1< NTW_USG>@4;5NUYYPMUP`RQ>N?TLXPYNUU \S c]Xt%][}[yWXNETZaZcP\AUHXOYHN@K=S@RELB\NbTdUq^fQjP_GW@\K_SU@\GUFVGYRYL^KNDD@LC^ObMfY YTRFIBC<=,BEHPVKZHlg f[\J[ML:M>JA[RXRL3^G`NcZOK =!=!D;F>G/E*M7S>@*E4O:[DdE[EQRTFNLHOF>J9M7M7G7TGUKS@M7F4H5JC N^PXJFGDA>26 GTXV^X\XPMB>:4MFVOKS Wi`b V] Mj\z bg `^ e\XS U^ Ze`aPAD/N<OCKAN=Q9T<UHXGXJXU_T_IhTiVgPY=S>_I\JVO[C]EZDV?R>SAXHYH]ZTTF8G6H9K9J8HHH?V=hK`HTFRBR:]F\LhV l_``a]ZUN@WQSR B/B/E1D);!A)aIeHO4J:TD ]DkR fLXK[F[M `VNCJ8Z<fNL8P@PG\M`J bS^P[PbSVMPP c[UG [W_i^bZU ]\TQSAM8F8K@GIP[MFO:XGaZ rcmUfP[PUMHCSB\CR8R2U9ZFWCUAYDL?TCV=UF[HY=Q:dNaM_NbK_E`GcJaI`RdL^?V9^EU:G2G6N6R1]<gNh` c[QIPOYX fZm` h[h\ e\YJPC\Y]]cUXIL@\E]HUI F2F2I7Q;I1L/R?O3P0ZKdS`>]?\CR8UE[MS>[C[?T:I-J+L.:*94RM YMXB`Qnngg\OYK[N\M KSZsegLI3-=-Q>`FYEVCTE_T_JhLpWpYiOlReM^LXGY;[<[<W8\D_HW>O9S:H2D1L9gPjO]ISLXH_PcUi\c[T<]@dGV;G-S7_EXAR8fIiO]DS2fCePZS ^Tmj.v8u/n{mvgiollh]S ^V d]pe a[RJPEVN]MP= `?`?S=MBbUfP`WVFO0VB WED5C1L/Y6\<eCM1M7^E[F^KfT_Q`J eQ`RYIeM kTg] Z\bRcW \Z\WbZ ebTZJJB22+NIdR\DcMYEXQbK^C[UhagIgP p\jPdM`N^P\HZC^EW:S6Q>ZQZIL<THaVaXUJ[\\RO<Q?]JVHYCbQW>F)N1G1G4G/I,WDQAR4O;ZB_E RSNN MDU[)g{%ovmg[RMDP< VB cSt\t\YLR>VC\LhX\O [D[DP6U<S7R8 ZE hU`F]A\?L1R:Y:[? X@K9TH g[bHi[ fPaL[I cZ fY^F]IfLgXeQcN]JeUrf mZjXeTaNYJbOkWcKgLdHZIJ<WG]HeUbgYIiGdN^IgPbNT?]NZBdRXLR9[AX@]I`KYK]L]IVL`LdQh\_HfLiP[?bG^HWTI>P3bQbXT;P1T>Q<H-M8^?bGfe QUV`X` U[E@;5UM dY gVZGcRcMYE[FYCN9]FZFSD C0C0@(L4[?W;R8Y=T8\=^8U5U3W;U;S=G5P5W:R9aNcT[?X9VB[L[Nh\ p\iOgRpYqaiadKcIcJhSkUaJ_H]EaKjX^GdI jYjRdIjKb<J-R:a=Z8V7[=a?[8\B]T]QUE^I]M]I]N`LfNdGfJcD^AePdHiJfKcLgTeR_JgLiN w\iFnHqVj`iRgH`@oPdJbT hafY^UVMH<S@kTqXiPRB\J]FP:_EYBZAcFbI_C cZcZeYgXqajWiSlWpXiSfU`KZKg]j`i`f\0sl5xu%nh+yk8|zEGT]aiYWymhZMI5C:1/7*|wqts|r%w%x{owc vf)zm&uk(uc.o,n'ug&qy`mTlTdKr[({tj segU`FgKkN"nR!aI&fM(pW0m6x2o0|k5nF6z.m1y3x6uCA<8{FHQE?B;}8uC@AEC7~4y?5z3{5{3t4z6y4z,{p sfa^U\|w{|vRboYpewxqbmi\i`ZcXTXPOMFND@JE;VRIpply}zTTKK?1aVKo`Yx^Xrloj|a\~~~qots{}v|giqhalh\enfpjepnpvps{mu{ox}hss_tt[utZso\yv`{e^ vw{~muorxpsujrlknrxyruxlry~z~yrwshlsinyryvoojlTlauxnjn]WY][]W[Za`emhq~zz|qoo>@HhjuvnrR?AT::meffklOBIWGKB"%, 0V<2U89) 3/101$6G$'@%)`KPXDI}ptLCIripj]i}wvo}icmmhqonusqsllsh`flajg[dmbhk^eymsvmwqmo~idnc^gidkqkoigmppo cXYcXY=/+3A',PDB8*"( 4 ?,"7*"`PXJ/,* 5 .$ /0@.06":/*_Z_LFA5* ! 1!#HAG[\YE>;\PPuji=71vxTNI_XZ\SS:3, "+B;BLX[@F],'7 % ' C53COK &3!!7"<5!D<.$ #%4.!  7' 0% D70;5,KC=0"%(nsg'>&/7'DHD>BC!+$ 26(++$B"44 )T?)ypf[^ % #!}bM uxux,(NBEja^-""B+,6!$/xmusrnWOM;4/<2,M>>I:?\QTRQG'#9+&WKG[VN&% JDPUX^|uhtg^b¾ZMV(2*!*% ]ji57@ smoJHL4==JNNR31UB8,%%%-8A8)%!zy}fcd6@0~}w|SVVfhjgt}7B6(4"8A38I@/4$s-LJ0(.8/*   5A80% [A6JUN)"0 "  {?,:1qmf}ykdXGG:%3($ kje~y~xu{|yz{k^[XNLZPVNQJaV`w$*V_]UGX;K>0J2)3)t|W]Y_f{vz8&;@,D8"56-6>>)4-&!4$!1+=,Q[alpGNdK?ROAURCZc[lDBMKG@9?8#1('=-8OK^cpwwSDaD7MSLhXSobbSLoE8SD:~o_\SH$& caiivvLWd_gpFNP("2((>=BUVoDEM .,-;3:tqu(&bfbžsxeh}wvPc_)73'/&>VTdyghpyystg_n0(;1?3HH$-'37=49;024;6;?:E<@D6?BC3IJ8PC-C;)78==*,*%2 (C-+1&JMZ;?JPMWjbzSIfN?VSI^JBXTPgKS_ )$"NEVbjsmqdc}aay_b}bcTNlG1;7215'5-!- ?<@[NhF@P=3AWI[jeccVPmM@UXPiPSj2>B!0!)+!,;3AD7NA6GETRZcrdb{fgot]XxI=X>;PACVWUn`^]^RNkA9X ŮŮƮǮǮֺ|}f}rski:%)44$08%/7#,4!'+*+)'-(./$1;0)'(&(1 "&/%""/2"-1$,2(19-*2%&,$"<.<:8<@CH|yJFM7A?/B;;7A@8I9A?IY]CKMHCJbbu;DI3<=EpsJux@`dHOZ;/9JKORRaH?T>/=E:K<8B2*/0*,=57XIYSL^LBTRBXMIZ34:>;E8EI.-+;>D+B80:0WLb[QjUH]OAUI8K`Vjlj\XxOF]XXpFJ^%.')+081FBQK:THASME]>;J4H@dqzirjp]XxQIdOGfI>XXXoa`~il^a}EC_ ªªëëëƮ̲ɮ|xn{pXA1$<5'.8''(((,),()""" )&!.&  "&$33+46+)+ !"-,&(,!960D2=HAMCKP>AGLARNCRNKU)4*<>ARH_WMg;.Tc\tXOlNB]RF_NQc!-.'/%TJdZOlQE`QE_ZVnSKdLEWU]ifr~l|JWlNNfSMhLF`_cygjpuuSXs ĪĪĬĬĬīĩêūϸȱĬǮʰ̱Ū׹гw_[RB<5(&$$ &"($'("%  "'"& $&!'#,( ') & /(%6,/F/CGDOAFGTM^OR]69;QBWZOgM:P,%$RmyHbe.C5*'$'!+-(171.4)0;1/=10:/-1++.$++%($1&$8-0C=D=28L>O=@D32087>+-&-0)E:F_Sk\Pg`SnTG]RAWR?Uh`v]TnZQhVJgTJb/<3 +C=I`ZwXOi[SoSIcUNiRE^VNc199NJC``:NF9FA;GE08.&&//&5?4.2&03)191,+%,+$$")+!.1)0+':254236/2282"#=:7nf}f]r]Ql^Snf_yYNgXI]kaug^wYJbhc|JDZ6<>JHT[Qhheza^z_Ys[Rp[WqULhOBZWQhHal#I9.P;<>?;C.+',("+' 6;38=6.1'A@=rnnj`YraVpe\ubZsZRif]qwt_RncZq[SkULcec|YRiYVege}_[u^Zub_yc_VLgL>VUQfXSiCDL/N?*K=)11&9/AYRLdh_kwdv ƩƩǨǩƪǩƩǪǩȧǩȪɧǧǧǨƨƧƨũŨƨǨ˩{vyru|fhlDA?2)%('&%,#,$0&'/(#73.964DBI`akIDIMBPKUYSnuZkv_kvLai=ROOmr) !2# ,0#$!$#+,$#& ("'-)%6/02/+.+%*&.*!:9.&/%,30&- ɥɥʦʥ˥ʤʦʥʥ˥ʥ˥˥˥̥̥̦̦̥ͥͥͥΤΥΥͤФDz}||bMY^\a9/(5*%1(82(>A7>=6@?=_\d\ZaAC?L0!8('& ).%3*0'+ !  # E5"K=)O=*G4%lX?fU=[I4A1#..'NMVlbxgVpeUmcVjdSexk|i]rtprnnf}xqvmh_xwpstfgGHV3<9*>+/I;4F<6K<>REOif1C=(:/-C7&2-)B/]nv ̢̢̢̤̣̤ˣ̢̤̣̣ͣͣ͢Σͤͣ΢ϢΤϢΣϢϣϣϢϢңîufziXdv~}ylnyIED8*"5-!71$AF=EOJBGA@I:DA:5$1$,+( 0%/$)7(O8%kOa߸ʛʝ•ݫ٨بkj]DHD?O?MbO_n^riXp|n|m[pk_vkbwmdxtl~pcykax}}ge_TrNE[?IKADIABI:7=SU]io}DLR+B:%1(&6H@QR^?CH ΟΟ͢͢͡Ρ͢͢ΡϡΠΡΡΡ΢ϡϢϡϡϠϡϡϡϢϢϡϡТѢ~~dHWshj[krg}yy\JUPD>[Qh849!  ̟͟͟͟͟͟͟͠͠Π͠ΟΟΟΟΟϟϠΟΠΠΟΠΠΟϟΠ͟ѡš~xiyW8A^DQ{wzwiTe\?Krdw6 fItSE4"9$5"3=-M9&w[?fÑ٧ץեңңӢӣӤԣԣդԤգդ֤ޫܪɨ~\FB-63-784GIOd`pc[of`lw^Uboisummje]raSjfYukez~rtPPbBBM&+#@I<{ynauWQc$5)  ˝˝˞˝̞˞̝̞̞̞̞̞͟͟͟͝͞͞͞͞͞͞͞ΞΞΞ͟͟͞Ѡz}pYiZ:DT4BeUldKUwp{qfP^U4:.`齍ի~Útuǡv轎ԣڨգѢѡѠѡѡѠҠҡҢӢӢҢӡӢԢӣԢۨۨÓmjJ\S>?@;>DEDAGNQXDFL /#  ʜʜʜʜʜʜ˛ʜʛ˛ʛʛ˜˜˜˝˜̝̜˝̛̛̛̜̜̜̜̝͛̚˛vzk|cIYeQgAmZi|wzos`wP*1<џw֢ҟգץפ֥ӣОΝϝΟΟПНПРОПџѠѠџџѠҟҠѡѠҟդۧڥϞn~fHNC.01$*)%((!*( 680HFGOMMVY]LNU;@=6=68;59D=15.&/ /71340*5) )-B,'  əəɚɛɚɚɚəȚɚʚșʘəʙʙʙʚʙ˙˚˙˙˙˙ʙʚʚʚʙ͚ĖrzxxeObm]mwomUj>t@*ќ͚͙͙͙͚͙͚͚͚͛͜͜ΜΜΝΝϜϝϜϝНМОНѝОООММўҞԡڤ֢ƓqoU>?-(#/,/0!/3$7:.6;-5<01@/4?16@73A54>=4:3/5+"('$)  ǖǖƘǗǘǘǘǗǖǗǗȘȗɗɘɗȘɗɗɕɗʕɗɗɖɘȘɗɘɘȗ̚ḑ}uymm\i|w{tykRa4^֠ʖ̗̗̘̗̗̘̙̘̙̙͙͚͙͛ΛΚϚΚΚΛϛϛϛϛϚϚϛϚЛϜϜНМўԠآ֡đhkO:9+   ('.-6/&/&1&30(13$:B9HDLTKWIFM..$"0 ŕŕƖƔƖŖŖƕŖƕŕƕǔǕƕƕƔǔǔǔƖǕƔƔƕǖǕȔǔǕƖƕ̕ҭ}z|~|qjVf~}vruk~xdtR+3]%͗ʕʕ˗˖˖ʖ˖˗˖ʖ˗ʗ˗̗̘̗̘̗̗̙͙͘͘Θ͙Ι͙͘ΙΘΙΙΙΛΙΛΚϜҞסѝԩ|^}cEH4$+-",'( %'*)-)3,$00$+.!) ĒĒŔēĔĔÕēŒēœŒēĔŔĒŒŒŒŒĒĔēœœœƒƑœœƓƓƓ̔žww}yysbHY~u{yt{rdKSGњlҚȓȓɔɕɔɕɖɔɔʕʕɔɖʕ˕ʖʖ˕ʕ˖̖̕˖̖˗̗˘˖˘˗˖˖˗˗̗̖̖̖˗˘̘Θӛ՞՝pdkJrZ=@+#" +.'% ÏÏőĒÓÑÒđĐĐĐÑĒĐđĐđđđĐĐđđđőőđĐőđđőĐőȑ~w~~wiQdgRayszqztp`jR14](ȔƒƒƓǓǒƑǓǒǒȒȒǒǓȒȓɔȓɔȔȔɕɔɔɔȔȔȕȓɔɓɓʔʖʔ˕˖˕̖̖̖̗̕̕̕̕ϙԜԜҙҚܪ~ySjAYjR2. $ ŽŽ‘ÐÏАŽÏÏÏÎϏÏÏϏÐώŽŽĎÏÐÐÏÎƑ븈}|||quizkU`X5?vC3ŐŒđđĐđőđďőőŐƒƒƒƑƑƒœœŒŒŒŒƒƑƑƓǒǑǒǓȔɓɓɓɓɔɔɕȕɔɔȔȔɔȔɓɔȔʔɔΚҜљԝϞѩ˨x ŽƎܭ{{z|veQ]9 EjL˔ŽÎÎÏÏÏÏÏÏďВÏÐĐÏÐÏĐđŏŏŐŐőĒőőőőőőŒœƒƒǒǓƓǒǒȒǒɒȓȒȔɔɔɓȔǔǕǓɓϙЙ̑ Ê۰|wyyzl|umdLW.N }ՌŒŒÎŽÍŽŽÏÏÎÏÐÏÐÐÏÏÏĐďđđŐƏƐŐƑŐŐŐƑƑƑƒƒƒǑƒƒœŒőƒƓË Ë紆xh{{jx}panxl|S+/0oMɒŽŽÎÏÎÎďÏÏďďĐďďÏŐŐŐŏĐĐŐĐĐĐđŒ 䰁u}hS[`EL?u=&ŠŒŒŽÍŽÎÎĎÎÏώΎÏÏĐ ܩw~uhTYL-1km|ŒŒŽŽ ֣prdskxxvosMIު wawsqnmprx{pʝ~ ﴀ汅ͬ~dۤ{ ~~|~~}}}~}}}~~~~}~~~}~~}~|~ }}}|}}|||{{||}}{z||||{z|{{}{|}{||}}}}|}|||}}|||}}~~}|}|~}}}|~~~ zz|{}{{zzz{zzzzz{zzyxzxxyyz{zzz{{zyy||zz|{||{z|{{{{|{{|||}{|{|}}|~}}~~~}|~~~{ xxz{zzyyzyzyywyxyxxxyxvxyxxxxvxzzyvxyxzxzzzyyyyzyyyzyyyz{zz{{z{{z|{{{{||}}}~}}||~}}~}}~~~~z xxyzxxxwwwvxwvxxuvuxwwuvwvuuwuvwwuvvwwvvwxwwywwwvxxxxxxxxxxywyxyxyxyzzz{{zz{{z{{||||||||}{{|~}}|}~~~}}x uuwxutvtuutwuuututuvtusstssvvtuttsuuttuuuutuuuvttuvvvuvwvwxwuwvvwwvwxxwxyyyyzzzzyz{yyyz{{yz{{|{z{||||||}~~~}~w rrvvsrutssrtrssrusrrqtrqprrssstsrssrsstrrsstrstututstutttuutuuvuutvwvwvwvvwwxwxxxxywwxyyyxzyzzyzz{{zy{{{{||{||||t rrssrqtrrrrrqrtqqsrqqpqonqrqqrpqsqoqrrqqpppqpqrsqsrqrssrtssstussstsuutsttuttuvwvwxwuxxvvwwwyxwywwyxxxxyzyzz{y{z{r rrsrrqqqqppqqppppppoopooooqqopqoqponpqpppopppoqpqrrqqprqqrqrrrrssqrsssssssrstttuvvutvtttuuuvvvvvvvuwvwwxxxyyxyyyp ooqqqpppponpppoonnmnoomoomopoonnnnomoppponnqomnopoponppppppooqqrqqrqqrrqprrsrsrrttsssssqsstrtstvuuuuttuvwxwwwwwvm nnooppmooonnonmmmlllmmmmmllmmmmmnlmllnnmllmnmmnnnmnnnnonoppoooooppnoppoqqpqpqqrrssqqrrrrrrsrsrsssrussrtuvvtuvutvm kknonmmnmllkllkjjjkkkklkklllllllmmmlkkllllmmmmnmnmmmnnnmmmnnnmoonomnonpoppppoqqqqrppqpoqoqqppqrrrrqqsrrsstttstuuk jjlmllkllllkkkjjjijjkjkkkklklkjikljllkkkkkllllmklllllllmlmllmmlnllmmlmmnmnnnnpnoqponooppnnooppqqpppqqppsrrrtttssi jjllllklkjkjjkhhjihiijkkjjjijjjijkiijiijijkijjkjjjjjkklmjkkmllklllklklllllmmmmnnnonnmooonnnmonononpqopopqrsppqrrh iilkkkjiihijjiiighghijjhijhhihiiiiihiihhiiihjhijihjjijijjkkkkjjjlkjjllllklklllklnmlmnmmonnmmnnnoooponpponpronmopg ggjjjkigghghighghhgiihhghgghhhhhhhghhhhhhhihghiihihiijihijjjkijijjiiijjkkkjkkkklllllklllllllmnmonomnmmnommppmmnoe ffihgijggggggfgggghhfgigfffggggghhfffggfggggghhhhhhhhhhihgiihgiijiiihhhikjjhjjjjkklkjkkkjllkkllmkkllmmllnnnonnnne eegghfhgffggfgfeegfgefgeeegfecefeffefffeffgfggfhgffgggfghgihgggihghhhghgihghiihihiiiijjjijkiijijkkjkkkljmmmnmnllc ccgffgffefffffeedfffffddedeeddfddeeeeefeefeefffgffegeffefgihghhghhgggggfffghhhfghhhhhhhjhhhiijiiiikjijkkkklmmlklb ddeecfeedcdedbcccdeddeddecddcdcdceedeecccdfeeedefffefffffߙeffggffegfdfdefefggggffgggfgfghgghhghihhihjigiiijkkkkkja ccccdec`aabaaaaccaacabecdebbbaaaߘ`ߗacbߗcaߖcߗbߘbߘacddccߘdߘdߘedޘcߘdߘfߗeߗeߗeߘdeddfߘcߙdcdddefgdefefefedeeefffeffggfeggfihhgghhhhjiijjjj_ ``edca``_`a`ab_`b``ߖcߖbߖaߕbߖ`ޖbb`aaߖaaaߕaޖ_ߗaޗaߖ`ޖ`ޖaޖaޖbޖaޖbޖbߖcߖbߖbޗbޗcޗbߖaޖaޖbߖcߗcޖdޗbߗcߖcޗcޗcbߗdߖdޗaߗdecߘdߗceߗcߗcdbcedbcdbdbddbddbdgdeeffeffgggghhhgiiii] __dc``^ߖ_ߖ_ߕ``_`ߕ`ߕ`ߖ_ߕaߕ_ߕ_ߖ`ߕaޕaߕ`ޕ^ޕ^ޕaߕ`ߕ_ޕ`ޖ`ߕ^ߕ_ޔ_ޕ_ߕ`ޕ_ޕbݕ`ޕ`ݕ_ݕ`ߔbޔaޕ`ݕbߕaݕaݕaݕ^ޕaݕaޕaޕbޔcݕbݕ`ޖ`ݖbޖdޖ`ޖcޖaޖdߖaݖ`ޖcߕdߕcݕcޖaߖbߖaޗbޖbߗbߗ`bacdcߘcbcdaabbacdddceedffedfefhhhhiii^ ߔ]ߔ]`ߕ_ߕ_^^ߔ_ޔ^ߔ]ߕ]ޕ\ޕ\ޔ^ޔ_ޔ^ޔ]ޔ^ޔ^ޔ`ޓ^ݔ]ݔ_ޔ_ܔ^ܓ^ݓ_ݔ]ݔ^ݕ_ݔ^ݔ\ݔ_ݔ_ݓ^ܔ^ݔ_ݓ_ݔ^ݔ\ݔ]ܓ`ܓ_ݔ]ݔ]ޓ^ݔ]ܓ_ޔ^ݓ^ݔ]ܕ^ܔ_ݔaݔ_ܔ`ݔaܔ`ݕaݕ`ݔ`ݔ`ݕ`ޖ`ݕ_ܕaݔaޔ`ߔbޕcݕaߕ`ޕ`ݕ`ݖbޕ``ޖ`ߔbߖaߗaߖbߖ`ߖ_ߖac`_`ߖadacaabceededeeeefgffgf\ ޓ[ޓ[ޔ]ߓ[ޓ^ޔ\ݓ]ݓ^ݒ^ޓ\ޔ\ݔ\ݓ]ޓ^ޔ]ݔ]ݓ^ݓ_ݔ]ݔ]ݔ^ݔ^ܓ_ܒ^ܒ]ܒ]ܒ\ܓ]ܒ^ܓ]ܓ^ܒ]ܓ^ܓ^ۓ^ܓ^ܓ^ܓ\ܔ\ݓ^ܒ]ܓ]ܓ\ܓ[ܓ\ܓ]ܓ\ܒ^ܓ[ܒ]ܓ_ۓ]ے]ܔ^ܓ^۔_۔^ܔ^ܓ^ܓ^ݔ_ܓ^۔_ܔ_ܔ^ܓ_ݔ_ܔ`ݔ^ܕ_ܔaܔ`ܔ`ݕ`ݔ`ޔ`ޔ`ݔ_ޔ_ݕ`ݖ_ߕbޕaޕ_ߕ`ޕ`ޖ`ߕ`ޕ`ޖ_ޖaޖ`ߗ`ߗa`b`ߗadccbbadeggfffe\ ޒ\ޒ\ޓ^ݓ\ܒ\ܒ\ݑ[ܑZܑ[ݑ[ܒZݓ\ݒ^ݒ]ݓ]ܓ]ܓ^ܓ`ݓ]ܔ\ܓ^ܒ]ے]ܒ]ܒ]ܒ\ܒ\ے[ے[ܒ[ܒ[ܒ[ܒ\ܑ]ܒ]ܒ\ۓ[ۓZے[ܒ\ۑ\ܑ]ۑ]ڒ[ۑ\ܑ[ۑ[ڒYۑ[ۑ\ے]ܑ\ܑ\ڒ]ړ]ڒ^ۓ]ۓ^ے[ۓ]ܓ_ۓ]ۓ]ۓ^ۓ_ۓ_ܓ^ܓ^ړ_ܔ^ܓ_ۓ_ܔ]ܔ]ܔ^ܔ]ܔ_ݔ]ݔ]ݔ^ݕ^ޔ_ݔ_ݔ`ޔ_ݕ]ޔ_ޕ_ݕ_ޖ^ݕ_ޔaޕ_ޖ_ߖ`ߗaߖ`ߖ`ߖbߗ`ޖaߗbߘaߘaߘbߘccdfffeޑZ ݑYݑYݓ]ܒ\ۑZܑZܐZڑYېZܐYݑZݒ[ܒ[ܒZܑ[ے[ے]ܒ\ܒ]ܒ]ܒ]ۑ\ۑ[ۑ[ۑ\ۑ[ڑZڑZےZڑZے[ۑ[ۑ\ڑ\ۑ\ۑ\ۑ[ڑZڑZڑZڑ[ڐ[ڐ[ڑZڑYې[ې[ّZڐ\ڑZڑZڐ[ۑ[ڑZڒ\ڑ]ڑZۑ[ۑ\ے]ۑ^ڒ^ے]ڒ]ۓ\ڒ]ڒ]ڒ[ْ]ڒ_ۑ^ڑ^ڒ]ܓ^ܒ^ܒ]ܓ^ۓ]ܓ]ۓ]ܓ[ۓ_ܒ_ݓ_ݓ^ܔ\ܔ]ܔ]ܔ]ݔ^ޔ^ޔ_ݔ_ޔ]ݕ_ޖ`ޕ^ޔ_ߕ_ߖ_ޕ`ߗ_ߗ_ߖ`ߖ``ߖccdddߘdސX ېXېXܒ[ܒZۑY܏Y܏YڐYڏYڐWېZܑ\ܑ[ۑ[ۑZڑZې]ۑ[ۑ[ܑ\ۑ\ڑ\ۑ\ڐZُYِXِ[ڐZّZِYِYڐZې[ُZِZڐZڐZِYِZڐZِXِXِYڐ[ُX؏ZُZڐXِX؏YُZڐ[ڑZڐYڐ[ُ\ڐYِYّZڐ\ڐ]ِ\ڑ\ْ[ّ\ّ\ّ\ّ\ّZڐ]ڐ\ڑ[ۑ[ې]ۑ\ۑ[ڒ\ڒ\ڑ\ے]ܒ\ܒ[ے\ے]ۑ\ے[ܓ[ܓ\ۓ]ۓ]ܔ]ܓ^ܓ^ݓ]ܓ_ܔ^ܕ^ݔ^ݔ^ݕ^ޕ^ޕ_ޕ_ޕ_ޔ`ߖ`ޕbߗaޘbߘdߗcߗbސX ܐXܐXܑYۑZڐXڎYڎXڎWڎWُVُZڐ[ې[ڐ[ڐZې[ڑ[ڐ[ې[ېYڑ[ڐZڏ[ِZؐXُZُZُY؏ZُYِXُYِX؏ZُXُYُXُY؏X؏X؏W؏X؎X؏X؏W׎W؎W؎V؏W؏X؎XُYُYُZُY؏Z؏[ُYُYُYُZؐXِZُ\ِZؐZؐ\ؐZِYِZُ[؏[ڐZِYّ[ڑ[ڐ\ِ\ڑZڐ[ېZۑ\ڑ[ڑ\ڒZڑ[ے[ۑ]ۑ^ڒ\ے\ܒ]ۓZے]ۓ]ۓ[ۓ\ۓ[ܓ]۔]ܔ]ܔ]ݔ^ܔ^ܔ_ݔ^ܕ_ݖaޖaߖbޖaݕb܏V ڎWڎWېZېYېXُWَVَVَWڏXڏWڏZڏXِYُYُZّZِXُZُZُYڏ[ُY؏YؐZُZ؎Y؏X؎X؎XَYُY؏Y؎Y؏V؏WُXَX؎W؏V؎V׍W׎V׎U׎W׍U׍T׍V׍V׍V؎W؎V׏W׎W؎X؎X؎XُXَX؎X؎X؏X؏Y؏Y؏Y؏X׏[׏ZُY؏Y׏Z׎[ؐYُYُYِX؏XُYُ[ِYِXِ[ِZڐ[ڑXڑYڐ\ڑ\ڑ[ّ[ڑ\ڑ\ڒ[ڒ[ڒ[ۓZےZےZܓ[ۓ]ۓ`ے\۔[ۓ]ܓ\ܓ\ܔ^ܔ_ݕaݕ_ޖ`ݖ_ۏT ڎUڎUڐWۏWۏVڏVُU؎T؏WُWِWڎW؏VَXُWَVَYِXُXُYُX؏YُZ؎X؏W؏Y؎X׏W׎W؎X؎V؏X؎X؎V׎T׎V؎W؎V׍V׍U׎U׍U׍V֌V֌T֌T֌S׍T֍U׌W׌V׍U֍V֍U׍W׍V׍U׎U׍X׎V׎W؎X׏W׏V׎W؍Y؏X׏W׎X׏X׎Z׏X׏X؏YُY؏W؎XُWُY؏W؏X؏YِXِZُYِYِZِZّZّ[ّ[ڑZّZؒYڒZۑYڒZڒ[ے[ڒ[ٓ\ۓ[ٓ\ۓZۓ\ڒ]ܒ]ܓ\ܓ\ܕ^ݕ_ܔ_ۍR ڍQڍQ܏U܏V܎UێTۏTڏSڏTڏSڎTڎTڎVێTَTَSڎTَUڍVڎUڎUڎUڎVَUَUڎUڎT؎T؎U؍U؎SڎTٌVٍT؎T،S،S؍S،S׌S؍R׍Q׌Q׌R׋Q׋P׌Q،Q׌R׋S׋R׋R׌R׌R،T׌S׌R׌S،T،S،U؍T؍R؎S؍S؍U؍W؍U؍T؍T؍U؍U؍W؎U؎U؎TَUَVُTَUڎTڏUُVڏVڎUُVِWڐVڐWۏWڏVِWِVڑXڐXۑWڑXېXۑXۑYےYےYےZۑZܑZܒYܑ[ݒYݓXޓ\ޓ[ޔ[܌Q kDkDmFmFmFmFmEmEmDlFmEmDmFlFlFmEmEmFkFlFmFmFlFmClFlFmEmElElElDlDlElElFlEkElElElClCkCkCkCjCjBkBjDkEkDkDjDkDkDkEkFkFkEkElFlElElDlElDlEmEmFmFmFmElEmFmFmFmFlFmFmFmFmGmFmFmFmFmFmFmFnGmFmFnFnGnInGnGnGnFnGnHnGnGnGnHnHnHnHnHoHpIpJoJƃP    @@@-xxxIuuuHvvvHuuuHtttHtttHtttHsssHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHrrrHsssHtttHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHvvvHwwwHwwwHxxxHxxxHxxxHxxxHyyyHyyyHzzzHzzzHzzzH~~~IDDD.mayavi-4.1.0/tvtk/tools/images/image_LICENSE.txt0000644000175100001440000000060111674464502022353 0ustar ischnellusers00000000000000The icons are mostly derived work from other icons. As such they are licensed accordingly to the original license: Crystal: LGPL Unless stated in this file, icons are work of enthought, and are released under a 3 clause BSD-like license. Files and orginal authors: ---------------------------------------------------------------- scene.ico | Crystal mayavi-4.1.0/tvtk/tools/ivtk.py0000755000175100001440000004251711674464502017467 0ustar ischnellusers00000000000000"""ivtk - Interactive TVTK. A utility module that makes VTK/TVTK easier to use from the Python interpreter. For a standalone application simply run this file. To use this under IPython (with -wthread) use the `viewer()` helper function or use the `IVTK` class. The widget can also make use of the tvtk pipeline browser. Here is example usage of the viewer along with tvtk under IPython: >>> from tvtk.tools import ivtk >>> from tvtk.api import tvtk >>> cs = tvtk.ConeSource() >>> m = tvtk.PolyDataMapper() >>> m.input = cs.output >>> a = tvtk.Actor() >>> a.mapper = m >>> v = ivtk.viewer() >>> v.scene.add_actors(a) >>> v.scene.reset_zoom() """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. # Standard library imports. import os.path # Enthought library imports. from pyface.api import FileDialog, GUI, OK, PythonShell from pyface.api import SplitApplicationWindow, ApplicationWindow from pyface.api import SplitPanel from tvtk.pyface.api import Scene, DecoratedScene from pyface.action.api import Action, MenuBarManager,\ MenuManager, Separator from pyface.image_resource import ImageResource from pyface.resource.api import resource_path from traits.api import Float, Str, Instance, Callable from tvtk.api import tvtk from tvtk.pipeline.browser import PipelineBrowser ###################################################################### # The scene icon. ###################################################################### def mk_scene_icon(): icon_path = os.path.join(resource_path(), 'images', 'scene.ico') return ImageResource(icon_path) scene_icon = mk_scene_icon() ###################################################################### # `ExitAction` class. ###################################################################### class ExitAction(Action): """ Exits the application. """ def __init__(self, window): """ Creates a new action. """ self._window = window self.name = "E&xit" def perform(self): """ Performs the action. """ self._window.close() ###################################################################### # `SaveImageAction` class. ###################################################################### class SaveImageAction(Action): """Saves the rendered scene to an image.""" def __init__(self, window): self._window = window self.name = "S&ave Scene" def perform(self): """Pops up a dialog used to save the scene to an image.""" extns = ['*.png', '*.jpg', '*.jpeg', '*.tiff', '*.bmp', '*.ps', '*.eps', '*.tex', '*.rib', '*.wrl', '*.oogl', '*.pdf', '*.vrml', '*.obj', '*.iv'] dlg = FileDialog(parent=self._window.control, action='save as', wildcard='|'.join(extns), title="Save scene to image") if dlg.open() == OK: self._window.scene.save(dlg.path) ###################################################################### # `SaveToClipboardAction` class. ###################################################################### class SaveToClipboardAction(Action): """ Saves rendered scene to the Clipboard. """ def __init__(self, window): """ Creates a new action. """ self._window = window self.name = "&Copy" def perform(self): """ Performs the action. """ self._window.scene.save_to_clipboard() ###################################################################### # `SpecialViewAction` class. ###################################################################### class SpecialViewAction(Action): """Sets the scene to a particular view.""" def __init__(self, window, name, view): """ Creates a new action. """ self._window = window self.name = name self.view = view def perform(self): """ Performs the action. """ # Hack! Works tho. try: meth = getattr(self._window.scene, self.view) meth() except AttributeError: pass def create_ivtk_menu(obj): """Creates a menu bar suitable for all IVTK windows. Parameters ---------- - obj : A Pyface application window. This is the window which requires the menu items. """ menu_bar_manager = MenuBarManager( MenuManager(SaveImageAction(obj), Separator(), ExitAction(obj), name = '&File', ), MenuManager(SaveToClipboardAction(obj), name = '&Edit', ), MenuManager(SpecialViewAction(obj, "&Reset Zoom", 'reset_zoom'), Separator(), SpecialViewAction(obj, "&Isometric", 'isometric_view'), SpecialViewAction(obj, "&X positive", 'x_plus_view'), SpecialViewAction(obj, "X negative", 'x_minus_view'), SpecialViewAction(obj, "&Y positive", 'y_plus_view'), SpecialViewAction(obj, "Y negative", 'y_minus_view'), SpecialViewAction(obj, "&Z positive", 'z_plus_view'), SpecialViewAction(obj, "Z negative", 'z_minus_view'), name = '&View', ) ) return menu_bar_manager ###################################################################### # `SceneWithBrowser` class. ###################################################################### class SceneWithBrowser(SplitPanel): """ Provides an Scene along with an embedded PyCrust Python shell. In the shell, 'scene' and 's' are bound to the Scene.""" # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.3) # The direction in which the panel is split. direction = Str('vertical') # The `Scene` instance into which VTK renders. scene = Instance(Scene) # The `PythonShell` instance. browser = Instance(PipelineBrowser) ########################################################################### # `IWidget` interface. ########################################################################### def destroy(self): if self.scene is not None: self.scene.close() super(SceneWithBrowser, self).destroy() ########################################################################### # Protected 'SplitPanel' interface. ########################################################################### def _create_lhs(self, parent): """ Creates the left hand side or top depending on the style. """ self._create_scene(parent) self.browser = PipelineBrowser(self.scene) self.browser.show(parent=parent) return self.browser.ui.control def _create_rhs(self, parent): """ Creates the right hand side or bottom depending on the style. 's' and 'scene' are bound to the Scene instance.""" self._create_scene(parent) self.scene.renderer.background = 0.5, 0.5, 0.5 return self.scene.control ########################################################################### # Private 'SceneWithBrowser' interface. ########################################################################### def _create_scene(self, parent): """ Make sure that the scene has been created. """ if self.scene is None: self.scene = DecoratedScene(parent) ###################################################################### # `IVTKWithCrust` class. ###################################################################### class IVTKWithCrust(SplitApplicationWindow): """ Provides an Scene along with an embedded PyCrust Python shell. In the shell, 'scene' and 's' are bound to the Scene.""" # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.7) # The direction in which the panel is split. direction = Str('horizontal') # The `Scene` instance into which VTK renders. scene = Instance(Scene) # The `PythonShell` instance. python_shell = Instance(PythonShell) ########################################################################### # 'object' interface. ########################################################################### def __init__(self, **traits): """ Creates a new window. """ # Base class constructor. super(IVTKWithCrust, self).__init__(**traits) self.title = 'TVTK Scene' # Create the window's menu bar. self.menu_bar_manager = create_ivtk_menu(self) ########################################################################### # `IWindow` interface. ########################################################################### def close(self): if self.scene is not None: self.scene.close() super(IVTKWithCrust, self).close() ########################################################################### # Protected 'SplitApplicationWindow' interface. ########################################################################### def _create_lhs(self, parent): """ Creates the left hand side or top depending on the style. """ self.scene = DecoratedScene(parent) self.scene.renderer.background = 0.5, 0.5, 0.5 return self.scene.control def _create_rhs(self, parent): """ Creates the right hand side or bottom depending on the style. 's' and 'scene' are bound to the Scene instance.""" self.python_shell = PythonShell(parent) self.python_shell.bind('scene', self.scene) self.python_shell.bind('s', self.scene) self.python_shell.bind('tvtk', tvtk) return self.python_shell.control ###################################################################### # `IVTKWithCrustAndBrowser` class. ###################################################################### class IVTKWithCrustAndBrowser(SplitApplicationWindow): """ Provides an Scene along with an embedded PyCrust Python shell. In the shell, 'scene' and 's' are bound to the Scene.""" # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.7) # The direction in which the panel is split. direction = Str('horizontal') # The `Scene` instance into which VTK renders. scene = Instance(Scene) # The `PipelineBrowser` instance. browser = Instance(PipelineBrowser) # The ordered split window to use. browser_scene = Instance(SceneWithBrowser) # The `PythonShell` instance. python_shell = Instance(PythonShell) ########################################################################### # 'object' interface. ########################################################################### def __init__(self, **traits): """ Creates a new window. """ # Base class constructor. super(IVTKWithCrustAndBrowser, self).__init__(**traits) self.title = 'TVTK Scene' # Create the window's menu bar. self.menu_bar_manager = create_ivtk_menu(self) ########################################################################### # `IWindow` interface. ########################################################################### def close(self): if self.scene is not None: self.scene.close() super(IVTKWithCrustAndBrowser, self).close() ########################################################################### # Protected 'SplitApplicationWindow' interface. ########################################################################### # The icon of the window icon = Instance(ImageResource, scene_icon) def _create_lhs(self, parent): """ Creates the left hand side or top depending on the style. """ self.browser_scene = SceneWithBrowser(parent) self.scene = self.browser_scene.scene self.browser = self.browser_scene.browser return self.browser_scene.control def _create_rhs(self, parent): """ Creates the right hand side or bottom depending on the style. 's' and 'scene' are bound to the Scene instance.""" self.python_shell = PythonShell(parent) self.python_shell.bind('scene', self.scene) self.python_shell.bind('s', self.scene) self.python_shell.bind('browser', self.browser) self.python_shell.bind('b', self.browser) self.python_shell.bind('tvtk', tvtk) return self.python_shell.control ###################################################################### # `IVTK` class. ###################################################################### class IVTK(ApplicationWindow): """ Provides an Scene along without an embedded Python shell. This is useful when scripting from the vanilla Python or IPython interpreter.""" # The `Scene` instance into which VTK renders. scene = Instance(Scene) # The callable (or class) to create the scene instance _scene_factory = Callable(DecoratedScene) ########################################################################### # 'object' interface. ########################################################################### def __init__(self, **traits): """ Creates a new application window. """ # Base class constructor. super(IVTK, self).__init__(**traits) self.title = 'TVTK Scene' self.menu_bar_manager = create_ivtk_menu(self) ########################################################################### # `IWindow` interface. ########################################################################### def close(self): if self.scene is not None: self.scene.close() super(IVTK, self).close() ########################################################################### # Protected 'ApplicationWindow' interface. ########################################################################### # The icon of the window icon = Instance(ImageResource, scene_icon) def _create_contents(self, parent): """ Create the contents of the window. """ self.scene = self._scene_factory(parent) return self.scene.control ###################################################################### # `IVTKWithBrowser` class. ###################################################################### class IVTKWithBrowser(ApplicationWindow): """ Provides an Scene along without an embedded Python shell. This is useful when scripting from the vanilla Python or IPython interpreter.""" # The `Scene` instance into which VTK renders. scene = Instance(Scene) # The `PipelineBrowser` instance. browser = Instance(PipelineBrowser) # The ordered split window to use. browser_scene = Instance(SceneWithBrowser) ########################################################################### # 'object' interface. ########################################################################### def __init__(self, **traits): """ Creates a new application window. """ # Base class constructor. super(IVTKWithBrowser, self).__init__(**traits) self.title = 'TVTK Scene' self.menu_bar_manager = create_ivtk_menu(self) ########################################################################### # `IWindow` interface. ########################################################################### def close(self): if self.scene is not None: self.scene.close() super(IVTKWithBrowser, self).close() ########################################################################### # Protected 'ApplicationWindow' interface. ########################################################################### # The icon of the window icon = Instance(ImageResource, scene_icon) def _create_contents(self, parent): """ Create the contents of the window. """ self.browser_scene = SceneWithBrowser(parent) self.scene = self.browser_scene.scene self.browser = self.browser_scene.browser return self.browser_scene.control ###################################################################### # Utility functions. ###################################################################### def viewer(browser=True, instantiate_gui=False): """Creates an IVTK instance, opens the window and returns the embedded scene inside it. This is useful from an IPython/vanilla Python shell. It returns the viewer window instance. Parameters ---------- - browser : `bool` (default, True) If True, creates an IVTK scene with an embedded PipelineBrowser. If False, does not create it. - instantiate_gui : `bool` (default: False) If True, create an instance of GUI(). This is useful when this function is invoked from within an IPython shell. OTOH, if this is called from within a wxPython app (or with ipython -wthread) you don't want to start another GUI instance. """ if instantiate_gui: gui = GUI() if browser: v = IVTKWithBrowser(size=(600,600)) else: v = IVTK(size=(600,600)) v.open() return v def main(): # Create the GUI. gui = GUI() # Create and open an application window. window = IVTKWithCrustAndBrowser(size=(800,600)) window.open() # Start the GUI event loop! gui.start_event_loop() if __name__ == '__main__': main() mayavi-4.1.0/tvtk/tools/visual.py0000644000175100001440000024013411674464502020005 0ustar ischnellusers00000000000000""" A module that provides VPython like capabilities. For more details about VPython, refer the official website. VPython : http://www.vpython.org/index.html The API is based on VPython, but instead of using OpenGL and other C libraries, this is based completly on TVTK, and Traits and Numpy. All of the demos are also translated from VPython examples. The implementation is done using object oriented design, and each visualization capability is implemented as a seperate traits class. The attributes of each class could be independently edited. The API is kept as similar to VPython as much as possible. This module offers the following classes : Display actors sphere, cylinder, cone, box, arrow, curve, ring, helix, ellipsoid Functionality classes frame, vector, animator Utility functions iterator, remove_actor, show To see examples of classes and functions look at the `test_*` functions at the end of this file. Here is a quick example demonstrating how to use visual. Note: To provide threading support for WXWidgets which are essential for all GUI fucntionalities in visual, the visual module should be interactively used in ipython only when wthread mode is enabled. visual will not work properly in an interactive mode in vanilla python interpretor. :: $ ipython -wthread In [1]: from tvtk.tools import visual In [2]: visual.test_sphere() In [3]: s = visual.sphere() # Create a sphere actor In [4]: s.edit_traits() # Edit sphere actor's properties via GUI """ # Author: Raashid Baig # Prabhu Ramachandran # # License: BSD Style. # Version: 1.0 # Year : 2007 # Standard library imports. import sys import numpy import time from math import sin, cos, pi, sqrt, acos, asin from vtk.util import colors as color # Enthought library imports. from traits.api import HasTraits, Trait, Instance, Tuple, Int, \ Float, Range, Button, Array, Color, Bool, Any, List, Enum from traitsui.api import View, Item, Group, RGBColorEditor, RangeEditor from traitsui.message import message from tvtk.api import tvtk from tvtk.tools import ivtk from pyface.api import GUI from pyface.timer.api import Timer from tvtk.tvtk_base import TVTKBase, vtk_color_trait # Set the global variable to None, for it's future use in function # get_viewer() _viewer = None ################################################################# ################ Utility function #################### ################################################################# def _create_viewer(): """Creates and retunrs the ivtk viewer to get_viewer() function""" v = ivtk.viewer(browser = False) v.scene.background = (0,0,0) GUI.process_events() return v def set_viewer(viewer): """Set the global viewer. Handy if you need to use visual from your own viewer. Pass an object that has a `scene` trait containing a tvtk scene instance. """ global _viewer _viewer = viewer def get_viewer(): """ Creates and returns an ivtk viewer. If the fuction is called 1st time than creates and returns the viewer, otherwise the originally created viewer itself is returned. Checking is done through the global variable _viewer. """ global _viewer if _viewer is None: # If no viewer has been created till this point in program # _create_viewer() is called and a new ivtk viewer is created _viewer = _create_viewer() else: try: _viewer.scene.render() except: # If the original ivtk window is destroyed when function # is called more than once an exception is raised so that # a new ivtk browser is created and returned. _viewer = _create_viewer() return _viewer def show_actor(*tvtk_actors): """ Gets an ivtk viewer from the function get_viewer() and adds all the actors given, to the ivtk scene. """ v = get_viewer() v.scene.add_actors(tvtk_actors) v.scene.reset_zoom() def remove_actor(*tvtk_actors): """ Gets the ivtk viewer from the function get_viewer() and removes all the actors given, from the ivtk scene.""" v = get_viewer() for x in tvtk_actors: v.scene.remove_actors(x.actor) v.scene.reset_zoom() def show(): """This function has to be called at the end in a stand alone visual program. Note - Don't call this function when running visual from ipython iterpretor in an interactive mode """ if '-wthread' in sys.argv: pass else: gui = GUI() gui.start_event_loop() def iterate(millisec, callable, *args, **kwargs): """Creates an instance of utility class Animator and returns it, used for programs with animations, see examples for demonstration """ return Animator(millisec, callable, *args, **kwargs) def translate(old, new, points): """Translate function takes 3 arguments the old position, new position and the points. The function translates all the points to the new position and returns the new points""" diff = new - old points[:] = points + diff return points def translate_points(diff, points): """Translate function takes 2 arguments the with which the points are required to be translated and the points. The function translates all the points to the new position and returns the new points""" points[:] = points + diff return points def _create_rotation_matrix(axis, angle): """Create a rotation matrix given an axis and an angle (in degrees) to rotate by. """ axis = numpy.asarray(axis, dtype = float) axis /= sqrt(numpy.inner(axis, axis)) #Normalizing the axis x = axis[0] y = axis[1] z = axis[2] t = angle*pi/180 cost = cos(t) sint = sin(t) rotator = numpy.zeros((3,3), float) rotator[0][0] = cost + (1-cost)*(x*x) rotator[0][1] = (1-cost)*x*y - (sint)*z rotator[0][2] = (1-cost)*x*z + (sint)*y rotator[1][0] = (1-cost)*y*x + (sint)*z rotator[1][1] = cost + (1-cost)*(y*y) rotator[1][2] = (1-cost)*y*z - (sint)*x rotator[2][0] = (1-cost)*z*x - (sint)*y rotator[2][1] = (1-cost)*z*y + (sint)*x rotator[2][2] = cost + (1-cost)*(z*z) return rotator def axis_changed(old, new, pos, points): """The function takes 4 arguments, the old and the new axis, the position and points. All arguments should be given as a numpy array, The first 3 can also be given in form of a tuple. The function rotates all the points so that they become aligned with the new axis""" # Creating a working normalized copy of old axis o = old/float(sqrt(numpy.inner(old, old))) # Creating a working normalized copy of new axis n = new/sqrt(numpy.inner(new, new)) dpdt = numpy.dot(o, n) if abs(abs(dpdt) - 1.0) < 1e-10: if dpdt < 0: #This is a must in the case when the new and the old axis are #opposite to each other diff = -pos points = translate_points(diff, points) #Flipping the points to reverse the axis points[:] = -points diff = pos points = translate_points(diff, points) return points else: #This is useful in the case when the new and the old axis are #very close to each other return points alpha = acos(dpdt)# Calculating angle between the old & new axis raxis = numpy.cross(o, n)# Calculating the axis about which to rotate #Creating the rotation multiplication matrix data = _create_rotation_matrix(raxis, 180.0*alpha/pi) if (numpy.allclose(pos, 0.0)): points[:] = numpy.dot(points, data.T) return points else: diff = -pos points = translate_points(diff, points) points[:] = numpy.dot(points, data.T) diff = pos points = translate_points(diff, points) return points def rotate(axis, angle, origin, pos, points, maxis): """Rotate function takes 6 arguments the axis about which the actor has to be rotated, the angle with which the actor has to be rotated, the point (origin) about which actor has to be rotated and posistion, points and current axis of the actor. The function returns the new position, points and axis of the actor after the rotation.""" data = _create_rotation_matrix(axis, angle) if (numpy.allclose(pos, 0.0) and numpy.allclose(origin, 0.0)): points[:] = numpy.dot(points, data.T) raxis = numpy.dot(maxis, data.T) return pos, points, raxis else: diff = (-1*origin[0], -1*origin[1], -1*origin[2]) pos = pos - origin points = translate_points(diff, points) points[:] = numpy.dot(points, data.T) pos = numpy.dot(pos, data.T) diff = (origin[0], origin[1], origin[2]) points = translate_points(diff, points) pos = pos + origin raxis = numpy.dot(maxis, data.T) return pos, points, raxis def rotate_single_point(axis, angle, origin, pos, maxis): """Rotate function takes 5 arguments the axis about which the actor has to be rotated, the angle with which the actor has to be rotated, the point (origin) about which actor has to be rotated and posistion, and current axis of the actor. The function returns the new position and new axis of the actor after the rotation.""" axis = numpy.asarray(axis, dtype = float) data = _create_rotation_matrix(axis, angle) if (numpy.allclose(pos, 0.0)): raxis = numpy.dot(maxis, data.T) return pos, raxis else: pos = pos - origin pos = numpy.dot(pos, data.T) pos = pos + origin raxis = pos/sqrt(numpy.inner(pos, pos)) return pos, raxis def scale(scale_factor, points, pos): """Scale function takes 2 arguments the scaling_factor in a form of list, or a tuple giving the scale factor for x,y and z axis. The function returns the new points ofthe actor after scaling""" #Creating the scaling multiplication matrix sc = numpy.asarray(scale_factor, dtype=float) data = numpy.diag(sc) if (numpy.allclose(pos, 0.0)): points[:] = numpy.dot(points, data.T) return points else: diff = (-1*pos[0], -1*pos[1], -1*pos[2]) points = translate_points(diff, points) points[:] = numpy.dot(points, data.T) diff = (pos[0], pos[1], pos[2]) points = translate_points(diff, points) return points ################################################################# ####################### Functionality classes ################### ################################################################# class VTimer(Timer): def __init__(self, millisecs, callable, *args, **kw_args): #Initializing the init method of parent class Timer Timer.__init__(self, millisecs, callable, *args, **kw_args) self.viewer = get_viewer() self.viewer.on_trait_change(self._close, 'closing') def _close(self): self.Stop() print "Stopping iterations since the viewer has been closed." def Notify(self): """Overridden to call the given callable. """ try: self.callable(*self.args, **self.kw_args) except StopIteration: self.Stop() except: self.Stop() raise class Animator(HasTraits): ##################################################################### # Traits definitions start_animation = Button('Start Animation') stop_animation = Button('Stop Animation') time_period = Range(1, 100000, 100, desc='Specifies frequency with which timer is called') itimer = Instance(VTimer) ###################################################################### # User interface view event_group = Group(Item('start_animation', style = 'simple'), Item('_'), Item('stop_animation', style = 'simple'), Item('_'), show_labels = False,) traits_view = View(event_group, Item(name = 'time_period'), title = 'Animation Controler', buttons = ['OK'], width = 250) ###################################################################### # Initialize object def __init__(self, millisec, callable, *args, **kwargs): self.time_period = millisec self.itimer = VTimer(millisec, callable, *args, **kwargs) ###################################################################### # Non-public methods, Event handlers def _start_animation_fired(self): self.itimer.Start() def _stop_animation_fired(self): self.itimer.Stop() def _time_period_changed(self, value): t = self.itimer if t is None: return t.millisec = value if t.IsRunning(): t.Stop() t.Start(value) class MVector(numpy.ndarray): """MVector class gives many of the functionalities given by Vector of VPython""" def __new__(subtype, x = 0.0, y = 0.0, z = 0.0): data = numpy.array((x, y, z), float) ret = numpy.ndarray.__new__(subtype, shape = (3,), buffer = data, dtype=float, order = False) return ret.copy() def _get_x(self): return self[0] def _set_x(self, val): self[0] = val x = property(_get_x, _set_x) def _get_y(self): return self[1] def _set_y(self, val): self[1] = val y = property(_get_y, _set_y) def _get_z(self): return self[2] def _set_z(self, val): self[2] = val z = property(_get_z, _set_z) def dot(vec1, vec2): """ Function performs the dot vector multiplication of 2 vector instances and returning a new vector instance equal to the dot product of given vectors""" i = vec1.x * vec2.x j = vec1.y * vec2.y k = vec1.z * vec2.z dot = i + j + k return dot def cross(vec1, vec2): """ Function performing the cross vector multiplication of 2 vector instances and returning a new vector instance equal to the cross product of given vectors""" i = (vec1.y*vec2.z - vec1.z*vec2.y) j = (vec1.z*vec2.x - vec1.x*vec2.z) k = (vec1.x*vec2.y - vec1.y*vec2.x) cross = MVector(i, j, k) return cross def mag(vec): """ Function computes and returns the magnitude of a vector""" mag = sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2) return mag def norm(vec): """ Function computes and returns the normalized form of a given vector""" norm = vec/sqrt(numpy.inner(vec, vec)) return norm class Frame(HasTraits): """Frame groups together all the actors given to it into a single unit, so that they can be manipulated as a combined entity.""" ##################################################################### # Traits definitions axis = Array(value = (1.0, 0.0, 0.0), desc = 'the frame axis') x = Range(-1e299, 1e299, 0.0, desc = 'the X coordinate of frame objects') y = Range(-1e299, 1e299, 0.0, desc = 'the Y coordinate of frame objects') z = Range(-1e299, 1e299, 0.0, desc = 'the Z coordinate of frame objects') pos = Array(value = (0.0, 0.0, 0.0), desc = 'the frame pos') objects = List visibility = Bool(True) viewer = Any ###################################################################### # User interface view traits_view = View(Group(Item(name = 'x', label = 'Pos X'), Item(name = 'y', label = 'Pos Y'), Item(name = 'z', label = 'Pos Z'), Item(name = 'visibility'), label = 'Frame Properties', show_border = True), buttons=['OK'], ) def __init__(self, *arguments, **traits): self.arg = list(arguments) HasTraits.__init__(self, **traits) self.keys = traits.keys() self._pos_changed(numpy.array([0.0, 0.0, 0.0]), self.pos) self._x_changed(0.0, self.x) self._y_changed(0.0, self.y) self._z_changed(0.0, self.z) #self._axis_changed(numpy.array([1.0, 0.0, 0.0]), self.axis) ###################################################################### # Non-public methods, Event handlers def _pos_changed(self, old, new): diff = new - old for a in self.arg: a.pos = a.pos + diff def _x_changed(self, old, new): diff = new - old for a in self.arg: a.x = a.x + diff def _y_changed(self, old, new): diff = new - old for a in self.arg: a.y = a.y + diff def _z_changed(self, old, new): diff = new - old for a in self.arg: a.z = a.z + diff def _axis_changed(self, old, new): if (numpy.allclose(old, new)): pass else: o = old/sqrt(numpy.inner(old, old)) n = new/sqrt(numpy.inner(new, new)) raxis = numpy.cross(o, n) # raxis is the axis about which the rotation of th objects # will be performed so that they have the new axis alpha = acos(numpy.dot(o, n)) # alpha is the angle between the old and the new axis alpha = 180.0*alpha/pi for a in self.arg: a.rotate(alpha, raxis, self.pos) def _visibility_changed(self, value): val = int(value) if (val == 1): for a in self.arg: a.actor.visibility = 1 else: for a in self.arg: a.actor.visibility = 0 ###################################################################### # Object's public methods def rotate(self, angle, axis, origin = numpy.array([0.0, 0.0, 0.0])): for a in self.arg: a.rotate(angle, axis, origin) pts = numpy.array([1.0, 0.0, 0.0])#junk points passed as arguments pos, pts, faxis = rotate(axis, angle, origin, self.pos, pts, self.axis) self.set(pos=pos, axis = faxis, trait_change_notify = False) ################################################################### ###################### Actor Classes ############################## ################################################################### class Curve(HasTraits): """Curve class creates a polydata source using point and connectivity arrays given by the user, which inturn is used to create a polydata actor""" ##################################################################### # Traits definitions points = Trait(None, None, Array('d', shape=(None,3)), desc='the points of the curve') pos = Array(value = (0.0, 0.0, 0.0), desc = 'the curve pos') radius = Range(0.0, 1.0e299, value = 0.01, desc = 'the radius of curve tube') axis = Array(value = (1.0, 0.0, 0.0), desc = 'the curve axis') x = Range(-1e299, 1e299, 0.0, desc = 'the X coordinate of curve') y = Range(-1e299, 1e299, 0.0, desc = 'the Y coordinate of curve') z = Range(-1e299, 1e299, 0.0, desc = 'the Z coordinate of curve') color = vtk_color_trait((1.0, 1.0, 1.0)) representation = Enum('s', 'w', 'p') visibility = Bool(True) polydata = Instance(tvtk.PolyData, args=()) property = Instance(tvtk.Property) stripper = Instance(tvtk.Stripper, args=()) tube = Instance(tvtk.TubeFilter, args=()) actor = Instance(tvtk.Actor, args=()) # tvtk Actor, for the usual pipeline architecture. viewer = Any ###################################################################### # User interface view traits_view = View(Group(Item(name = 'color'), Item(name = 'visibility'), Item(name = 'radius'), Item(name = 'x', label = 'Pos X'), Item(name = 'y', label = 'Pos Y'), Item(name = 'z', label = 'Pos Z'), Item(name = 'representation'), label = 'Curve Properties', show_border = True), buttons=['OK'], ) def __init__(self, **traits): self.property = self.actor.property HasTraits.__init__(self, **traits) self._points_changed(self.points) self._color_changed(self.color) self._visibility_changed(self.visibility) self._radius_changed(self.radius) self._axis_changed(numpy.array((1.0, 0.0, 0.0)), self.axis) self.stripper.input = self.polydata self.tube.input = self.stripper.output self.tube.number_of_sides = 4 self.tube.capping = 1 m = tvtk.PolyDataMapper() m.input = self.tube.output self.actor.mapper = m self.property = self.actor.property self.property.representation = self.representation show_actor(self.actor) self.viewer = get_viewer() self.property.on_trait_change(self.viewer.scene.render) self.actor.on_trait_change(self.viewer.scene.render) self.tube.on_trait_change(self.viewer.scene.render) ###################################################################### # Object's public methods def append(self, pnt): """Function appeneds new points given as arguments to the current points""" self.extend([pnt]) def extend(self, pts): if self.points is None: p = pts else: n = self.points.shape[0] p = numpy.resize(self.points, (n+len(pts), 3)) p[:n] = self.points p[n:] = pts self.points = p self.update() def update(self): self.polydata.modified() self.viewer.scene.render() def rotate(self, angle, axis, origin = numpy.array([0.0, 0.0, 0.0])): """Function takes atleast 2 arguments: axis about which to rotate the actor and angle with which to rotate the actor, the 3rd agrument is origin i.e. the point about which to rotate the actor, by default it is set to the global origin""" p, pi, ax = rotate(axis, angle, origin, self.pos, self.points, numpy.array([0.0, 0.0, 0.0])) self.set(pos = p, trait_change_notify = False) self.set(points = pi, trait_change_notify = False) self.set(axis = ax, trait_change_notify = False) self.polydata.points = self.points self.polydata.modified() self.render() def render(self): """Function redraws/refreshs the ivtk viewer's scene""" v = self.viewer if v is not None: v.scene.render() ###################################################################### # Non-public methods, Event handlers def _points_changed(self, value): self.polydata.points = value if value is None: np = 0 lines = None else: np = len(self.points) - 1 lines = numpy.zeros((np, 2), 'l') lines[:,0] = numpy.arange(0, np-0.5, 1, 'l') lines[:,1] = numpy.arange(1, np+0.5, 1, 'l') self.polydata.lines = lines v = self.viewer if v is not None: v.scene.render() def _x_changed(self, value): self.x = value self.pos = (self.x, self.pos[1],self.pos[2]) def _y_changed(self, value): self.y = value self.pos = (self.pos[0], self.y, self.pos[2]) def _z_changed(self, value): self.z = value self.pos = (self.pos[0], self.pos[1], self.z) def _pos_changed(self, old, new): self.set(x = new[0], trait_change_notify = False) self.set(y = new[1], trait_change_notify = False) self.set(z = new[2], trait_change_notify = False) p = translate(old, new, self.points) self.set(points = p, trait_change_notify = False) self.polydata.points = self.points self.polydata.modified() self.render() def _axis_changed(self, old, new): self.points = axis_changed(old, new, self.pos, self.points) self.polydata.points = self.points self.polydata.modified() self.render() def _color_changed(self, value): self.actor.property.color = value def _radius_changed(self, value): self.tube.radius = self.radius def _representation_changed(self, value): self.property.representation = self.representation self.property.modified() self.render() def _visibility_changed(self, value): val = int(value) if (val == 1): self.actor.visibility = 1 else: self.actor.visibility = 0 class Ring(HasTraits): """Ring class creates a Ring form tvtk polydata, follows the usual VTK pipeline and creates a ring actor.""" ##################################################################### # Traits definitions points = Array('d', shape = (360,3)) radius = Range(-1e299, 1e299, value = 0.5, desc = 'the ring radius') pos = Array(value = (0.0, 0.0, 0.0), desc = 'the ring pos') axis = Array(value = (1.0, 0.0, 0.0), desc = 'the ring axis') color = vtk_color_trait((1.0, 1.0, 1.0)) x = Range(-1e299, 1e299, 0.0, desc = 'the X coordinate of ring center') y = Range(-1e299, 1e299, 0.0, desc = 'the Y coordinate of ring center') z = Range(-1e299, 1e299, 0.0, desc = 'the Z coordinate of ring center') representation = Enum('s', 'w', 'p') thickness = Range(0, 1e299, value = 0.01, desc = 'the ring thickness') visibility = Bool(True) viewer = Any polydata = Instance(tvtk.PolyData, ()) property = Instance(tvtk.Property) tube = Instance(tvtk.TubeFilter, ()) actor = Instance(tvtk.Actor, ()) # tvtk Actor, for the usual pipeline architecture. ###################################################################### # User interface view traits_view = View(Group(Item(name = 'radius'), Item(name = 'thickness'), Item(name = 'x', label = 'Pos X'), Item(name = 'y', label = 'Pos Y'), Item(name = 'z', label = 'Pos Z'), Item(name = 'color'), Item(name = 'visibility'), Item(name = 'representation'), label = 'Ring Properties', show_border = True), buttons=['OK'], ) def __init__(self, **traits): self.property = self.actor.property HasTraits.__init__(self, **traits) self._create_points() self._pos_changed(numpy.array([0.0, 0.0, 0.0]), self.pos) self._x_changed(self.x) self._y_changed(self.y) self._z_changed(self.z) self._color_changed(self.color) self._visibility_changed(self.visibility) self._thickness_changed(self.thickness) self._axis_changed(numpy.array((1.0, 0.0, 0.0)), self.axis) normals = tvtk.PolyDataNormals(input = self.polydata) self.tube.input = normals.output self.tube.number_of_sides = 4 self.tube.capping = 1 m = tvtk.PolyDataMapper() m.input = self.tube.output self.actor.mapper = m self.property = self.actor.property self.property.representation = self.representation show_actor(self.actor) self.viewer = get_viewer() self.property.on_trait_change(self.viewer.scene.render) self.actor.on_trait_change(self.viewer.scene.render) self.tube.on_trait_change(self.viewer.scene.render) ###################################################################### # Non-public methods, Event handlers def _create_points(self): for i in range(0,360,1): theta = i*pi/180 self.points[i][0] = 0.0 self.points[i][1] = self.radius*sin(theta) self.points[i][2] = self.radius*cos(theta) np = len(self.points) - 1 lines = numpy.zeros((np, 2), 'l') lines[:,0] = numpy.arange(0, np-0.5, 1, 'l') lines[:,1] = numpy.arange(1, np+0.5, 1, 'l') self.polydata.points = self.points self.polydata.lines = lines v = self.viewer if v is not None: v.scene.render() def _color_changed(self, value): self.actor.property.color = value def _radius_changed(self, old, new): factor = new/old if (numpy.allclose(self.pos, 0.0)): self.points[:] = factor*self.points[:] self.polydata.modified() self.render() else: c = self.pos diff = (0.0, 0.0, 0.0) - c self.points = translate_points(diff, self.points) self.points[:] = factor*self.points[:] diff = c self.points = translate_points(diff, self.points) self.polydata.points = self.points self.polydata.modified() self.render() def _x_changed(self, value): self.x = value self.pos = (self.x, self.pos[1],self.pos[2]) def _y_changed(self, value): self.y = value self.pos = (self.pos[0], self.y, self.pos[2]) def _z_changed(self, value): self.z = value self.pos = (self.pos[0], self.pos[1], self.z) def _pos_changed(self, old, new): self.set(x = new[0], trait_change_notify = False) self.set(y = new[1], trait_change_notify = False) self.set(z = new[2], trait_change_notify = False) self.points = translate(old, new, self.points) self.polydata.points = self.points self.polydata.modified() self.render() def _axis_changed(self, old, new): self.points = axis_changed(old, new, self.pos, self.points) self.polydata.points = self.points self.polydata.modified() self.render() def _representation_changed(self, value): self.property.representation = self.representation self.property.modified() self.render() def _visibility_changed(self, value): val = int(value) if (val == 1): self.actor.visibility = 1 else: self.actor.visibility = 0 def _thickness_changed(self, value): self.tube.radius = value ###################################################################### # Object's public methods def rotate(self, angle, axis, origin = numpy.array([0.0, 0.0, 0.0])): """Function takes atleast 2 arguments: angle with which to rotate the actor, and the axis about which to rotate the actor, the 3rd agrument is origin i.e. the point about which to rotate the actor, by default it is set to the global origin""" p, pi, ax = rotate(axis, angle, origin, self.pos, self.points, self.axis) self.set(pos = p, trait_change_notify = False) self.points = pi self.set(axis = ax, trait_change_notify = False) self.polydata.points = self.points self.polydata.modified() self.render() def render(self): v = self.viewer if v is not None: v.scene.render() class Cone(HasTraits): """Cone class creates Cone from polydata obtained from tvtk ConeSource, follows the usual VTK pipeline and creates a Cone actor, which is passed to the show_actor() function as an argument. """ ##################################################################### # Traits definitions points = Array('d', shape = (7,3)) radius = Range(0.0, 100.0, value = 0.5, desc = 'the cone radius') height = Range(0.0, 100.0, value = 1.0, desc = 'the cone height') pos = Array(value = (0.0, 0.0, 0.0), desc = 'the cone pos') axis = Array(value = (1.0, 0.0, 0.0), desc = 'the cone axis') color = vtk_color_trait((1.0, 1.0, 1.0)) x = Range(-1e299, 1e299, 0.0, desc = 'the X coordinate of cone center') y = Range(-1e299, 1e299, 0.0, desc = 'the Y coordinate of cone center') z = Range(-1e299, 1e299, 0.0, desc = 'the Z coordinate of cone center') representation = Enum('s', 'w', 'p') visibility = Bool(True) viewer = Any polydata = Instance(tvtk.PolyData, ()) property = Instance(tvtk.Property) actor = Instance(tvtk.Actor, ()) ###################################################################### # User interface view traits_view = View(Group(Item(name = 'radius'), Item(name = 'height'), Item(name = 'x', label = 'Pos X'), Item(name = 'y', label = 'Pos Y'), Item(name = 'z', label = 'Pos Z'), Item(name = 'color'), Item(name = 'visibility'), Item(name = 'representation'), label = 'Cone Properties', show_border = True), buttons=['OK'], ) def __init__(self, **traits): self.property = self.actor.property HasTraits.__init__(self, **traits) self._create_points(self.radius, self.height, self.pos, self.axis) self._pos_changed(numpy.array([0.0, 0.0, 0.0]), self.pos) self._x_changed(self.x) self._y_changed(self.y) self._z_changed(self.z) self._color_changed(self.color) m = tvtk.PolyDataMapper() m.input = self.polydata self.actor.mapper = m self.property = self.actor.property self.property.representation = self.representation show_actor(self.actor) # passing the actors function for rendering self.viewer = get_viewer() # getting the ivtk viewer self.property.on_trait_change(self.viewer.scene.render) self.actor.on_trait_change(self.viewer.scene.render) ###################################################################### # Non-public methods, Event handlers def _create_points(self, r, h, c, d): cs = tvtk.ConeSource(radius = r, height = h, center = tuple(c), direction = tuple(d)) cs.update() ps = cs.output points = ps.points.to_array() self.points = points self.polydata.points = self.points self.polydata.polys = ps.polys return points, ps.polys def _color_changed(self, value): self.actor.property.color = value def _radius_changed(self): points, lines = self._create_points(self.radius, self.height, self.pos, self.axis) self.polydata.points = points self.polydata.modified() self.render() def _height_changed(self): points, lines = self._create_points(self.radius, self.height, self.pos, self.axis) self.polydata.points = points self.polydata.modified() self.render() def _pos_changed(self, old, new): self.set(x = new[0], trait_change_notify = False) self.set(y = new[1], trait_change_notify = False) self.set(z = new[2], trait_change_notify = False) points, lines = self._create_points(self.radius, self.height, self.pos, self.axis) self.points = points self.polydata.modified() self.render() def _axis_changed(self): points, lines = self._create_points(self.radius, self.height, self.pos, self.axis) self.polydata.points = points self.polydata.modified() self.render() def _x_changed(self, value): self.x = value self.pos = (self.x, self.pos[1],self.pos[2]) def _y_changed(self, value): self.y = value self.pos = (self.pos[0], self.y, self.pos[2]) def _z_changed(self, value): self.z = value self.pos = (self.pos[0], self.pos[1], self.z) def _representation_changed(self, value): self.property.representation = self.representation self.property.modified() self.render() def _visibility_changed(self, value): val = int(value) if (val == 1): self.actor.visibility = 1 else: self.actor.visibility = 0 ###################################################################### # Object's public methods def rotate(self, angle, axis, origin = numpy.array([0.0, 0.0, 0.0])): """Function takes atleast 2 arguments: axis about which to rotate the actor and angle with which to rotate the actor, the 3rd agrument is origin i.e. the point about which to rotate the actor, by default it is set to the global origin""" p, pi, ax = rotate(axis, angle, origin, self.pos, self.points, self.axis) self.set(pos = p, trait_change_notify = False) self.points = pi self.set(axis = ax, trait_change_notify = False) self.polydata.points = self.points self.polydata.modified() self.render() def render(self): v = self.viewer if v is not None: v.scene.render() class Sphere(HasTraits): """Sphere class creates Sphere from tvtk SphereSource, follows the usual VTK pipeline and creates a Sphere actor, which is passed to the show_actor() function as an argument. """ ##################################################################### # Traits definitions radius = Range(0.0, 1e299, value = 0.5, desc = 'the sphere radius') pos = Array(value = (0.0, 0.0, 0.0), desc = 'the sphere pos') axis = Array(value = (1.0, 0.0, 0.0), desc= 'the sphere axis') color = vtk_color_trait((1.0, 1.0, 1.0)) x = Range(-1e299, 1e299, 0.0, desc = 'the X coordinate of sphere center') y = Range(-1e299, 1e299, 0.0, desc = 'the Y coordinate of sphere center') z = Range(-1e299, 1e299, 0.0, desc = 'the Z coordinate of sphere center') representation = Enum('s', 'w', 'p') visibility = Bool(True) viewer = Any polydata = Instance(tvtk.PolyData, ()) property = Instance(tvtk.Property) actor = Instance(tvtk.Actor, ()) # tvtk Actor, for the usual pipeline architecture. ###################################################################### # User interface view traits_view = View(Group(Item(name = 'radius', style = 'simple'), Item(name = 'x', label = 'Pos X'), Item(name = 'y', label = 'Pos Y'), Item(name = 'z', label = 'Pos Z'), Item(name = 'color'), Item(name = 'visibility'), Item(name = 'representation'), label = 'Sphere Properties', show_border = True), buttons=['OK'], ) def __init__(self, **traits): self.property = self.actor.property HasTraits.__init__(self, **traits) self._create_points(self.radius, self.pos) self._color_changed(self.color) self._visibility_changed(self.visibility) self._x_changed(self.x) self._y_changed(self.y) self._z_changed(self.z) normals = tvtk.PolyDataNormals(input = self.polydata) m = tvtk.PolyDataMapper(input = normals.output) # the usual vtk pipleine countinuation self.actor.mapper = m self.property = self.actor.property self.property.representation = self.representation show_actor(self.actor) # passing the actors function for rendering self.viewer = get_viewer() # getting the ivtk viewer self.property.on_trait_change(self.viewer.scene.render) self.actor.on_trait_change(self.viewer.scene.render) ###################################################################### # Non-public methods, Event handlers def _create_points(self, r, c): sp = tvtk.SphereSource(radius = r, center = tuple(c), phi_resolution = 20, theta_resolution = 20) sp.update() ps = sp.output points = ps.points.to_array() self.points = points self.polydata.points = self.points self.polydata.polys = ps.polys return points, ps.polys def _radius_changed(self, value): points, polys = self._create_points(self.radius, self.pos) self.polydata.points = points self.polydata.modified() self.render() def _color_changed(self, value): self.actor.property.color = value def _pos_changed(self, old, new): self.set(x = new[0], trait_change_notify = False) self.set(y = new[1], trait_change_notify = False) self.set(z = new[2], trait_change_notify = False) points, lines = self._create_points(self.radius, self.pos) self.polydata.points = points self.polydata.modified() self.render() def _axis_changed(self, old, new): self.points = axis_changed(old, new, self.pos, self.points) self.polydata.points = self.points self.polydata.modified() self.render() def _x_changed(self, value): self.x = value self.pos = (self.x, self.pos[1],self.pos[2]) def _y_changed(self, value): self.y = value self.pos = (self.pos[0], self.y, self.pos[2]) def _z_changed(self, value): self.z = value self.pos = (self.pos[0], self.pos[1], self.z) def _representation_changed(self, value): self.property.representation = self.representation self.property.modified() self.render() def _visibility_changed(self, value): val = int(value) if (val == 1): self.actor.visibility = 1 else: self.actor.visibility = 0 ###################################################################### # Object's public methods def render(self): v = self.viewer if v is not None: v.scene.render() def rotate(self, angle, axis, origin = numpy.array([0.0, 0.0, 0.0])): """Function takes atleast 2 arguments: axis about which to rotate the actor and angle with which to rotate the actor, the 3rd agrument is origin i.e. the point about which to rotate the actor, by default it is set to the global origin""" p, pi, ax = rotate(axis, angle, origin, self.pos, self.points, self.axis) self.set(pos = p, trait_change_notify = False) self.points = pi self.set(axis = ax, trait_change_notify = False) self.polydata.points = self.points self.polydata.modified() self.render() class Cylinder(HasTraits): """Cylinder class creates Cylinder from tvtk CylinderSource, follows the usual VTK pipeline and creates a Sphere actor, which is passed to the show_actor() function as an argument. """ ##################################################################### # Traits definitions # XXX: These should really not be ranges, but positive numbers. radius = Range(0.0, 1e299, value = 1.0, desc = 'the cylinder radius') length = Range(0.0, 1e299, value = 1.0, desc = 'the cylinder length') pos = Array(value = (0.0, 0.0, 0.0), desc = 'the cylinder pos') axis = Array(value = (1.0, 0.0, 0.0), desc = 'the cylinder axis') points = Array('d', shape = (60,3)) color = vtk_color_trait((1.0, 1.0, 1.0)) x = Range(-1e299, 1e299, 0.0, desc = 'the X coordinate of cylinder center') y = Range(-1e299, 1e299, 0.0, desc = 'the Y coordinate of cylinder center') z = Range(-1e299, 1e299, 0.0, desc = 'the Z coordinate of cylinder center') representation = Enum('s', 'w', 'p') visibility = Bool(True) viewer = Any polydata = Instance(tvtk.PolyData, ()) actor = Instance(tvtk.Actor, ()) # tvtk Actor, for the usual pipeline architecture. property = Instance(tvtk.Property) ###################################################################### # User interface view traits_view = View(Group(Item(name = 'radius', style = 'simple'), Item(name = 'length', style = 'simple'), Item(name = 'x', label = 'Pos X'), Item(name = 'y', label = 'Pos Y'), Item(name = 'z', label = 'Pos Z'), Item(name = 'color'), Item(name = 'visibility'), Item(name = 'representation'), label = 'Cylinder Properties', show_border = True), buttons=['OK'], ) def __init__(self, **traits): self.property = self.actor.property HasTraits.__init__(self, **traits) self._create_points(self.radius, self.pos, self.length) self._color_changed(self.color) self._visibility_changed(self.visibility) self._axis_changed(numpy.array([1.0, 0.0, 0.0]), self.axis) self._x_changed(self.x) self._y_changed(self.y) self._z_changed(self.z) normals = tvtk.PolyDataNormals(input = self.polydata) m = tvtk.PolyDataMapper(input = normals.output) # the usual vtk pipleine countinuation self.actor.mapper = m self.property = self.actor.property self.property.representation = self.representation show_actor(self.actor) # passing the actors function for rendering self.viewer = get_viewer() # getting the ivtk viewer self.property.on_trait_change(self.viewer.scene.render) self.actor.on_trait_change(self.viewer.scene.render) ###################################################################### # Non-public methods, Event handlers def _create_points(self, r, c, h): cp = tvtk.CylinderSource(radius = r, height = h, resolution = 15) cp.update() ps = cp.output points = ps.points.to_array() l = len(points) for i in range(0, l, 1): points[i][1] = points[i][1] + h/2.0 points = axis_changed(numpy.array([0.0,1.0,0.0]),numpy.array([1.0,0.0,0.0]),numpy.array([0.0, 0.0, 0.0]), points) points = translate(numpy.array([0.0, 0.0, 0.0]), self.pos, points) self.points = points self.polydata.points = self.points self.polydata.polys = ps.polys return points, ps.polys def _radius_changed(self, old, new): self.points, polys = self._create_points(self.radius, self.pos, self.length) self.polydata.points = self.points self.polydata.modified() self.render() def _length_changed(self, value): self.points, polys = self._create_points(self.radius, self.pos, self.length) self.polydata.points = self.points self.polydata.modified() self.render() def _axis_changed(self, old, new): self.points = axis_changed(old, new, self.pos, self.points) self.polydata.points = self.points self.polydata.modified() self.render() def _pos_changed(self, old, new): self.set(x = new[0], trait_change_notify = False) self.set(y = new[1], trait_change_notify = False) self.set(z = new[2], trait_change_notify = False) self.points = translate(old, new, self.points) self.polydata.points = self.points self.polydata.modified() self.render() def _color_changed(self, value): self.actor.property.color = value def _x_changed(self, value): self.x = value self.pos = (self.x, self.pos[1],self.pos[2]) def _y_changed(self, value): self.y = value self.pos = (self.pos[0], self.y, self.pos[2]) def _z_changed(self, value): self.z = value self.pos = (self.pos[0], self.pos[1], self.z) def _representation_changed(self, value): self.property.representation = self.representation self.property.modified() self.render() def _visibility_changed(self, value): val = int(value) if (val == 1): self.actor.visibility = 1 else: self.actor.visibility = 0 ###################################################################### # Object's public methods def rotate(self, angle, axis, origin = numpy.array([0.0, 0.0, 0.0])): """Function takes atleast 2 arguments: axis about which to rotate the actor and angle with which to rotate the actor, the 3rd agrument is origin i.e. the point about which to rotate the actor, by default it is set to the global origin""" p, pi, ax = rotate(axis, angle, origin, self.pos, self.points, self.axis) self.set(pos = p, trait_change_notify = False) self.points = pi self.set(axis = ax, trait_change_notify = False) self.polydata.points = self.points self.polydata.modified() self.render() def render(self): v = self.viewer if v is not None: v.scene.render() class Box(HasTraits): """Box class creates Box from tvtk CubeSource, follows the usual VTK pipeline and creates a Cube actor, which is passed to the show_actor() function as an argument. """ ##################################################################### # Traits definitions size = Tuple((1.0, 1.0, 1.0), desc = 'the box size') pos = Array(value = (0.0, 0.0, 0.0), desc = 'the Box pos') color = vtk_color_trait((1.0, 1.0, 1.0)) x = Range(-1e299, 1e299, 0.0, desc = 'the X coordinate of box center') y = Range(-1e299, 1e299, 0.0, desc = 'the Y coordinate of box center') z = Range(-1e299, 1e299, 0.0, desc = 'the Z coordinate of box center') representation = Enum('s', 'w', 'p') length = Range(0, 1e299, 1.0, desc = 'the box length') height = Range(0, 1e299, 1.0, desc = 'the box height') width = Range(0, 1e299, 1.0, desc = 'the box width') axis = Array(value = (1.0, 0.0, 0.0), desc = 'the box axis') points = Array('d', shape = (24,3)) visibility = Bool(True) viewer = Any polydata = Instance(tvtk.PolyData, ()) actor = Instance(tvtk.Actor, ()) # tvtk Actor, for the usual pipeline architecture. property = Instance(tvtk.Property) ###################################################################### # User interface view traits_view = View(Group(Item(name = 'length'), Item(name = 'height'), Item(name = 'width'), Item(name = 'x', label = 'Pos X'), Item(name = 'y', label = 'Pos Y'), Item(name = 'z', label = 'Pos Z'), Item(name = 'color'), Item(name = 'visibility'), Item(name = 'representation'), label = 'Box Properties', show_border = True), buttons = ['OK'], ) def __init__(self, **traits): self.property = self.actor.property HasTraits.__init__(self, **traits) self._create_points(self.size, self.pos) self._color_changed(self.color) self._visibility_changed(self.visibility) self._axis_changed(numpy.array([1.0, 0.0, 0.0]), self.axis) self._x_changed(self.x) self._y_changed(self.y) self._z_changed(self.z) self._length_changed(self.length) self._height_changed(self.height) self._width_changed(self.width) m = tvtk.PolyDataMapper() # the usual vtk pipleine countinuation m.input = self.polydata self.actor.mapper = m self.property = self.actor.property self.property.representation = self.representation show_actor(self.actor) # passing the actors function for rendering self.viewer = get_viewer() # getting the ivtk viewer self.property.on_trait_change(self.viewer.scene.render) self.actor.on_trait_change(self.viewer.scene.render) ###################################################################### # Non-public methods, Event handlers def _create_points(self, s, c): cp = tvtk.CubeSource(x_length = s[0], y_length = s[1], z_length = s[2], center = tuple(c)) cp.update() ps = cp.output points = ps.points.to_array() self.points = points self.polydata.points = self.points self.polydata.polys = ps.polys return points, ps.polys def _size_changed(self, old, new): self.set(length = new[0], trait_change_notify = False) self.set(height = new[1], trait_change_notify = False) self.set(width = new[2], trait_change_notify = False) self.points, lines = self._create_points(self.size, self.pos) self.polydata.points = self.points self.polydata.modified() self.render() def _pos_changed(self, old, new): self.set(x = new[0], trait_change_notify = False) self.set(y = new[1], trait_change_notify = False) self.set(z = new[2], trait_change_notify = False) self.points = translate(old, new, self.points) #self.connectivity.points = self.points self.polydata.modified() self.render() def _axis_changed(self, old, new): self.points = axis_changed(old, new, self.pos, self.points) #self.connectivity.points = self.points self.polydata.modified() self.render() def _color_changed(self, value): self.actor.property.color = value def _x_changed(self, value): self.x = value self.pos = (self.x, self.pos[1],self.pos[2]) def _y_changed(self, value): self.y = value self.pos = (self.pos[0], self.y, self.pos[2]) def _z_changed(self, value): self.z = value self.pos = (self.pos[0], self.pos[1], self.z) def _representation_changed(self, value): self.property.representation = self.representation self.property.modified() self.render() def _length_changed(self, value): self.size = (self.length, self.size[1],self.size[2]) def _height_changed(self, value): self.size = (self.size[0], self.height, self.size[2]) def _width_changed(self, value): self.size = (self.size[0], self.size[1], self.width) def _visibility_changed(self, value): val = int(value) if (val == 1): self.actor.visibility = 1 else: self.actor.visibility = 0 ###################################################################### # Object's public methods def rotate(self, angle, axis, origin = numpy.array([0.0, 0.0, 0.0])): """Function takes atleast 2 arguments: axis about which to rotate the actor and angle with which to rotate the actor, the 3rd agrument is origin i.e. the point about which to rotate the actor, by default it is set to the global origin""" p, pi, ax = rotate(axis, angle, origin, self.pos, self.points, self.axis) self.set(pos = p, trait_change_notify = False) self.points = pi self.set(axis = ax, trait_change_notify = False) #self.connectivity.points = self.points self.polydata.modified() self.render() def render(self): v = self.viewer if v is not None: v.scene.render() class Arrow(HasTraits): """Arrow class creates Arrow from tvtk ArrowSource, follows the usual VTK pipeline and creates a Arrow actor, which is passed to the show_actor() function as an argument. """ ##################################################################### # Traits definitions points = Array('d', shape = (31 ,3)) radius_cone = Range(0.0, 10.0, value = 0.08, desc = 'the radius of cone portion of arrow') radius_shaft = Range(0.0, 5.0, value = 0.03, desc = 'the radius of shaft portion of arrow') length_cone = Range(0.0, 1.0, value = 0.35, desc = 'shaft length of arrow') axis = Array(value = (1.0, 0.0, 0.0), desc = 'the arrow axis') color = vtk_color_trait((1.0, 1.0, 1.0)) pos = Array(value = (0.0, 0.0, 0.0), desc = 'the Arrow pos') x = Range(-1e299, 1e299, 0.0, desc = 'the X coordinate of arrow center') y = Range(-1e299, 1e299, 0.0, desc = 'the Y coordinate of arrow center') z = Range(-1e299, 1e299, 0.0, desc = 'the Z coordinate of arrow center') representation = Enum('s', 'w', 'p') visibility = Bool(True) viewer = Any actor = Instance(tvtk.Actor, ()) # tvtk Actor, for the usual pipeline architecture. property = Instance(tvtk.Property) polydata = Instance(tvtk.PolyData, ()) ###################################################################### # User interface view traits_view = View(Group(Item(name = 'radius_cone'), Item(name = 'length_cone'), Item(name = 'radius_shaft'), Item(name = 'x', label = 'Pos X'), Item(name = 'y', label = 'Pos Y'), Item(name = 'z', label = 'Pos Z'), Item(name = 'color'), Item(name = 'visibility'), Item(name = 'representation'), label = 'Arrow Properties', show_border = True), buttons=['OK']) def __init__(self, **traits): self.property = self.actor.property HasTraits.__init__(self, **traits) self._create_points(self.radius_cone, self.length_cone, self.radius_shaft, self.pos) self._color_changed(self.color) self._visibility_changed(self.visibility) self._pos_changed(numpy.array([0.0, 0.0, 0.0]), self.pos) self._x_changed(self.x) self._y_changed(self.y) self._z_changed(self.z) self._axis_changed(numpy.array((1.0, 0.0, 0.0)), self.axis) normals = tvtk.PolyDataNormals(input = self.polydata) m = tvtk.PolyDataMapper(input = normals.output) # the usual vtk pipleine countinuation self.actor.mapper = m self.property = self.actor.property self.property.representation = self.representation show_actor(self.actor) # passing the actors function for rendering self.viewer = get_viewer() # getting the ivtk viewer self.property.on_trait_change(self.viewer.scene.render) self.actor.on_trait_change(self.viewer.scene.render) ###################################################################### # Non-public methods, Event handlers def _create_points(self, rc, lc, rs, ps): asrc = tvtk.ArrowSource(tip_radius = rc, tip_length = lc, shaft_radius = rs) asrc.update() ps = asrc.output points = ps.points.to_array() points = translate(numpy.array([0.0, 0.0, 0.0]), self.pos, points) self.points = points self.polydata.points = self.points self.polydata.polys = ps.polys return points, ps.polys def _radius_cone_changed(self, old, new): self.points, polys = self._create_points(self.radius_cone, self.length_cone, self.radius_shaft, self.pos) self.polydata.points = self.points self.polydata.modified() self.render() def _length_cone_changed(self, old, new): self.points, polys = self._create_points(self.radius_cone, self.length_cone, self.radius_shaft, self.pos) self.polydata.points = self.points self.polydata.modified() self.render() def _radius_shaft_changed(self, old, new): self.points, polys = self._create_points(self.radius_cone, self.length_cone, self.radius_shaft, self.pos) self.polydata.points = self.points self.polydata.modified() self.render() def _pos_changed(self, old, new): self.set(x = new[0], trait_change_notify = False) self.set(y = new[1], trait_change_notify = False) self.set(z = new[2], trait_change_notify = False) self.points = translate(old, new, self.points) self.polydata.points = self.points self.polydata.modified() self.render() def _axis_changed(self, old, new): self.points = axis_changed(old, new, self.pos, self.points) self.polydata.points = self.points self.polydata.modified() self.render() def _color_changed(self, value): self.actor.property.color = value def _x_changed(self, value): self.x = value self.pos = (self.x, self.pos[1],self.pos[2]) def _y_changed(self, value): self.y = value self.pos = (self.pos[0], self.y, self.pos[2]) def _z_changed(self, value): self.z = value self.pos = (self.pos[0], self.pos[1], self.z) def _representation_changed(self, value): self.property.representation = self.representation self.property.modified() self.render() def _visibility_changed(self, value): val = int(value) if (val == 1): self.actor.visibility = 1 else: self.actor.visibility = 0 ###################################################################### # Object's public methods def rotate(self, angle, axis, origin = numpy.array([0.0, 0.0, 0.0])): """Function takes atleast 2 arguments: axis about which to rotate the actor and angle with which to rotate the actor, the 3rd agrument is origin i.e. the point about which to rotate the actor, by default it is set to the global origin""" p, pi, ax = rotate(axis, angle, origin, self.pos, self.points, self.axis) self.set(pos = p, trait_change_notify = False) self.points = pi self.set(axis = ax, trait_change_notify = False) self.polydata.points = self.points self.polydata.modified() self.render() def render(self): v = self.viewer if v is not None: v.scene.render() class Helix(HasTraits): """Helix class creates Helix/Spring from tvtk polydata, follows the usual VTK pipeline and creates an Helix actor, which is passed to the show_actor() function as an argument. """ ##################################################################### # Traits definitions coils = Int(5) points = Array('d', shape = (None, 3)) radius = Range(0.01, 1e299, value = 0.2, desc = 'the helix radius') length = Range(0.01, 1e299, value = 1.0, desc = 'the helix length') pos = Array(value = (0.0, 0.0, 0.0), desc = 'the helix position') axis = Array(value = (1.0, 0.0, 0.0), desc = 'the helix axis') color = vtk_color_trait((1.0, 1.0, 1.0)) x = Range(-1e299, 1e299, 0.0, desc = 'the X coordinate of helix center') y = Range(-1e299, 1e299, 0.0, desc = 'the Y coordinate of helix center') z = Range(-1e299, 1e299, 0.0, desc = 'the Z coordinate of helix center') representation = Enum('s', 'w', 'p') thickness = Range(0, 1e299, value = 0.01, desc = 'the helix thickness') visibility = Bool(True) viewer = Any polydata = Instance(tvtk.PolyData, ()) property = Instance(tvtk.Property) tube = Instance(tvtk.TubeFilter, ()) actor = Instance(tvtk.Actor, ()) # tvtk Actor, for the usual pipeline architecture. ###################################################################### # User interface view traits_view = View(Group(Item(name = 'radius'), Item(name = 'length'), Item(name = 'thickness'), Item(name = 'x', label = 'Pos X'), Item(name = 'y', label = 'Pos Y'), Item(name = 'z', label = 'Pos Z'), Item(name = 'color'), Item(name = 'visibility'), Item(name = 'representation'), label = 'Helix Properties', show_border = True), buttons=['OK']) def __init__(self, **traits): self.property = self.actor.property HasTraits.__init__(self, **traits) self._create_points() self._pos_changed(numpy.array([0.0, 0.0, 0.0]), self.pos) self._x_changed(self.x) self._y_changed(self.y) self._z_changed(self.z) self._color_changed(self.color) self._visibility_changed(self.visibility) self._thickness_changed(self.thickness) self._axis_changed(numpy.array((1.0, 0.0, 0.0)), self.axis) normals = tvtk.PolyDataNormals(input = self.polydata) self.tube.input = normals.output self.tube.number_of_sides = 4 self.tube.capping = 1 m = tvtk.PolyDataMapper() m.input = self.tube.output self.actor.mapper = m self.property = self.actor.property self.property.representation = self.representation show_actor(self.actor) self.viewer = get_viewer() self.property.on_trait_change(self.viewer.scene.render) self.actor.on_trait_change(self.viewer.scene.render) self.tube.on_trait_change(self.viewer.scene.render) ###################################################################### # Non-public methods, Event handlers def _create_points(self): h = self.length/(self.coils*10) cons = self.length/(self.coils*10) j = 0 self.points.resize(self.coils*10, 3) for i in range(0,self.coils*360,36): theta = i*pi/180 self.points[j][0] = h self.points[j][1] = self.radius*sin(theta) self.points[j][2] = self.radius*cos(theta) j = j+1 h = h + cons np = len(self.points) - 1 lines = numpy.zeros((np, 2), 'l') lines[:,0] = numpy.arange(0, np-0.5, 1, 'l') lines[:,1] = numpy.arange(1, np+0.5, 1, 'l') self.polydata.points = self.points self.polydata.lines = lines def _color_changed(self, value): self.actor.property.color = value def _radius_changed(self, old, new): self._create_points() self._pos_changed(numpy.array([0.0, 0.0, 0.0]), self.pos) self.change_axis(numpy.array((1.0, 0.0, 0.0)), self.axis) self.polydata.points = self.points self.polydata.modified() self.render() def _coils_changed(self, old, new): p = numpy.arange(self.coils*10*3, dtype = float) p = p.reshape(self.coils*10,3) h = self.length/(self.coils*10) cons = self.length/(self.coils*10) j = 0 for i in range(0,self.coils*360,36): theta = i*pi/180 p[j][0] = h p[j][1] = self.radius*sin(theta) p[j][2] = self.radius*cos(theta) j = j+1 h = h + cons np = len(p) - 1 lines = numpy.zeros((np, 2), 'l') lines[:,0] = numpy.arange(0, np-0.5, 1, 'l') lines[:,1] = numpy.arange(1, np+0.5, 1, 'l') self.polydata.points = p self.polydata.lines = lines self.points = p self._pos_changed(numpy.array([0.0, 0.0, 0.0]), self.pos) self.change_axis(numpy.array((1.0, 0.0, 0.0)), self.axis) self.polydata.modified() self.render() def _x_changed(self, value): self.x = value self.pos = (self.x, self.pos[1],self.pos[2]) def _y_changed(self, value): self.y = value self.pos = (self.pos[0], self.y, self.pos[2]) def _z_changed(self, value): self.z = value self.pos = (self.pos[0], self.pos[1], self.z) def _representation_changed(self, value): self.property.representation = self.representation self.property.modified() self.render() def _pos_changed(self, old, new): self.set(x = new[0], trait_change_notify = False) self.set(y = new[1], trait_change_notify = False) self.set(z = new[2], trait_change_notify = False) self.points = translate(old, new, self.points) #self.polydata.points = self.points self.polydata.modified() self.render() def _axis_changed(self, old, new): self.points = axis_changed(old, new, self.pos, self.points) #self.polydata.points = self.points self.polydata.modified() self.render() def change_axis(self, old, new): self.points = axis_changed(old, new, self.pos, self.points) self.polydata.points = self.points def _length_changed(self, old, new): self._create_points() v = self.viewer if v: v.scene.disable_render = True self._pos_changed(numpy.array([0.0, 0.0, 0.0]), self.pos) self.change_axis(numpy.array((1.0, 0.0, 0.0)), self.axis) if v: v.scene.disable_render = False self.polydata.points = self.points self.polydata.modified() self.render() def _visibility_changed(self, value): val = int(value) if (val == 1): self.actor.visibility = 1 else: self.actor.visibility = 0 def _thickness_changed(self, value): self.tube.radius = value ###################################################################### # Object's public methods def rotate(self, angle, axis, origin = numpy.array([0.0, 0.0, 0.0])): """Function takes atleast 2 arguments: axis about which to rotate the actor and angle with which to rotate the actor, the 3rd agrument is origin i.e. the point about which to rotate the actor, by default it is set to the global origin""" p, pi, ax = rotate(axis, angle, origin, self.pos, self.points, self.axis) self.set(pos = p, trait_change_notify = False) self.points = pi self.set(axis = ax, trait_change_notify = False) self.polydata.points = self.points self.polydata.modified() self.render() def render(self): v = self.viewer if v is not None: v.scene.render() class Ellipsoid(HasTraits): """Ellipsoid class creates Ellipsoid from tvtk SphereSource by suitably scaling it, follows the usual VTK pipeline and creates a Ellipsoid actor, which is passed to the show_actor() function as an argument. """ ##################################################################### # Traits definitions radius = Range(0.0, 1e299, value = 0.5, desc = 'Undistorted ellipsoid radius') pos = Array(value = (0.0, 0.0, 0.0), desc = 'Ellipsoid pos') color = vtk_color_trait((1.0, 1.0, 1.0)) axis = Array(value = (1.0, 0.0, 0.0), desc= 'Ellipsoid axis') size = Array(value = (1.0, 0.5, 1.0), desc= 'Ellipsoid size_factor') length = Range(0.0, 1e299, 1.0, desc = 'Scaling factor in X direction') height = Range(0.0, 1e299, 0.5, desc = 'Sscaling factor in Y direction') width = Range(0.0, 1e299, 1.0, desc = 'Scaling factor in Z direction') x = Range(-1e299, 1e299, 0.0, desc = 'X coordinate of ellipsoid center') y = Range(-1e299, 1e299, 0.0, desc = 'Y coordinate of ellipsoid center') z = Range(-1e299, 1e299, 0.0, desc = 'Z coordinate of ellipsoid center') representation = Enum('s', 'w', 'p') visibility = Bool(True) viewer = Any polydata = Instance(tvtk.PolyData, ()) property = Instance(tvtk.Property) actor = Instance(tvtk.Actor, ()) # tvtk Actor, for the usual pipeline architecture. ###################################################################### # User interface view traits_view = View(Group(Item(name = 'radius', style = 'simple'), Item(name = 'x', label = 'Pos X'), Item(name = 'y', label = 'Pos Y'), Item(name = 'z', label = 'Pos Z'), Item(name = 'length', label = 'Length'), Item(name = 'height', label = 'Height'), Item(name = 'width', label = 'Width'), Item(name = 'color'), Item(name = 'visibility'), Item(name = 'representation'), label = 'Ellipsoid Properties', show_border = True), buttons=['OK']) def __init__(self, **traits): self.property = self.actor.property HasTraits.__init__(self, **traits) self._create_points(self.radius, self.pos) self._color_changed(self.color) self._visibility_changed(self.visibility) self._x_changed(self.x) self._y_changed(self.y) self._z_changed(self.z) self._axis_changed(numpy.array((1.0, 0.0, 0.0)), self.axis) normals = tvtk.PolyDataNormals(input = self.polydata) m = tvtk.PolyDataMapper(input = normals.output) # the usual vtk pipleine countinuation self.actor.mapper = m self.property = self.actor.property self.property.representation = self.representation show_actor(self.actor) # passing the actors function for rendering self.viewer = get_viewer() # getting the ivtk viewer self.property.on_trait_change(self.viewer.scene.render) self.actor.on_trait_change(self.viewer.scene.render) ###################################################################### # Non-public methods, Event handlers def _create_points(self, r, c): sp = tvtk.SphereSource(radius = r, center = tuple(c), phi_resolution = 20, theta_resolution = 20) sp.update() ps = sp.output points = ps.points.to_array() points = scale(self.size, points, self.pos) self.points = points self.polydata.points = self.points self.polydata.polys = ps.polys return points, ps.polys def _radius_changed(self, value): points, polys = self._create_points(self.radius, self.pos) self.polydata.points = points self.polydata.modified() self.render() def _size_changed(self, value): points, polys = self._create_points(self.radius, self.pos) self.polydata.points = points self.polydata.modified() self.render() def _color_changed(self, value): self.actor.property.color = value def _pos_changed(self, old, new): self.set(x = new[0], trait_change_notify = False) self.set(y = new[1], trait_change_notify = False) self.set(z = new[2], trait_change_notify = False) points, lines = self._create_points(self.radius, self.pos) self.polydata.points = points self.polydata.modified() self.render() def _axis_changed(self, old, new): points = axis_changed(old, new, self.pos, self.points) self.points = points self.polydata.points = self.points self.polydata.modified() self.render() def _x_changed(self, value): self.x = value self.pos = (self.x, self.pos[1],self.pos[2]) def _y_changed(self, value): self.y = value self.pos = (self.pos[0], self.y, self.pos[2]) def _z_changed(self, value): self.z = value self.pos = (self.pos[0], self.pos[1], self.z) def _representation_changed(self, value): self.property.representation = self.representation self.property.modified() self.render() def _length_changed(self, value): self.x_scale = value self.size = (self.length, self.size[1], self.size[2]) def _height_changed(self, value): self.y_scale = value self.size = (self.size[0], self.height, self.size[2]) def _width_changed(self, value): self.z_scale = value self.size = (self.size[0], self.size[1], self.width) def _visibility_changed(self, value): val = int(value) if (val == 1): self.actor.visibility = 1 else: self.actor.visibility = 0 ###################################################################### # Object's public methods def render(self): v = self.viewer if v is not None: v.scene.render() def rotate(self, angle, axis, origin = numpy.array([0.0, 0.0, 0.0])): """Function takes atleast 2 arguments: axis about which to rotate the actor and angle with which to rotate the actor, the 3rd agrument is origin i.e. the point about which to rotate the actor, by default it is set to the global origin""" p, pi, ax = rotate(axis, angle, origin, self.pos, self.points, self.axis) self.set(pos = p, trait_change_notify = False) self.points = pi self.set(axis = ax, trait_change_notify = False) self.polydata.points = self.points self.polydata.modified() self.render() ########################################################### ################### Compatibility layer ################### ########################################################### sphere = Sphere vector = MVector frame = Frame curve = Curve ring = Ring cone = Cone cylinder = Cylinder box = Box arrow = Arrow helix = Helix ellipsoid = Ellipsoid mag = numpy.linalg.norm def rate(arg): msg = """Do not use rate, instead use the iterate() function. Iterate should be called with a callback. This callback will be periodically called via a timer. This will make your script's UI completely interactive. """ print '*'*80 print msg print '*'*80 ############################################################ #################### Test Functions ####################### ############################################################ def test_sphere(): s1 = sphere() s2 = sphere(radius = 1.5, pos = (2, 0, 0), color = (1, 0, 0)) s2.edit_traits() def test_box(): b1 = box() b2 = box(center = (2, 0, 0), size = (2, 1, 1), color = (0.5, 0.5, 1.0)) b2.edit_traits() def test_cone(): c = cone(pos = (5,0,0), color = (1,1,0), axis = (1,1,0)) c.edit_traits() def test_cylinder(): c1 = cylinder() c2 = cylinder(radius = 1.5, pos = (2, 0, 0), color = (1, 0, 0)) c2.edit_traits() def test_arrow(): a = arrow() a.edit_traits() def test_curve(): c = curve(points = ([[0,0,0],[1,0,0],[0,1,0],[0,0,1]])) c.edit_traits() return c def test_ring(): r1 = ring() r2 = ring(radius = 1.5, pos = (2,0,0), color = (1,0,0)) r2.edit_traits() def test_helix(): h1 = helix() r2 = helix(radius = 0.5, pos = (2,0,0), color = (1,0,0)) r2.edit_traits() def test_ellipsoid(): e1 = ellipsoid() e1.edit_traits() def test_remove_actors(): """Test fuction for testing integrity of remove function for actors""" s = sphere(radius = 1.5, pos = (0, 0, 0), color = (1, 0, 0)) b = box(center = (1, 0, 0), size = (2, 1, 1), color = (0.5, 0.5, 1.0)) co = cone(resolution = 100, pos = (2, 0, 0), color = (0, 1, 0)) cy = cylinder(resolution = 100, pos = (3, 0, 0), color = (0, 0, 1)) time.sleep(3) print "Removed sphere from scene" remove_actor(s) time.sleep(3) print "Removed cone from cone" remove_actor(co) def test_frame(): c1 = cone(pos = (3.0, 0.0, 0.0)) r1 = ring() f = frame(r1,c1) f.edit_traits() return f def rotate_frame(): """Test fuction for testing integrity of rotation function of frame""" r1 = ring(pos = (1,0,0)) h1 = helix(pos = (1,0,0)) f = frame(r1,h1) f.pos = (2,0,0) f.axis = (1,1,1) j = 1 def anim(): f.rotate(j, [0.0, 1.0, 0.0]) ti = iterate(200, anim) ti.edit_traits() return ti def test_rotate(): """Test fuction for testing integrity of rotation function of actors""" r = ring() r.pos = (3,0,0) r2 = ring(pos = (3,0,0)) points1 = r2.points r.rotate(90, [0,1,0], [1,0,0]) r.rotate(-90, [0,1,0], [1,0,0]) points2 = r.points if (numpy.allclose(points1, points2)): print "All clear" else: print "Test failed" return r def test_translate(): """This is a basic examples function demonstrating the creating simple animation from visual actors. Note the show function has to be called in stand alone programs.""" b = box() xlen = 10 s = sphere(pos = (xlen, 0, 0)) s.velocity = (-1,0,0) def anim(): x = s.x if (x < 1): s.velocity = (1, 0, 0) elif (x > xlen): s.velocity = (-1, 0, 0) s.x = x + s.velocity[0] ti = iterate(50, anim) ti.edit_traits() def bounce(): """This is a basic example function, extending the previous example.""" xlen = 10 b1 = box(size = (1, 4, 4), color = (0,1,0)) b2 = box(size = (1, 4, 4), color = (0,1,0), pos = (xlen, 0, 0)) s = sphere(radius = 0.5, pos = (xlen, 0, 0), color = (1, 0, 0)) s.velocity = (-1, 0, 0) def anim(): x = s.x if (x == 1): s.velocity = (1, 0, 0) elif (x == (xlen-1)): s.velocity = (-1, 0, 0) s.x = x + s.velocity[0] t = iterate(60, anim) t.edit_traits() return t mayavi-4.1.0/tvtk/tools/tvtk_doc.py0000644000175100001440000003230411674464502020315 0ustar ischnellusers00000000000000""" Utility code that provides classes helpful in choosing a suitable TVTK class. It does this by providing a list of all the classes along with the option to be able to search for the documentation. The nice thing about the UI is that it performs some kind of completion on names typed by the user, plus it allows users to search through the TVTK class docs very easily. Once a search string is typed the completion and available lists are modified so you can do completion of the searched class names. If a unique enough string is typed the class docs are shown. """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. # Standard library imports. import vtk import types import inspect # Enthought library imports. from traits.api import HasTraits, Property, List, Str, \ Instance, Button, Int from traitsui.api import View, Group, Item, EnumEditor,\ ListEditor, TextEditor from tvtk.api import tvtk from tvtk.common import get_tvtk_name ################################################################################ # Utility functions. ################################################################################ def get_tvtk_class_names(): """Returns 4 lists: 1. A list of all the TVTK class names that are not abstract. 2. A list of the TVTK sources (have only outputs and no inputs) 3. A list of the TVTK filters (both inputs and outputs) 4. A list of the TVTK sinks (only inputs and no outputs) """ # Shut of VTK warnings for the time being. o = vtk.vtkObject w = o.GetGlobalWarningDisplay() o.SetGlobalWarningDisplay(0) # Turn it off. all = [] src = [] filter = [] sink = [] for name in dir(vtk): if name.startswith('vtk'): klass = getattr(vtk, name) try: c = klass() except TypeError: continue tvtk_name = get_tvtk_name(name) all.append(tvtk_name) has_input = has_output = False if hasattr(klass, 'GetNumberOfInputPorts'): if c.GetNumberOfInputPorts() > 0: has_input = True if hasattr(klass, 'GetNumberOfOutputPorts'): if c.GetNumberOfOutputPorts() > 0: has_output = True if has_input: if has_output: filter.append(tvtk_name) else: sink.append(tvtk_name) elif has_output: src.append(tvtk_name) o.SetGlobalWarningDisplay(w) result = (all, src, filter, sink) for x in result: x.sort() return result def get_func_doc(func, fname): """Returns function documentation.""" if inspect.isfunction(func): func_obj = func elif inspect.ismethod(func): func_obj = func.im_func else: return '' args, vargs, vkw = inspect.getargs(func_obj.func_code) defaults = func_obj.func_defaults doc = fname + inspect.formatargspec(args, vargs, vkw, defaults) d = inspect.getdoc(func) if d is not None: doc += '\n\n' + d + '\n\n' return doc def get_tvtk_class_doc(obj): """Return's the objects documentation.""" doc = obj.__doc__ + '\nTraits:\n-------------------\n\n' ignore = ['trait_added', 'trait_modified'] for key, trait in obj.traits().iteritems(): if key.startswith('_') or key.endswith('_') or key in ignore: continue doc += '\n%s: %s'%(key, trait.help) doc += '\nMethods:\n----------------------\n\n' traits = obj.trait_names() for name in dir(obj): if name in traits or name.startswith('_'): continue if name.find('trait') > -1 and name != 'update_traits': continue func = getattr(obj, name) if callable(func): doc += '\n' + get_func_doc(func, name) return doc # GLOBALS TVTK_CLASSES, TVTK_SOURCES, TVTK_FILTERS, TVTK_SINKS = get_tvtk_class_names() ################################################################################ # `DocSearch` class. ################################################################################ class DocSearch(object): """A simple class that provides a method to search through class documentation. This code is taken from mayavi-1.x's ivtk.VtkHelp """ # These are class attributes to prevent regenerating them everytime # this class is instantiated. VTK_CLASSES = [] VTK_CLASS_DOC = [] def __init__(self): self.vtk_classes = self.VTK_CLASSES self.vtk_c_doc = self.VTK_CLASS_DOC if len(self.VTK_CLASSES) == 0: self._setup_data() def _setup_data(self): self.vtk_classes = [x for x in dir(vtk) if x.startswith('vtk')] n = len(self.vtk_classes) # Store the class docs in the list given below. self.vtk_c_doc = ['']*n # setup the data. for i in range(n): c = self.vtk_classes[i] try: doc = getattr(vtk, c).__doc__.lower() self.vtk_c_doc[i] = doc except AttributeError: pass def search(self, word): """ Search for word in class documentation and return matching classes. This is also case insensitive. The searching supports the 'and' and 'or' keywords that allow for fairly complex searches. A space between words assumes that the two words appear one after the other. Parameters ---------- word -- name to search for. """ assert type(word) in types.StringTypes, \ "Sorry, passed argument, %s is not a string."%word if len(word.strip()) == 0: return [] lword = word.lower().strip() tmp_list = lword.split() wlist = [] prev = "" for w in tmp_list: z = w.strip() if z in ('and', 'or'): if prev and prev not in ('and', 'or'): wlist.append(prev) wlist.append(z) prev = z else: if prev and prev not in ('and', 'or'): prev = prev + ' ' + z else: prev = z if prev in ('and', 'or'): del wlist[-1] elif prev: wlist.append(prev) ret = [] i = 0 vtk_classes = self.vtk_classes vtk_c_doc = self.vtk_c_doc N = len(vtk_classes) while i < N: stored_test = 0 do_test = '' for w in wlist: if w == 'and': do_test = 'and' elif w == 'or': do_test = 'or' else: test = (vtk_c_doc[i].find(w) > -1) if do_test == 'and': stored_test = stored_test and test elif do_test == 'or': stored_test = stored_test or test elif do_test == '': stored_test = test if stored_test: ret.append(vtk_classes[i]) i = i + 1 return [get_tvtk_name(x) for x in ret] _search_help_doc = """ Help on Searching --------------------------------------- To search for a particular TVTK class, type in the 'class_name' text entry widget. The class names are all case sensitive. You may also select the class from the list of available class names at the top. As you type you will see completion options in the completions list, the instant a complete match is found the class documentation will be show in the bottom. You can also search the TVTK class documentation for strings (case insensitive). The search option supports the 'and' and 'or' keywords to do advanced searches. Press / to perform the search. The top 25 hits will show up in the completions, to view a particular hit either select the choice from the available ones or type in the name in the 'class_name' entry box. To clear the search string click the 'Clear search' button or erase the search string manually. """ ################################################################################ # `TVTKClassChooser` class. ################################################################################ class TVTKClassChooser(HasTraits): # The selected object, is None if no valid class_name was made. object = Property # The TVTK class name to choose. class_name = Str('', desc='class name of TVTK class (case sensitive)') # The string to search for in the class docs -- the search supports # 'and' and 'or' keywords. search = Str('', desc='string to search in TVTK class documentation '\ 'supports the "and" and "or" keywords. '\ 'press to start search. '\ 'This is case insensitive.') clear_search = Button # The class documentation. doc = Str(_search_help_doc) # Completions for the choice of class. completions = List(Str) # List of available class names as strings. available = List(TVTK_CLASSES) ######################################## # Private traits. finder = Instance(DocSearch) n_completion = Int(25) ######################################## # View related traits. view = View(Group(Item(name='class_name', editor=EnumEditor(name='available')), Item(name='class_name', has_focus=True ), Item(name='search', editor=TextEditor(enter_set=True, auto_set=False) ), Item(name='clear_search', show_label=False), Item('_'), Item(name='completions', editor=ListEditor(columns=3), style='readonly' ), Item(name='doc', resizable=True, label='Documentation', style='custom') ), id='tvtk_doc', resizable=True, width=800, height=600, title='TVTK class chooser', buttons = ["OK", "Cancel"] ) ###################################################################### # `object` interface. ###################################################################### def __init__(self, **traits): super(TVTKClassChooser, self).__init__(**traits) self._orig_available = list(self.available) ###################################################################### # Non-public interface. ###################################################################### def _get_object(self): o = None if len(self.class_name) > 0: try: o = getattr(tvtk, self.class_name)() except (AttributeError, TypeError): pass return o def _class_name_changed(self, value): av = self.available comp = [x for x in av if x.startswith(value)] self.completions = comp[:self.n_completion] if len(comp) == 1 and value != comp[0]: self.class_name = comp[0] o = self.object if o is not None: self.doc = get_tvtk_class_doc(o) else: self.doc = _search_help_doc def _finder_default(self): return DocSearch() def _clear_search_fired(self): self.search = '' def _search_changed(self, value): if len(value) < 3: self.available = self._orig_available return f = self.finder result = f.search(value) if len(result) == 0: self.available = self._orig_available elif len(result) == 1: self.class_name = result[0] else: self.available = result self.completions = result[:self.n_completion] ################################################################################ # `TVTKSourceChooser` class. ################################################################################ class TVTKSourceChooser(TVTKClassChooser): available = List(TVTK_SOURCES) ################################################################################ # `TVTKFilterChooser` class. ################################################################################ class TVTKFilterChooser(TVTKClassChooser): available = List(TVTK_FILTERS) ################################################################################ # `TVTKSinkChooser` class. ################################################################################ class TVTKSinkChooser(TVTKClassChooser): available = List(TVTK_SINKS) def main(): """Pops up a class chooser which doubles as a nice help search documentation tool. """ s = TVTKClassChooser() s.configure_traits() if __name__ == '__main__': main() mayavi-4.1.0/tvtk/tools/__init__.py0000644000175100001440000000013211674464502020231 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/tvtk/tools/mlab.py0000644000175100001440000013335211674464502017420 0ustar ischnellusers00000000000000"""A module that provides Matlab like 3d visualization functionality. The general idea is shamelessly stolen from the `high-level API`_ provided by Octaviz_. Some of the test cases and demos are also translated from there! .. _Octaviz: http://octaviz.sourceforge.net/ .. _high-level API: http://octaviz.sourceforge.net/index.php?page=manpagesq The implementation provided here is object oriented and each visualization capability is implemented as a class that has traits. So each of these may be configured. Each visualization class derives (ultimately) from MLabBase which is responsible for adding/removing its actors into the render window. The classes all require that the RenderWindow be a `pyface.tvtk.scene.Scene` instance (this constraint can be relaxed if necessary later on). This module offers the following broad class of functionality: `Figure` This basically manages all of the objects rendered. Just like figure in any Matlab like environment. A convenience function called `figure` may be used to create a nice Figure instance. `Glyphs` This and its subclasses let one place glyphs at points specified as inputs. The subclasses are: `Arrows`, `Cones`, `Cubes`, `Cylinders`, `Spheres`, and `Points`. `Line3` Draws lines between the points specified at initialization time. `Outline` Draws an outline for the contained objects. `Title` Draws a title for the entire figure. `LUTBase` Manages a lookup table and a scalar bar (legend) for it. This is subclassed by all classes that need a LUT. `SurfRegular` MayaVi1's imv.surf like functionality that plots surfaces given x (1D), y(1D) and z (or a callable) arrays. `SurfRegularC` Also plots contour lines. `TriMesh` Given triangle connectivity and points, plots a mesh of them. `FancyTriMesh` Plots the mesh using tubes and spheres so its fancier. `Mesh` Given x, y generated from numpy.mgrid, and a z to go with it. Along with optional scalars. This class builds the triangle connectivity (assuming that x, y are from numpy.mgrid) and builds a mesh and shows it. `FancyMesh` Like mesh but shows the mesh using tubes and spheres. `Surf` This generates a surface mesh just like Mesh but renders the mesh as a surface. `Contour3` Shows contour for a mesh. `ImShow` Allows one to view large numeric arrays as image data using an image actor. This is just like MayaVi1's `mayavi.tools.imv.viewi`. To see nice examples of all of these look at the `test_*` functions at the end of this file. Here is a quick example that uses these test functions:: >>> from tvtk.tools import mlab >>> f = mlab.figure() >>> mlab.test_surf(f) # Create a spherical harmonic. >>> f.pop() # Remove it. >>> mlab.test_molecule(f) # Show a caffeine molecule. >>> f.renwin.reset_zoom() # Scale the view. >>> f.pop() # Remove this. >>> mlab.test_lines(f) # Show pretty lines. >>> f.clear() # Remove all the stuff on screen. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. import numpy from traits.api import HasTraits, List, Instance, Any, Float, Bool, \ Str, Trait, Int from pyface.api import GUI from tvtk.api import tvtk from tvtk.tvtk_base import TVTKBase, vtk_color_trait from tvtk.tools import ivtk # Set this to False to not use LOD Actors. USE_LOD_ACTOR = True VTK_VER = float(tvtk.Version().vtk_version[:3]) ###################################################################### # Utility functions. ###################################################################### def _make_actor(**kwargs): """Return a TVTK actor. If `mlab.USE_LOD_ACTOR` is `True` it returns an LODActor if not it returns a normal actor. """ if USE_LOD_ACTOR: r = tvtk.LODActor(number_of_cloud_points=1500) r.property.point_size = 2.0 r.set(**kwargs) return r else: return tvtk.Actor(**kwargs) def _create_structured_points_direct(x, y, z=None): """Creates a StructuredPoints object given input data in the form of numpy arrays. Input Arguments: x -- Array of x-coordinates. These should be regularly spaced. y -- Array of y-coordinates. These should be regularly spaced. z -- Array of z values for the x, y values given. The values should be computed such that the z values are computed as x varies fastest and y next. If z is None then no scalars are associated with the structured points. Only the structured points data set is created. """ nx = len(x) ny = len(y) if z is not None: nz = numpy.size(z) assert nx*ny == nz, "len(x)*len(y) != len(z)"\ "You passed nx=%d, ny=%d, nz=%d"%(nx, ny, nz) xmin, ymin = x[0], y[0] dx, dy= (x[1] - x[0]), (y[1] - y[0]) sp = tvtk.StructuredPoints(dimensions=(nx,ny,1), origin=(xmin, ymin, 0), spacing=(dx, dy, 1)) if z is not None: sp.point_data.scalars = numpy.ravel(z) sp.point_data.scalars.name = 'scalars' return sp def sampler(xa, ya, func, *args, **kwargs): """Samples a function at an array of ordered points (with equal spacing) and returns an array of scalars as per VTK's requirements for a structured points data set, i.e. x varying fastest and y varying next. Input Arguments: xa -- Array of x points. ya -- Array if y points. func -- function of x, and y to sample. args -- additional positional arguments for func() (default is empty) kwargs -- a dict of additional keyword arguments for func() (default is empty) """ ret = func(xa[:,None] + numpy.zeros_like(ya), numpy.transpose(ya[:,None] + numpy.zeros_like(xa)), *args, **kwargs ) return numpy.transpose(ret) def _check_sanity(x, y, z): """Checks the given arrays to see if they are suitable for surf.""" msg = "Only ravelled or 2D arrays can be viewed! "\ "This array has shape %s" % str(z.shape) assert len(z.shape) <= 2, msg if len( z.shape ) == 2: msg = "len(x)*len(y) != len(z.flat). You passed "\ "nx=%d, ny=%d, shape of z=%s"%(len(x), len(y), z.shape) assert z.shape[0]*z.shape[1] == len(x)*len(y), msg msg = "length of y(%d) and x(%d) must match shape of z "\ "%s. (Maybe you need to swap x and y?)"%(len(y), len(x), str(z.shape)) assert z.shape == (len(y), len(x)), msg def squeeze(a): "Returns a with any ones from the shape of a removed" a = numpy.asarray(a) b = numpy.asarray(a.shape) val = numpy.reshape(a, tuple(numpy.compress(numpy.not_equal(b, 1), b))) return val def make_surf_actor(x, y, z, warp=1, scale=[1.0, 1.0, 1.0], make_actor=True, *args, **kwargs): """Creates a surface given regularly spaced values of x, y and the corresponding z as arrays. Also works if z is a function. Currently works only for regular data - can be enhanced later. Parameters ---------- x -- Array of x points (regularly spaced) y -- Array if y points (regularly spaced) z -- A 2D array for the x and y points with x varying fastest and y next. Also will work if z is a callable which supports x and y arrays as the arguments. warp -- If true, warp the data to show a 3D surface (default = 1). scale -- Scale the x, y and z axis as per passed values. Defaults to [1.0, 1.0, 1.0]. make_actor -- also create actors suitably (default True) args -- additional positional arguments for func() (default is empty) kwargs -- a dict of additional keyword arguments for func() (default is empty) """ if callable(z): zval = numpy.ravel(sampler(x, y, z, *args, **kwargs)) x, y = squeeze(x), squeeze(y) else: x, y = squeeze(x), squeeze(y) _check_sanity(x, y, z) zval = numpy.ravel(z) assert len(zval) > 0, "z is empty - nothing to plot!" xs = x*scale[0] ys = y*scale[1] data = _create_structured_points_direct(xs, ys, zval) if not make_actor: return data if warp: geom_f = tvtk.ImageDataGeometryFilter(input=data) warper = tvtk.WarpScalar(input=geom_f.output, scale_factor=scale[2]) normals = tvtk.PolyDataNormals(input=warper.output, feature_angle=45) mapper = tvtk.PolyDataMapper(input=normals.output, scalar_range=(min(zval),max(zval))) else: mapper = tvtk.PolyDataMapper(input=data, scalar_range=(min(zval),max(zval))) actor = _make_actor(mapper=mapper) return data, actor def make_triangle_polydata(triangles, points, scalars=None): t = numpy.asarray(triangles, 'l') assert t.shape[1] == 3, "The list of polygons must be Nx3." if scalars is not None: assert len(points) == len(numpy.ravel(scalars)) pd = tvtk.PolyData(points=points, polys=t) if scalars is not None: pd.point_data.scalars = numpy.ravel(scalars) pd.point_data.scalars.name = 'scalars' return pd def make_triangles_points(x, y, z, scalars=None): """Given x, y, and z co-ordinates made using numpy.mgrid and optional scalars. This function returns triangles and points corresponding to a mesh formed by them. Parameters ---------- - x : array A list of x coordinate values formed using numpy.mgrid. - y : array A list of y coordinate values formed using numpy.mgrid. - z : array A list of z coordinate values formed using numpy.mgrid. - scalars : array (optional) Scalars to associate with the points. """ assert len(x.shape) == 2, "Array x must be 2 dimensional." assert len(y.shape) == 2, "Array y must be 2 dimensional." assert len(z.shape) == 2, "Array z must be 2 dimensional." assert x.shape == y.shape, "Arrays x and y must have same shape." assert y.shape == z.shape, "Arrays y and z must have same shape." nx, ny = x.shape i, j = numpy.mgrid[0:nx-1,0:ny-1] i, j = numpy.ravel(i), numpy.ravel(j) t1 = i*ny+j, (i+1)*ny+j, (i+1)*ny+(j+1) t2 = (i+1)*ny+(j+1), i*ny+(j+1), i*ny+j nt = len(t1[0]) triangles = numpy.zeros((nt*2, 3), 'l') triangles[0:nt,0], triangles[0:nt,1], triangles[0:nt,2] = t1 triangles[nt:,0], triangles[nt:,1], triangles[nt:,2] = t2 points = numpy.zeros((nx, ny, 3), 'd') points[:,:,0], points[:,:,1], points[:,:,2] = x, y, z points = numpy.reshape(points, (nx*ny, 3)) return triangles, points ###################################################################### # `MLabBase` class. ###################################################################### class MLabBase(HasTraits): # List of actors. actors = List(TVTKBase) # Renderwindow to render into. renwin = Any def update(self): self.renwin.render() def render(self): if self.renwin: self.renwin.render() def _renwin_changed(self, old, new): if old: old.remove_actors(self.actors) old.render() if new: new.add_actors(self.actors) new.render() def _actors_changed(self, old, new): self._handle_actors(old, new) def _actors_items_changed(self, list_event): self._handle_actors(list_event.removed, list_event.added) def _handle_actors(self, removed, added): rw = self.renwin if rw: rw.remove_actors(removed) rw.add_actors(added) rw.render() ###################################################################### # `Glyphs` class. ###################################################################### class Glyphs(MLabBase): # The source glyph which is placed at various locations. glyph_source = Any # A Glyph3D instance replicates the glyph_sources at various # points. glyph = Instance(tvtk.Glyph3D, (), {'vector_mode':'use_vector', 'scale_mode':'data_scaling_off'}) # Color of the glyphs. color = vtk_color_trait((1.0, 1.0, 1.0)) def __init__(self, points, vectors=None, scalars=None, **traits): super(Glyphs, self).__init__(**traits) if vectors is not None: assert len(points) == len(vectors) if scalars is not None: assert len(points) == len(scalars) self.points = points self.vectors = vectors self.scalars = scalars polys = numpy.arange(0, len(points), 1, 'l') polys = numpy.reshape(polys, (len(points), 1)) pd = tvtk.PolyData(points=points, polys=polys) if self.vectors is not None: pd.point_data.vectors = vectors pd.point_data.vectors.name = 'vectors' if self.scalars is not None: pd.point_data.scalars = scalars pd.point_data.scalars.name = 'scalars' self.poly_data = pd self.glyph.input = pd if self.glyph_source: self.glyph.source = self.glyph_source.output mapper = tvtk.PolyDataMapper(input=self.glyph.output) actor = _make_actor(mapper=mapper) actor.property.color = self.color self.actors.append(actor) def update(self): self.poly_data.update() self.renwin.render() def _color_changed(self, val): if self.actors: self.actors[0].property.color = val self.render() def _glyph_source_changed(self, val): self.glyph.source = val.output self.render() ###################################################################### # `Arrows` class. ###################################################################### class Arrows(Glyphs): # The arrow glyph which is placed at various locations. glyph_source = Instance(tvtk.ArrowSource, ()) ###################################################################### # `Cones` class. ###################################################################### class Cones(Glyphs): # The cone glyph which is placed at various locations. glyph_source = Instance(tvtk.ConeSource, ()) # Radius of the cone. radius = Float(0.05, desc='radius of the cone') def __init__(self, points, vectors=None, scalars=None, **traits): super(Cones, self).__init__(points, vectors, scalars, **traits) self._radius_changed(self.radius) def _radius_changed(self, val): self.glyph_source.radius = val self.render() ###################################################################### # `Cubes` class. ###################################################################### class Cubes(Glyphs): # The cube glyph which is placed at various locations. glyph_source = Instance(tvtk.CubeSource, ()) # The side length of the cube. length = Float(0.05, desc='side length of the cube') def __init__(self, points, vectors=None, scalars=None, **traits): super(Cubes, self).__init__(points, vectors, scalars, **traits) self._radius_changed(self.radius) def _length_changed(self, val): self.glyph_source.x_length = val self.glyph_source.y_length = val self.glyph_source.z_length = val self.render() ###################################################################### # `Cylinders` class. ###################################################################### class Cylinders(Glyphs): # The cylinder glyph which is placed at various locations. glyph_source = Instance(tvtk.CylinderSource, ()) ###################################################################### # `Spheres` class. ###################################################################### class Spheres(Glyphs): # The sphere which is placed at various locations. glyph_source = Instance(tvtk.SphereSource, (), {'phi_resolution':15, 'theta_resolution':30}) # Radius of the sphere. radius = Float(0.05, desc='radius of the sphere') def __init__(self, points, vectors=None, scalars=None, **traits): super(Spheres, self).__init__(points, vectors, scalars, **traits) self._radius_changed(self.radius) def _radius_changed(self, val): self.glyph_source.radius = val self.render() ###################################################################### # `Points` class. ###################################################################### class Points(Glyphs): # The point which is placed at various locations. glyph_source = Instance(tvtk.PointSource, (), {'radius':0, 'number_of_points':1}) ###################################################################### # `Line3` class. ###################################################################### class Line3(MLabBase): # Radius of the tube filter. radius = Float(0.01, desc='radius of the tubes') # Should a tube filter be used or not. use_tubes = Bool(True, desc='specifies if the tube filter should be used') # The Tube filter used to generate tubes from the lines. tube_filter = Instance(tvtk.TubeFilter, (), {'number_of_sides':6}) # Color of the actor. color = vtk_color_trait((1.0, 1.0, 1.0)) def __init__(self, points, **traits): super(MLabBase, self).__init__(**traits) assert len(points[0]) == 3, "The points must be 3D" self.points = points np = len(points) - 1 lines = numpy.zeros((np, 2), 'l') lines[:,0] = numpy.arange(0, np-0.5, 1, 'l') lines[:,1] = numpy.arange(1, np+0.5, 1, 'l') pd = tvtk.PolyData(points=points, lines=lines) self.poly_data = pd mapper = tvtk.PolyDataMapper() self.mapper = mapper tf = self.tube_filter tf.radius = self.radius if self.use_tubes: tf.input = pd mapper.input = tf.output a = _make_actor(mapper=mapper) a.property.color = self.color self.actors.append(a) def _radius_changed(self, val): self.tube_filter.radius = val self.render() def _use_tubes_changed(self, val): if val: tf = self.tube_filter tf.input = self.poly_data self.mapper.input = tf.output else: self.mapper.input = self.poly_data self.render() def _color_changed(self, val): if self.actors: self.actors[0].property.color = val self.render() ###################################################################### # `Outline` class. ###################################################################### class Outline(MLabBase): # The axis instance to use to annotate the outline axis = Instance(tvtk.CubeAxesActor2D, (), {'label_format':"%4.2g", 'fly_mode':"outer_edges", 'font_factor':1.25, 'number_of_labels':5, 'corner_offset':0.0, 'scaling':0}) # The outline source. outline = Instance(tvtk.OutlineSource, ()) def __init__(self, **traits): super(Outline, self).__init__(**traits) out_mapper = tvtk.PolyDataMapper(input=self.outline.output) out_actor = _make_actor(mapper=out_mapper) axis = self.axis if hasattr(axis, 'view_prop'): axis.view_prop = out_actor else: axis.prop = out_actor self.actors.extend([out_actor, axis]) def update(self): if self.renwin: rw = self.renwin v1, v2 = [x.visibility for x in self.actors] self.actors[0].visibility = 0 self.actors[1].visibility = 0 rw.render() bounds = rw.renderer.compute_visible_prop_bounds() self.outline.bounds = bounds rw.render() self.actors[0].visibility = v1 self.actors[1].visibility = v2 def _renwin_changed(self, old, new): super(Outline, self)._renwin_changed(old, new) if old: old.on_trait_change(self.update, 'actor_added', remove=True) old.on_trait_change(self.update, 'actor_removed', remove=True) if new: self.axis.camera = new.renderer.active_camera new.on_trait_change(self.update, 'actor_added') new.on_trait_change(self.update, 'actor_removed') ###################################################################### # `Title` class. ###################################################################### class Title(MLabBase): # Text of the title. text = Str('Title', desc='text of the title') # The text actor that renders the title. text_actor = Instance(tvtk.TextActor, ()) def __init__(self, **traits): super(Title, self).__init__(**traits) ta = self.text_actor if VTK_VER > 5.1: ta.set(text_scale_mode='prop', height=0.05, input=self.text) else: ta.set(scaled_text=True, height=0.05, input=self.text) pc = ta.position_coordinate pc.coordinate_system = 'normalized_viewport' pc.value = 0.25, 0.925, 0.0 self.actors.append(self.text_actor) def _text_changed(self, val): self.text_actor.input = val self.render() ###################################################################### # `LUTBase` class. ###################################################################### class LUTBase(MLabBase): # The choices for the lookuptable lut_type = Trait('red-blue', 'red-blue', 'blue-red', 'black-white', 'white-black', desc='the type of the lookup table') # The LookupTable instance. lut = Instance(tvtk.LookupTable, ()) # The scalar bar. scalar_bar = Instance(tvtk.ScalarBarActor, (), {'orientation':'horizontal', 'width':0.8, 'height':0.17}) # The scalar_bar widget. scalar_bar_widget = Instance(tvtk.ScalarBarWidget, ()) # The legend name for the scalar bar. legend_text = Str('Scalar', desc='the title of the legend') # Turn on/off the visibility of the scalar bar. show_scalar_bar = Bool(False, desc='specifies if scalar bar is shown or not') def __init__(self, **traits): super(LUTBase, self).__init__(**traits) self.lut.number_of_colors = 256 self._lut_type_changed(self.lut_type) self.scalar_bar.set(lookup_table=self.lut, title=self.legend_text) pc = self.scalar_bar.position_coordinate pc.coordinate_system = 'normalized_viewport' pc.value = 0.1, 0.01, 0.0 self.scalar_bar_widget.set(scalar_bar_actor=self.scalar_bar, key_press_activation=False) def _lut_type_changed(self, val): if val == 'red-blue': hue_range = 0.0, 0.6667 saturation_range = 1.0, 1.0 value_range = 1.0, 1.0 elif val == 'blue-red': hue_range = 0.6667, 0.0 saturation_range = 1.0, 1.0 value_range = 1.0, 1.0 elif val == 'black-white': hue_range = 0.0, 0.0 saturation_range = 0.0, 0.0 value_range = 0.0, 1.0 elif val == 'white-black': hue_range = 0.0, 0.0 saturation_range = 0.0, 0.0 value_range = 1.0, 0.0 lut = self.lut lut.set(hue_range=hue_range, saturation_range=saturation_range, value_range=value_range, number_of_table_values=256, ramp='sqrt') lut.force_build() self.render() def _legend_text_changed(self, val): self.scalar_bar.title = val self.scalar_bar.modified() self.render() def _show_scalar_bar_changed(self, val): if self.renwin: self.scalar_bar_widget.enabled = val self.renwin.render() def _renwin_changed(self, old, new): sbw = self.scalar_bar_widget if old: sbw.interactor = None old.render() if new: sbw.interactor = new.interactor sbw.enabled = self.show_scalar_bar new.render() super(LUTBase, self)._renwin_changed(old, new) ###################################################################### # `SurfRegular` class. ###################################################################### class SurfRegular(LUTBase): def __init__(self, x, y, z, warp=1, scale=[1.0, 1.0, 1.0], f_args=(), f_kwargs=None, **traits): super(SurfRegular, self).__init__(**traits) if f_kwargs is None: f_kwargs = {} data, actor = make_surf_actor(x, y, z, warp, scale, *f_args, **f_kwargs) self.data = data mapper = actor.mapper mapper.lookup_table = self.lut self.lut.table_range = mapper.scalar_range self.actors.append(actor) ###################################################################### # `SurfRegularC` class. ###################################################################### class SurfRegularC(LUTBase): # Number of contours. number_of_contours = Int(10, desc='number of contours values') # The contour filter. contour_filter = Instance(tvtk.ContourFilter, ()) def __init__(self, x, y, z, warp=1, scale=[1.0, 1.0, 1.0], f_args=(), f_kwargs=None, **traits): super(SurfRegularC, self).__init__(**traits) if f_kwargs is None: f_kwargs = {} data, actor = make_surf_actor(x, y, z, warp, scale, *f_args, **f_kwargs) mapper = actor.mapper mapper.lookup_table = self.lut self.lut.table_range = mapper.scalar_range self.data = data dr = data.point_data.scalars.range cf = self.contour_filter cf.input = data cf.generate_values(self.number_of_contours, dr[0], dr[1]) mapper = tvtk.PolyDataMapper(input=cf.output, lookup_table=self.lut) cont_actor = _make_actor(mapper=mapper) self.actors.extend([actor, cont_actor]) def _number_of_contours_changed(self, val): dr = self.data.point_data.scalars.range self.contour_filter.generate_values(val, dr[0], dr[1]) self.render() ###################################################################### # `TriMesh` class. ###################################################################### class TriMesh(LUTBase): # Disables/enables scalar visibility. scalar_visibility = Bool(False, desc='show scalar visibility') # Representation of the mesh as surface or wireframe. surface = Bool(False, desc='show as surface or wireframe') # Color of the mesh. color = vtk_color_trait((0.5, 1.0, 0.5)) def __init__(self, triangles, points, scalars=None, **traits): """ Parameters ---------- - triangles : array This contains a list of vertex indices forming the triangles. - points : array Contains the list of points referred to in the triangle list. - scalars : array (optional) Scalars to associate with the points. """ super(TriMesh, self).__init__(**traits) self.pd = make_triangle_polydata(triangles, points, scalars) mapper = tvtk.PolyDataMapper(input=self.pd, lookup_table=self.lut, scalar_visibility=self.scalar_visibility) if scalars is not None: rs = numpy.ravel(scalars) dr = min(rs), max(rs) mapper.scalar_range = dr self.lut.table_range = dr actor = _make_actor(mapper=mapper) representation = 'w' if self.surface: representation = 's' if representation == 'w': actor.property.set(diffuse=0.0, ambient=1.0, color=self.color, representation=representation) else: actor.property.set(diffuse=1.0, ambient=0.0, color=self.color, representation=representation) self.actors.append(actor) def _scalar_visibility_changed(self, val): if self.actors: mapper = self.actors[0].mapper mapper.scalar_visibility = val self.render() def _surface_changed(self, val): if self.actors: representation = 'w' if val: representation = 's' actor = self.actors[0] if representation == 'w': actor.property.set(diffuse=0.0, ambient=1.0, representation=representation) else: actor.property.set(diffuse=1.0, ambient=0.0, representation=representation) self.render() def _color_changed(self, val): if self.actors: self.actors[0].property.color = val self.render() ###################################################################### # `FancyTriMesh` class. ###################################################################### class FancyTriMesh(LUTBase): """Shows a mesh of triangles and draws the edges as tubes and points as balls.""" # Disables/enables scalar visibility. scalar_visibility = Bool(False, desc='show scalar visibility') # Color of the mesh. color = vtk_color_trait((0.5, 1.0, 0.5)) # The radius of the tubes. tube_radius = Float(0.0, desc='radius of the tubes') # The radius of the spheres. sphere_radius = Float(0.0, desc='radius of the spheres') # The TubeFilter used to make the tubes for the edges. tube_filter = Instance(tvtk.TubeFilter, (), {'vary_radius':'vary_radius_off', 'number_of_sides':6}) # The sphere source for the points. sphere_source = Instance(tvtk.SphereSource, (), {'theta_resolution':12, 'phi_resolution':12}) def __init__(self, triangles, points, scalars=None, **traits): """ Parameters ---------- - triangles : array This contains a list of vertex indices forming the triangles. - points : array Contains the list of points referred to in the triangle list. - scalars : array (optional) Scalars to associate with the points. """ super(FancyTriMesh, self).__init__(**traits) self.points = points self.pd = make_triangle_polydata(triangles, points, scalars) # Update the radii so the default is computed correctly. self._tube_radius_changed(self.tube_radius) self._sphere_radius_changed(self.sphere_radius) scalar_vis = self.scalar_visibility # Extract the edges and show the lines as tubes. self.extract_filter = tvtk.ExtractEdges(input=self.pd) extract_f = self.extract_filter self.tube_filter.set(input=extract_f.output, radius=self.tube_radius) edge_mapper = tvtk.PolyDataMapper(input=self.tube_filter.output, lookup_table=self.lut, scalar_visibility=scalar_vis) edge_actor = _make_actor(mapper=edge_mapper) edge_actor.property.color = self.color # Create the spheres for the points. self.sphere_source.radius = self.sphere_radius spheres = tvtk.Glyph3D(scaling=0, source=self.sphere_source.output, input=extract_f.output) sphere_mapper = tvtk.PolyDataMapper(input=spheres.output, lookup_table=self.lut, scalar_visibility=scalar_vis) sphere_actor = _make_actor(mapper=sphere_mapper) sphere_actor.property.color = self.color if scalars is not None: rs = numpy.ravel(scalars) dr = min(rs), max(rs) self.lut.table_range = dr edge_mapper.scalar_range = dr sphere_mapper.scalar_range = dr self.actors.extend([edge_actor, sphere_actor]) def _scalar_visibility_changed(self, val): if self.actors: for i in self.actors: i.mapper.scalar_visibility = val self.render() def _tube_radius_changed(self, val): points = self.points if val < 1.0e-9: val = (max(numpy.ravel(points)) - min(numpy.ravel(points)))/250.0 self.tube_radius = val self.tube_filter.radius = val self.render() def _sphere_radius_changed(self, val): points = self.points if val < 1.0e-9: val = (max(numpy.ravel(points)) - min(numpy.ravel(points)))/100.0 self.sphere_radius = val self.sphere_source.radius = val self.render() def _color_changed(self, val): if self.actors: self.actors[0].property.color = val self.render() ###################################################################### # `Mesh` class. ###################################################################### class Mesh(TriMesh): def __init__(self, x, y, z, scalars=None, **traits): """ Parameters ---------- - x : array A list of x coordinate values formed using numpy.mgrid. - y : array A list of y coordinate values formed using numpy.mgrid. - z : array A list of z coordinate values formed using numpy.mgrid. - scalars : array (optional) Scalars to associate with the points. """ triangles, points = make_triangles_points(x, y, z, scalars) super(Mesh, self).__init__(triangles, points, scalars, **traits) ###################################################################### # `FancyMesh` class. ###################################################################### class FancyMesh(FancyTriMesh): def __init__(self, x, y, z, scalars=None, **traits): """ Parameters ---------- - x : array A list of x coordinate values formed using numpy.mgrid. - y : array A list of y coordinate values formed using numpy.mgrid. - z : array A list of z coordinate values formed using numpy.mgrid. - scalars : array (optional) Scalars to associate with the points. """ triangles, points = make_triangles_points(x, y, z, scalars) super(FancyMesh, self).__init__(triangles, points, scalars, **traits) ###################################################################### # `Surf` class. ###################################################################### class Surf(LUTBase): # Disables/enables scalar visibility. scalar_visibility = Bool(True, desc='show scalar visibility') # Color of the mesh. color = vtk_color_trait((0.5, 1.0, 0.5)) def __init__(self, x, y, z, scalars=None, **traits): """ Parameters ---------- - x : array A list of x coordinate values formed using numpy.mgrid. - y : array A list of y coordinate values formed using numpy.mgrid. - z : array A list of z coordinate values formed using numpy.mgrid. - scalars : array (optional) Scalars to associate with the points. """ super(Surf, self).__init__(**traits) triangles, points = make_triangles_points(x, y, z, scalars) self.pd = make_triangle_polydata(triangles, points, scalars) mapper = tvtk.PolyDataMapper(input=self.pd, lookup_table=self.lut, scalar_visibility=self.scalar_visibility) if scalars is not None: rs = numpy.ravel(scalars) dr = min(rs), max(rs) mapper.scalar_range = dr self.lut.table_range = dr actor = _make_actor(mapper=mapper) actor.property.set(color=self.color) self.actors.append(actor) def _scalar_visibility_changed(self, val): if self.actors: mapper = self.actors[0].mapper mapper.scalar_visibility = val self.render() def _surface_changed(self, val): if self.actors: representation = 'w' if val: representation = 's' self.actors[0].property.representation = representation self.render() def _color_changed(self, val): if self.actors: self.actors[0].property.color = val self.render() ###################################################################### # `Contour3` class. ###################################################################### class Contour3(LUTBase): # Number of contours. number_of_contours = Int(10, desc='number of contours values') # The contour filter. contour_filter = Instance(tvtk.ContourFilter, ()) def __init__(self, x, y, z, scalars, **traits): """ Parameters ---------- - x : array A list of x coordinate values formed using numpy.mgrid. - y : array A list of y coordinate values formed using numpy.mgrid. - z : array A list of z coordinate values formed using numpy.mgrid. - scalars : array Scalars to associate with the points. """ super(Contour3, self).__init__(**traits) triangles, points = make_triangles_points(x, y, z, scalars) self.pd = make_triangle_polydata(triangles, points, scalars) dr = self.pd.point_data.scalars.range self.lut.table_range = dr cf = self.contour_filter cf.input = self.pd cf.generate_values(self.number_of_contours, dr[0], dr[1]) mapper = tvtk.PolyDataMapper(input=cf.output, lookup_table=self.lut, scalar_range=dr) cont_actor = _make_actor(mapper=mapper) self.actors.append(cont_actor) def _number_of_contours_changed(self, val): dr = self.pd.point_data.scalars.range self.contour_filter.generate_values(val, dr[0], dr[1]) self.render() ###################################################################### # `ImShow` class. ###################################################################### class ImShow(LUTBase): """Allows one to view a 2D numpy array as an image. This works best for very large arrays (like 1024x1024 arrays). """ # Interpolate the image or not. interpolate = Bool(False, desc='specifies if image should be interpolated') def __init__(self, arr, scale=[1.0, 1.0, 1.0], **traits): """ Parameters ---------- - arr : Array to be viewed. - scale : Scale the x, y and z axis as per passed values. Defaults to [1.0, 1.0, 1.0]. """ super(ImShow, self).__init__(**traits) assert len(arr.shape) == 2, "Only 2D arrays can be viewed!" ny, nx = arr.shape dx, dy, junk = numpy.array(scale)*1.0 xa = numpy.arange(0, nx*scale[0] - 0.1*dx, dx, 'f') ya = numpy.arange(0, ny*scale[1] - 0.1*dy, dy, 'f') arr_flat = numpy.ravel(arr) min_val = min(arr_flat) max_val = max(arr_flat) sp = _create_structured_points_direct(xa, ya) lut = self.lut lut.table_range = min_val, max_val a = lut.map_scalars(arr_flat, 0, 0) sp.point_data.scalars = a sp.point_data.scalars.name = 'scalars' sp.scalar_type = 'unsigned_char' sp.number_of_scalar_components = 4 ia = tvtk.ImageActor(input=sp, interpolate=self.interpolate) self.actors.append(ia) def _interpolate_changed(self, val): if self.actors: ia = self.actors[0] ia.interpolate = val self.render() ###################################################################### # `Figure` class. ###################################################################### class Figure(HasTraits): """A Figure manages varuous MLabBase objects. Each of these objects contains an actor and does something neat.""" # The various instances of MLabBase that populate this figure. objects = List(MLabBase) def __init__(self, renwin, **traits): super(Figure, self).__init__(**traits) self.renwin = renwin def add(self, obj): """Add an object to the figure. This adds the actors of the object to the renderwindow.""" self.objects.append(obj) def pop(self): """Pops out the last object.""" return self.objects.pop() def clear(self): """Removes all objects in the figure.""" self.objects = [] def _objects_changed(self, new, old): self._handle_objects(new, old) def _objects_items_changed(self, list_event): self._handle_objects(list_event.removed, list_event.added) def _handle_objects(self, removed, added): for obj in removed: obj.renwin = None rw = self.renwin for obj in added: obj.renwin = rw rw.reset_zoom() rw.render() def figure(outline=True, browser=True): """Simple helper function that returns a usable figure. Parameters ---------- - outline : `bool` (default: True) If True, create an outline bounding box along with an axes marker for the scene. - browser : `bool` (default, True) If True, creates an IVTK scene with an embedded PipelineBrowser. If False, does not create it. """ v = ivtk.viewer(browser) f = Figure(v.scene) if outline: o = Outline() f.add(o) v.scene.reset_zoom() return f ###################################################################### # Test functions. ###################################################################### def test_arrows(fig): a = Arrows([[-1,-1,-1],[1,0,0]], [[1,1,1],[0,1,0]], color=(1,0,0)) fig.add(a) def test_lines(fig): """Generates a pretty set of lines.""" n_mer, n_long = 6, 11 pi = numpy.pi dphi = pi/1000.0 phi = numpy.arange(0.0, 2*pi + 0.5*dphi, dphi, 'd') mu = phi*n_mer x = numpy.cos(mu)*(1+numpy.cos(n_long*mu/n_mer)*0.5) y = numpy.sin(mu)*(1+numpy.cos(n_long*mu/n_mer)*0.5) z = numpy.sin(n_long*mu/n_mer)*0.5 pts = numpy.zeros((len(mu), 3), 'd') pts[:,0], pts[:,1], pts[:,2] = x, y, z l = Line3(pts, radius=0.05, color=(0.0, 0.0, 0.8)) fig.add(l) def test_molecule(fig): """Generates and shows a Caffeine molecule.""" o = [[30, 62, 19],[8, 21, 10]] n = [[31, 21, 11], [18, 42, 14], [55, 46, 17], [56, 25, 13]] c = [[5, 49, 15], [30, 50, 16], [42, 42, 15], [43, 29, 13], [18, 28, 12], [32, 6, 8], [63, 36, 15], [59, 60, 20]] h = [[23, 5, 7], [32, 0, 16], [37, 5, 0], [73, 36, 16], [69, 60, 20], [54, 62, 28], [57, 66, 12], [6, 59, 16], [1, 44, 22], [0, 49, 6]] oxygen = Spheres(o, radius=8, color=(1,0,0)) nitrogen = Spheres(n, radius=10, color=(0,0,1)) carbon = Spheres(c, radius=10, color=(0,1,0)) hydrogen = Spheres(h, radius=5, color=(1,1,1)) for i in oxygen, nitrogen, carbon, hydrogen: fig.add(i) def test_trimesh(fig): """Test for simple triangle mesh.""" pts = numpy.array([[0.0,0,0], [1.0,0.0,0.0], [1,1,0]], 'd') triangles = [[0, 1, 2]] t1 = TriMesh(triangles, pts) fig.add(t1) pts1 = pts.copy() pts1[:,2] = 1.0 t2 = FancyTriMesh(triangles, pts1) fig.add(t2) def test_surf_regular(fig, contour=1): """Test Surf on regularly spaced co-ordinates like MayaVi.""" def f(x, y): return numpy.sin(x*y)/(x*y) x = numpy.arange(-7., 7.05, 0.1) y = numpy.arange(-5., 5.05, 0.05) if contour: s = SurfRegularC(x, y, f) else: s = SurfRegular(x, y, f) fig.add(s) def test_simple_surf(fig): """Test Surf with a simple collection of points.""" x, y = numpy.mgrid[0:3:1,0:3:1] z = x s = Surf(x, y, z, numpy.asarray(z, 'd')) fig.add(s) def test_surf(fig): """A very pretty picture of spherical harmonics translated from the octaviz example.""" pi = numpy.pi cos = numpy.cos sin = numpy.sin dphi, dtheta = pi/250.0, pi/250.0 [phi,theta] = numpy.mgrid[0:pi+dphi*1.5:dphi,0:2*pi+dtheta*1.5:dtheta] m0 = 4; m1 = 3; m2 = 2; m3 = 3; m4 = 6; m5 = 2; m6 = 6; m7 = 4; r = sin(m0*phi)**m1 + cos(m2*phi)**m3 + sin(m4*theta)**m5 + cos(m6*theta)**m7 x = r*sin(phi)*cos(theta) y = r*cos(phi) z = r*sin(phi)*sin(theta); s = Surf(x, y, z, z) fig.add(s) def test_mesh_sphere(fig): """Create a simple sphere and test the mesh.""" pi = numpy.pi cos = numpy.cos sin = numpy.sin du, dv = pi/20.0, pi/20.0 phi, theta = numpy.mgrid[0.01:pi+du*1.5:du, 0:2*pi+dv*1.5:dv] r = 1.0 x = r*sin(phi)*cos(theta) y = r*sin(phi)*sin(theta) z = r*cos(phi) s = FancyMesh(x, y, z, z, scalar_visibility=True) fig.add(s) def test_mesh(fig): """Create a fancy looking mesh (example taken from octaviz).""" pi = numpy.pi cos = numpy.cos sin = numpy.sin du, dv = pi/20.0, pi/20.0 u, v = numpy.mgrid[0.01:pi+du*1.5:du, 0:2*pi+dv*1.5:dv] x = (1- cos(u))*cos(u+2*pi/3) * cos(v + 2*pi/3.0)*0.5 y = (1- cos(u))*cos(u+2*pi/3) * cos(v - 2*pi/3.0)*0.5 z = cos(u-2*pi/3.) m = FancyMesh(x, y, z, z, scalar_visibility=True) fig.add(m) def test_imshow(fig): """Show a large random array.""" z_large = numpy.random.random((1024, 512)) i = ImShow(z_large) fig.add(i) def main(): gui = GUI() # Create and open an application window. window = ivtk.IVTKWithCrustAndBrowser(size=(800,600)) window.open() f = Figure(window.scene) # Create an outline. o = Outline() f.add(o) # Create some pretty pictures. #test_lines(f) test_surf(f) window.scene.reset_zoom() # Start the GUI event loop! gui.start_event_loop() if __name__ == '__main__': main() mayavi-4.1.0/tvtk/tvtk_access.py0000644000175100001440000000435211674464502017653 0ustar ischnellusers00000000000000"""Creates a pseudo-package called `tvtk` that lets one use the tvtk classes in a clean and quick manner. The `TVTK` class is instantiated and this instance serves as the `tvtk` 'module'. For more details on this see the devel.txt in the TVTK documentation directory. """ # Author: Prabhu Ramachandran # Copyright (c) 2007-2008, Enthought, Inc. # License: BSD Style. from os.path import exists, join, dirname, isdir # The tvtk wrapper code is all typically inside one zip file. We try to # find this file and then create the 'tvtk' module wrapper from that. If # the ZIP file is extracted into a tvtk_classes directory the ZIP file is # not used and the tvtk_classes directory is inserted into sys.path and # the directory contents are used for the tvtk classes -- note that you # must have the following structure # tvtk_classes/tvtk_classes/__init__.py. This is handy for tools like # pydev (Eclipse). If neither the tvtk_classes directory or the zip file # is found an error is raised. _zip = join(dirname(__file__), 'tvtk_classes.zip') tvtk_class_dir = join(dirname(__file__), 'tvtk_classes') if not ( exists(tvtk_class_dir) and isdir(tvtk_class_dir) or exists(_zip)): raise ImportError("TVTK not built properly. " "Unable to find either a directory: %s or a file: %s " "with the TVTK classes." % (tvtk_class_dir, _zip) ) # Check if the VTK version is the same as that used to build TVTK. from tvtk.tvtk_classes.vtk_version import vtk_build_version # Make sure VTK is installed. try: import vtk except ImportError, m: msg = '%s\n%s\nDo you have vtk installed properly?\n' \ 'VTK (and build instructions) can be obtained from http://www.vtk.org\n' \ % (m, '_'*80) raise ImportError(msg) vtk_version = vtk.vtkVersion().GetVTKVersion()[:3] if vtk_version != vtk_build_version: msg = '*'*80 + "\n" + \ 'WARNING: Imported VTK version (%s) does not match the one used\n'\ ' to build the TVTK classes (%s). This may cause problems.\n'\ ' Please rebuild TVTK.\n'%(vtk_version, vtk_build_version) +\ '*'*80 + '\n' print msg # Now setup TVTK itself. from tvtk.tvtk_classes import tvtk_helper tvtk = tvtk_helper.TVTK() mayavi-4.1.0/tvtk/tests/0000755000175100001440000000000011674464502016126 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/tests/test_ctf_util.py0000644000175100001440000000442111674464502021351 0ustar ischnellusers00000000000000"""Simple tests for the color transfer function utilities in tvtk.""" import unittest from tvtk.util.ctf import (load_ctfs, save_ctfs, \ rescale_ctfs, ColorTransferFunction, PiecewiseFunction) from tvtk.api import tvtk def make_volume_prop(mins=255, maxs=355): """Make a volume property for the testing.""" table = tvtk.VolumeProperty() ctf = ColorTransferFunction() ds = (maxs-mins)/4.0 try: ctf.range = (mins, maxs) except Exception: # VTK versions < 5.2 don't seem to need this. pass ctf.add_rgb_point(mins, 0.00, 0.0, 1.00) ctf.add_rgb_point(mins+ds, 0.25, 0.5, 0.75) ctf.add_rgb_point(mins+2*ds, 0.50, 1.0, 0.50) ctf.add_rgb_point(mins+3*ds, 0.75, 0.5, 0.25) ctf.add_rgb_point(maxs, 1.00, 0.0, 0.00) otf = PiecewiseFunction() otf.add_point(mins, 0.0) otf.add_point(maxs, 0.2) table.set_color(ctf) table.set_scalar_opacity(otf) return table, ctf, otf ################################################################################ # `TestCTFUtil` class. ################################################################################ class TestCTFUtil(unittest.TestCase): def setUp(self): """Called before every test is run.""" vp, ctf, otf = make_volume_prop() self.vp = vp self.ctf = ctf self.otf = otf def tearDown(self): """Called after the test is run.""" return def test_save_load_ctf(self): """Test saving and loading of a CTF.""" # Create a default ctf, save it. data = save_ctfs(self.vp) # load it into another volume property, mvp = tvtk.VolumeProperty() ctf = load_ctfs(data, mvp) # get the data from the new one mdata = save_ctfs(mvp) # check that both the data are identical. self.assertEqual(mdata, data) def test_rescale_ctf(self): """Test rescaling a CTF.""" # Expected data. evp, ectf, eotf = make_volume_prop(0.0, 1.0) edata = save_ctfs(evp) # Rescaled data. ctf, otf = rescale_ctfs(self.vp, (0.0, 1.0)) data = save_ctfs(self.vp) # check that both the data are identical. self.assertEqual(edata, data) if __name__ == '__main__': unittest.main() mayavi-4.1.0/tvtk/tests/test_messenger.py0000644000175100001440000001055711674464502021537 0ustar ischnellusers00000000000000"""Tests for messenger.py """ # Author: Prabhu Ramachandran # Copyright (c) 2004, Enthought, Inc. # License: BSD Style. import unittest from tvtk import messenger ################################################################# # Support code. ################################################################# class A: def __init__(self): self.event = None self.args = None self.kw = None self.did_catch_all = 0 def callback(self, obj, event, *args, **kw): self.event = event self.args = args self.kw = kw def catch_all_cb(self, obj, event, *args, **kw): self.did_catch_all = 1 ret = None def callback(obj, event, *args, **kw): global ret ret = event, args, kw class B: def __init__(self): self.a = A() messenger.connect(self, 'method', self.a.callback) messenger.connect(self, 'function', callback) def __del__(self): messenger.disconnect(self) def send(self, *args, **kw): messenger.send(self, 'method', *args, **kw) messenger.send(self, 'function', *args, **kw) ################################################################# # The test case. ################################################################# class TestMessenger(unittest.TestCase): def test_basic(self): """Test basic functionality of the messenger.""" m = messenger.Messenger() orig_len = len(m._signals) b = B() b.send(1, test=1) self.assertEqual(b.a.event, 'method') self.assertEqual(ret[0], 'function') self.assertEqual(b.a.args, (1,)) self.assertEqual(ret[1], (1,)) self.assertEqual(b.a.kw, {'test':1}) self.assertEqual(ret[2], {'test':1}) # Ensures that disconnect works and also that there are no # reference cycles. self.assertEqual(len(m._signals) > orig_len, True) del b self.assertEqual(len(m._signals), orig_len) def test_reload(self): """Tests if module is reload safe.""" b = B() m = messenger.Messenger() orig_len = len(m._signals) reload(messenger) m = messenger.Messenger() self.assertEqual(len(m._signals), orig_len) b.send(1, test=1) self.assertEqual(b.a.event, 'method') self.assertEqual(ret[0], 'function') self.assertEqual(b.a.args, (1,)) self.assertEqual(ret[1], (1,)) self.assertEqual(b.a.kw, {'test':1}) self.assertEqual(ret[2], {'test':1}) def test_catchall(self): """Tests if catch all handlers are called.""" b = B() b.send() self.assertEqual(b.a.event, 'method') self.assertEqual(b.a.args, ()) self.assertEqual(b.a.kw, {}) self.assertEqual(b.a.did_catch_all, 0) messenger.connect(b, 'AnyEvent', b.a.catch_all_cb) b.send(1, test=1) self.assertEqual(b.a.event, 'method') self.assertEqual(b.a.args, (1,)) self.assertEqual(b.a.kw, {'test':1}) self.assertEqual(b.a.did_catch_all, 1) b.a.did_catch_all = 0 messenger.disconnect(b, 'AnyEvent') b.send(1, test=1) self.assertEqual(b.a.did_catch_all, 0) def test_disconnect(self): """Tests if disconnection works correctly.""" global ret ret = None b = B() messenger.disconnect(b) b.send(1, test=1) self.assertEqual(b.a.event, None) self.assertEqual(b.a.args, None) self.assertEqual(b.a.kw, None) self.assertEqual(b.a.did_catch_all, 0) self.assertEqual(ret, None) def test_send_on_dead_ref(self): """Test if sending to a gc'd callback works gracefully.""" class C: def foo(self, o, e): pass c = C() c1 = C() messenger.connect(c1, 'foo', c.foo) messenger.send(c1, 'foo') # Test if things behave sanely if a message was sent and one # of the callbacks has been gc'd. m = messenger.Messenger() l1 = len(m._signals[hash(c1)]['foo']) # del c messenger.send(c1, 'foo') # l2 = len(m._signals[hash(c1)]['foo']) # Since 'c' is gc'd this callback should have been cleared # out. self.assertEqual(l2, l1 - 1) # Clean up. messenger.disconnect(c1) if __name__ == "__main__": unittest.main() mayavi-4.1.0/tvtk/tests/test_array_handler.py0000644000175100001440000004050211674464502022353 0ustar ischnellusers00000000000000""" Tests for array_handler.py. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2008, Enthought, Inc. # License: BSD Style. import unittest import vtk import numpy from tvtk import array_handler from tvtk import tvtk_base # FIXME: test_tvtk_base.py is in the local directory so just doing # from test_tvtk_base import Prop # should be enough, however nose 0.9.3 will not find it, unless you give # it the full path. It nose 0.10.3 works fine in this respect. from tvtk.tests.test_tvtk_base import Prop def mysum(arr): val = arr while type(val) == numpy.ndarray: val = numpy.sum(val) return val class TestArrayHandler(unittest.TestCase): def _check_arrays(self, arr, vtk_arr): self.assertEqual(vtk_arr.GetNumberOfTuples(), len(arr)) if len(arr.shape) == 2: dim1 = arr.shape[1] self.assertEqual(vtk_arr.GetNumberOfComponents(), dim1) for i in range(len(arr)): if dim1 in [1,2,3,4,9]: res = getattr(vtk_arr, 'GetTuple%s'%dim1)(i) self.assertEqual(numpy.sum(res - arr[i]), 0) else: res = [vtk_arr.GetComponent(i, j) for j in range(dim1)] self.assertEqual(numpy.sum(res - arr[i]), 0) else: if arr.dtype.char == 'c': for i in range(len(arr)): self.assertEqual(chr(int(vtk_arr.GetTuple1(i))), arr[i]) else: for i in range(len(arr)): self.assertEqual(vtk_arr.GetTuple1(i), arr[i]) def test_array2vtk(self): """Test Numeric array to VTK array conversion and vice-versa.""" # Put all the test arrays here. t_z = [] # Test the different types of arrays. t_z.append(numpy.array([-128, 0, 127], numpy.int8)) # FIXME: character arrays are a problem since there is no # unique mapping to a VTK data type and back. #t_z.append(numpy.array([-128, 0, 127], numpy.character)) t_z.append(numpy.array([-32768, 0, 32767], numpy.int16)) t_z.append(numpy.array([-2147483648, 0, 2147483647], numpy.int32)) t_z.append(numpy.array([0, 255], numpy.uint8)) t_z.append(numpy.array([0, 65535], numpy.uint16)) t_z.append(numpy.array([0, 4294967295L], numpy.uint32)) t_z.append(numpy.array([-1.0e38, 0, 1.0e38], 'f')) t_z.append(numpy.array([-1.0e299, 0, 1.0e299], 'd')) # Check multi-component arrays. t_z.append(numpy.array([[1], [2], [300]], 'd')) t_z.append(numpy.array([[1, 20], [300, 4000]], 'd')) t_z.append(numpy.array([[1, 2, 3], [4, 5, 6]], 'f')) t_z.append(numpy.array([[1, 2, 3],[4, 5, 6]], 'd')) t_z.append(numpy.array([[1, 2, 3, 400],[4, 5, 6, 700]], 'd')) t_z.append(numpy.array([range(9),range(10,19)], 'f')) # Test if a Python list also works. t_z.append(numpy.array([[1., 2., 3., 400.],[4, 5, 6, 700]], 'd')) # Test if arrays with number of components not in [1,2,3,4,9] work. t_z.append(numpy.array([[1, 2, 3, 400, 5000], [4, 5, 6, 700, 8000]], 'd')) t_z.append(numpy.array([range(10), range(10,20)], 'd')) for z in t_z: vtk_arr = array_handler.array2vtk(z) # Test for memory leaks. self.assertEqual(vtk_arr.GetReferenceCount(), array_handler.BASE_REFERENCE_COUNT) self._check_arrays(z, vtk_arr) z1 = array_handler.vtk2array(vtk_arr) if len(z.shape) == 1: self.assertEqual(len(z1.shape), 1) if z.dtype.char != 'c': #print z1 self.assertEqual(sum(numpy.ravel(z) - numpy.ravel(z1)), 0) else: #print z1.astype('c') self.assertEqual(z, z1.astype('c')) # Check if type conversion works correctly. z = numpy.array([-128, 0, 127], numpy.int8) vtk_arr = vtk.vtkDoubleArray() ident = id(vtk_arr) vtk_arr = array_handler.array2vtk(z, vtk_arr) # Make sure this is the same array! self.assertEqual(ident, id(vtk_arr)) self._check_arrays(z, vtk_arr) # Check the vtkBitArray. vtk_arr = vtk.vtkBitArray() vtk_arr.InsertNextValue(0) vtk_arr.InsertNextValue(1) vtk_arr.InsertNextValue(0) vtk_arr.InsertNextValue(1) arr = array_handler.vtk2array(vtk_arr) self.assertEqual(numpy.sum(arr - [0,1,0,1]), 0) vtk_arr = array_handler.array2vtk(arr, vtk_arr) self.assertEqual(vtk_arr.GetValue(0), 0) self.assertEqual(vtk_arr.GetValue(1), 1) self.assertEqual(vtk_arr.GetValue(2), 0) self.assertEqual(vtk_arr.GetValue(3), 1) # ---------------------------------------- # Test if the array is copied or not. a = numpy.array([[1, 2, 3],[4, 5, 6]], 'd') vtk_arr = array_handler.array2vtk(a) # Change the numpy array and see if the changes are # reflected in the VTK array. a[0] = [10.0, 20.0, 30.0] self.assertEqual(vtk_arr.GetTuple3(0), (10., 20., 30.)) # Make sure the cache is doing its job. key = vtk_arr.__this__ z = array_handler._array_cache.get(vtk_arr) self.assertEqual(numpy.sum(z - numpy.ravel(a)), 0.0) l1 = len(array_handler._array_cache) # del the Numeric array and see if this still works. del a self.assertEqual(vtk_arr.GetTuple3(0), (10., 20., 30.)) # Check the cache -- just making sure. self.assertEqual(len(array_handler._array_cache), l1) # Delete the VTK array and see if the cache is cleared. del vtk_arr self.assertEqual(len(array_handler._array_cache), l1-1) self.assertEqual(array_handler._array_cache._cache.has_key(key), False) # Make sure bit arrays are copied. vtk_arr = vtk.vtkBitArray() a = numpy.array([0,1,0,1], numpy.int32) vtk_arr = array_handler.array2vtk(a, vtk_arr) del a self.assertEqual(vtk_arr.GetValue(0), 0) self.assertEqual(vtk_arr.GetValue(1), 1) self.assertEqual(vtk_arr.GetValue(2), 0) self.assertEqual(vtk_arr.GetValue(3), 1) # Make sure the code at least runs for all the non-complex # numerical dtypes in numpy. for dtype in (numpy.sctypes['int'] + numpy.sctypes['uint'] + numpy.sctypes['float']): array_handler.array2vtk(numpy.zeros((1,), dtype=dtype)) def test_arr2cell_array(self): """Test Numeric array to vtkCellArray conversion.""" # Test list of lists. a = [[0], [1, 2], [3, 4, 5], [6, 7, 8, 9]] cells = array_handler.array2vtkCellArray(a) z = numpy.array([1, 0, 2, 1,2, 3, 3,4,5, 4, 6,7,8,9]) arr = array_handler.vtk2array(cells.GetData()) self.assertEqual(numpy.sum(arr - z), 0) self.assertEqual(len(arr.shape), 1) self.assertEqual(len(arr), 14) # Test if optional argument stuff also works. cells = vtk.vtkCellArray() ident = id(cells) cells = array_handler.array2vtkCellArray(a, cells) self.assertEqual(id(cells), ident) arr = array_handler.vtk2array(cells.GetData()) self.assertEqual(numpy.sum(arr - z), 0) self.assertEqual(cells.GetNumberOfCells(), 4) # Make sure this resets the cell array and does not add to the # existing list! cells = array_handler.array2vtkCellArray(a, cells) self.assertEqual(cells.GetNumberOfCells(), 4) # Test Numeric array handling. N = 3 a = numpy.zeros((N,3), numpy.int) a[:,1] = 1 a[:,2] = 2 cells = array_handler.array2vtkCellArray(a) arr = array_handler.vtk2array(cells.GetData()) expect = numpy.array([3, 0, 1, 2]*3, numpy.int) self.assertEqual(numpy.alltrue(numpy.equal(arr, expect)), True) self.assertEqual(cells.GetNumberOfCells(), N) # Test if a list of Numeric arrays of different cell lengths works. l_a = [a[:,:1], a, a[:2,:2]] cells = array_handler.array2vtkCellArray(l_a) arr = array_handler.vtk2array(cells.GetData()) expect = numpy.array([1, 0]*3 + [3, 0, 1, 2]*3 + [2, 0,1]*2, numpy.int) self.assertEqual(numpy.alltrue(numpy.equal(arr, expect)), True) self.assertEqual(cells.GetNumberOfCells(), N*2 + 2) # This should not take a long while. This merely tests if a # million cells can be created rapidly. N = int(1e6) a = numpy.zeros((N,3), numpy.int) a[:,1] = 1 a[:,2] = 2 cells = array_handler.array2vtkCellArray(a) self.assertEqual(cells.GetNumberOfCells(), N) def test_arr2vtkPoints(self): """Test Numeric array to vtkPoints conversion.""" a = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]] p = array_handler.array2vtkPoints(a) self.assertEqual(p.GetPoint(0), (0.0, 0.0, 0.0)) self.assertEqual(p.GetPoint(1), (1.0, 1.0, 1.0)) p = vtk.vtkPoints() ident = id(p) p = array_handler.array2vtkPoints(numpy.array(a), p) self.assertEqual(p.GetPoint(0), (0.0, 0.0, 0.0)) self.assertEqual(p.GetPoint(1), (1.0, 1.0, 1.0)) self.assertEqual(id(p), ident) self.assertRaises(AssertionError, array_handler.array2vtkPoints, [0.0, 1.0]) self.assertRaises(AssertionError, array_handler.array2vtkPoints, [0.0, 1.0, 1.0]) def test_arr2vtkIdList(self): """Test array to vtkIdList conversion.""" a = [1, 2, 3, 4, 5] p = array_handler.array2vtkIdList(a) for i, j in enumerate(a): self.assertEqual(p.GetId(i), j) p = vtk.vtkIdList() ident = id(p) p = array_handler.array2vtkIdList(numpy.array(a), p) for i, j in enumerate(a): self.assertEqual(p.GetId(i), j) self.assertEqual(id(p), ident) self.assertRaises(AssertionError, array_handler.array2vtkIdList, [[1,2,3]]) def test_get_correct_sig(self): """Test multiple signature cases that have array arguments.""" obj = tvtk_base.TVTKBase(vtk.vtkIdTypeArray) sigs = [ None, [['vtkDataArray']], [['int', 'vtkIdList']], [['int', 'vtkPoints'], ['int', 'int']], [['int', 'vtkPoints'], ['int']], [['int'], ['int', 'vtkPoints']], [['int', 'vtkDataArray'], ['int', 'int']], [['int', 'vtkDataArray'], ['int', 'int']], [['vtkIdList', 'vtkCellArray'], ['int', 'vtkPoints'], ['int', 'vtkDataArray']], [['vtkIdList', 'vtkCellArray'], ['int', 'vtkPoints'], ['int', 'vtkDataArray']], [['vtkIdTypeArray', 'vtkCellArray'], ['int', 'vtkPoints'], ['int', 'vtkDataArray']], [['vtkIdTypeArray', 'vtkCellArray'], ['int', 'vtkPoints'], ['int', 'vtkDataArray']], [['vtkIdTypeArray', 'vtkCellArray'], ['int', 'vtkPoints'], ['int', ('float', 'float', 'float')]], ] args = [ [1], # No sig info. ['foo'], # One sig. [1], # One sig. [1], # Error [1], # Only one valid sig. [1,[1,1,1]], # Only one valid sig. [1, [1,1,1]], # Multiple valid sigs. [1,1], # No arrays! [1,1], # No match so returns None. [1, [1,1,1]], # ambiguous, pick first match. [numpy.array([1,1]), [1,1,1]], # Match! [obj, [2,1,2,3]], # TVTK array object, match. [[2,1,2,3], obj], # TVTK array object, match but has # wrong argument. Should be caught # by VTK. ] res = [ None, ['vtkDataArray'], ['int', 'vtkIdList'], TypeError, ['int'], ['int', 'vtkPoints'], ['int', 'vtkDataArray'], None, None, ['int', 'vtkPoints'], ['vtkIdTypeArray', 'vtkCellArray'], ['vtkIdTypeArray', 'vtkCellArray'], ['vtkIdTypeArray', 'vtkCellArray'], ] for i in range(len(sigs)): if res[i] is TypeError: self.assertRaises(res[i], array_handler.get_correct_sig, args[i], sigs[i]) else: s = array_handler.get_correct_sig(args[i], sigs[i]) #print s, res[i] self.assertEqual(s, res[i]) def test_deref_array(self): """Test if dereferencing array args works correctly.""" sigs = [[['vtkDataArray']], [['vtkFloatArray']], [['vtkCellArray']], [['vtkPoints']], [['int', 'vtkIdList']], [['int', ('float', 'float'), 'vtkDataArray']], [['Prop', 'int', 'vtkDataArray']], [['Points', ('float', 'float', 'float')]] ] args = [[[1,2,3]], [[0,0,0]], [[[1,2,3],[4,5,6]]], [[[0.,0.,0.], [1.,1.,1.]]], [1, [1,2,3]], [1, (0.0, 0.0), [1.0, 1.0, 1.0]], [Prop(), 1, numpy.array([1.0, 1.0, 1.0])], [[[1,2,3]], [1,2,3]] ] r = array_handler.deref_array(args[0], sigs[0]) self.assertEqual(mysum(array_handler.vtk2array(r[0]) -args[0]), 0) r = array_handler.deref_array(args[1], sigs[1]) self.assertEqual(mysum(array_handler.vtk2array(r[0]) - args[1]), 0) r = array_handler.deref_array(args[2], sigs[2]) self.assertEqual(r[0].GetNumberOfCells(), 2) r = array_handler.deref_array(args[3], sigs[3]) self.assertEqual(mysum(array_handler.vtk2array(r[0].GetData()) - numpy.array(args[3], 'f')), 0) r = array_handler.deref_array(args[4], sigs[4]) self.assertEqual(r[0], 1) self.assertEqual(r[1].__class__.__name__, 'vtkIdList') r = array_handler.deref_array(args[5], sigs[5]) self.assertEqual(r[0], 1) self.assertEqual(r[1], (0.0, 0.0)) self.assertEqual(mysum(array_handler.vtk2array(r[2]) -args[5][2]), 0) r = array_handler.deref_array(args[6], sigs[6]) self.assertEqual(r[0].IsA('vtkProperty'), True) self.assertEqual(r[1], 1) self.assertEqual(mysum(array_handler.vtk2array(r[2]) -args[6][2]), 0) r = array_handler.deref_array(args[7], sigs[7]) def test_reference_to_array(self): """Does to_array return an existing array instead of a new copy.""" arr = numpy.arange(0.0, 10.0, 0.1) arr = numpy.reshape(arr, (25, 4)) vtk_arr = array_handler.array2vtk(arr) arr1 = array_handler.vtk2array(vtk_arr) # Now make sure these are using the same memory. arr[0][0] = 100.0 self.assertEqual(arr[0][0], arr1[0][0]) self.assertEqual(arr.shape, arr1.shape) def test_array_cache(self): """Test the ArrayCache class.""" cache = array_handler.ArrayCache() # Test if len works. self.assertEqual(len(cache), 0) arr = numpy.zeros(100, float) varr = vtk.vtkFloatArray() # test contains self.assertEqual(varr not in cache, True) cache.add(varr, arr) self.assertEqual(len(cache), 1) self.assertEqual(varr in cache, True) # Test the get method. self.assertEqual(cache.get(varr) is arr, True) # Test if the cache is cleared when the array is deleted. del varr self.assertEqual(len(cache), 0) def test_id_array(self): """Test if a vtkIdTypeArray is converted correctly.""" arr = vtk.vtkIdTypeArray() arr.SetNumberOfTuples(10) for i in range(10): arr.SetValue(i, i) np = array_handler.vtk2array(arr) self.assertEqual(numpy.all(np == range(10)), True) if __name__ == "__main__": unittest.main() mayavi-4.1.0/tvtk/tests/test_tvtk.py0000644000175100001440000004707611674464502020545 0ustar ischnellusers00000000000000"""Tests for tvtk objects. Some of these tests are copied from test_tvtk_base. This is just to make sure that the generated code works well. """ # Author: Prabhu Ramachandran # Copyright (c) 2004, Enthought, Inc. # License: BSD Style. import unittest import cPickle import weakref import vtk import new import sys import gc import numpy from tvtk import tvtk_base from tvtk.common import get_tvtk_name from traits.api import TraitError try: from tvtk.api import tvtk except ImportError: msg = """ You need to build the tvtk_classes.zip file to run this test. To generate tvtk_classes.zip you must do the following:: $ cd ../ # This is the enthought/tvtk directory $ python code_gen.py """ raise ImportError, msg # Only used for testing. from tvtk.tvtk_classes import tvtk_helper def mysum(arr): val = arr while type(val) == numpy.ndarray: val = numpy.sum(val) return val class TestTVTK(unittest.TestCase): def test_wrap_vtk(self): """Test if the wrap_vtk function works.""" o = vtk.vtkVolume() w = tvtk_helper.wrap_vtk(o) self.assertEqual(w.__class__.__name__, 'Volume') w1 = tvtk_helper.wrap_vtk(w) self.assertEqual(w, w1) del w1, w, o class A: pass a = A() w = tvtk_helper.wrap_vtk(a) self.assertEqual(a, w) def test_cache(self): """Test the caching of ancestor classes.""" # Shut off pesky warnings. vtk.vtkObject.GlobalWarningDisplayOff() o = tvtk.ImageFFT() vtk_ver = vtk.vtkVersion().GetVTKVersion() if vtk_ver[:3] in ['4.2', '4.4']: cached = ['ImageFourierFilter', 'ImageDecomposeFilter', 'ImageIterateFilter', 'ImageToImageFilter', 'ImageSource', 'Source', 'ProcessObject', 'Object', 'ObjectBase'] else: cached = ['ImageFourierFilter', 'ImageDecomposeFilter', 'ImageIterateFilter', 'ThreadedImageAlgorithm', 'ImageAlgorithm', 'Algorithm', 'Object', 'ObjectBase'] for i in cached: self.assertEqual(tvtk_helper._cache.has_key(i), True) vtk.vtkObject.GlobalWarningDisplayOn() def test_custom(self): """Test if custom modules can be imported.""" # Hack to simulate a module inside tvtk.custom. mod = new.module('xml_data_reader') class XMLDataReader: def f(self): return 'f' setattr(mod, 'XMLDataReader', XMLDataReader) sys.modules['tvtk.custom.xml_data_reader'] = mod # Now test if this is the one imported. r = tvtk.XMLDataReader() self.assertEqual(r.f(), 'f') self.assertEqual(r.__class__.__bases__, ()) # Clean up. del sys.modules['tvtk.custom.xml_data_reader'] def test_basic(self): """Test a simple tvtk pipeline.""" # If this works without any problems, we are ok. cs = tvtk.ConeSource() m = tvtk.PolyDataMapper() m.input = cs.output # This should work. m.input = cs.get_output() # This should also work. a = tvtk.Actor() a.mapper = m cs.resolution = 36 p = a.property p.representation = 'w' def test_do_change(self): """Test if VTK object changes when trait is changed.""" p = tvtk.Property() p.edge_visibility = not p.edge_visibility p.representation = 'p' p.interpolation = 'phong' p.opacity = 0.5 p.color = (0,1,0) p.diffuse_color = (1,1,1) p.specular_color = (1,1,0) for t, g in p._updateable_traits_: val = getattr(p._vtk_obj, g)() if t in ['representation', 'interpolation']: self.assertEqual(val, getattr(p, t + '_')) else: self.assertEqual(val, getattr(p, t)) def test_auto_update(self): """Test if traits are updated when the VTK object changes.""" p = tvtk.Property() obj = p._vtk_obj obj.SetEdgeVisibility(1) self.assertEqual(p.edge_visibility, 1) obj.SetOpacity(0.5) self.assertEqual(p.opacity, 0.5) obj.SetRepresentationToPoints() self.assertEqual(p.representation, 'points') val = (1.0, 1.0, 0.0) obj.SetColor(val) self.assertEqual(p.color, val) val = (1.0, 0.0, 0.0) obj.SetDiffuseColor(val) self.assertEqual(p.diffuse_color, val) val = (0.0, 1.0, 0.0) obj.SetSpecularColor(val) self.assertEqual(p.specular_color, val) def test_obj_del(self): """Test object deletion and reference cycles.""" p = tvtk.Property() p.representation = 0 ref = weakref.ref(p) del p self.assertEqual(ref(), None) def test_help_trait(self): """Test if the help attribute is correct.""" n = tvtk.PolyDataNormals() t = n.traits() test = t['splitting'].help != t['non_manifold_traversal'].help self.assertEqual(test, True) def test_object_cache(self): """Test if object cache works.""" cs = tvtk.ConeSource() hash1 = hash(cs) o = cs.output if hasattr(o, 'producer_port'): src = o.producer_port.producer else: src = o.source self.assertEqual(src, cs) self.assertEqual(hash1, hash(src)) del cs, src gc.collect() cs = tvtk.ConeSource() # Force cs to be gc'd! if hasattr(o, 'producer_port'): src = o.producer_port.producer else: src = o.source self.assertEqual(hash1 != hash(src), True) # Test for a bug with collections and the object cache. r = tvtk.Renderer() def _get_props(obj): if hasattr(obj, 'view_props'): return obj.view_props else: return obj.props p = _get_props(r) l1 = len(tvtk_base._object_cache) p1 = _get_props(r) del p1 l2 = len(tvtk_base._object_cache) self.assertEqual(l1, l2) def test_init_traits(self): """Test if the objects traits can be set in __init__.""" p = tvtk.Property(opacity=0.1, color=(1,0,0), representation='p') self.assertEqual(p.opacity, 0.1) self.assertEqual(p.color, (1.0, 0.0, 0.0)) self.assertEqual(p.representation, 'points') # Test case where the object traits are wrong. self.assertRaises(TraitError, tvtk.Property, foo='bar') cs = tvtk.ConeSource(radius=0.1, height=0.5, resolution=32) self.assertEqual(cs.radius, 0.1) self.assertEqual(cs.height, 0.5) self.assertEqual(cs.resolution, 32) # Test case where the object traits are wrong. self.assertRaises(TraitError, tvtk.ConeSource, foo=1) def test_matrix4x4(self): """Test if Matrix4x4 works nicely.""" m = tvtk.Matrix4x4() [m.set_element(i, j, i*4 +j) for i in range(4) for j in range(4)] s = cPickle.dumps(m) del m m = cPickle.loads(s) for i in range(4): for j in range(4): self.assertEqual(m.get_element(i, j), i*4 + j) # Test the from/to_array functions. a = numpy.array(range(16), dtype=float) a.shape = 4,4 m = tvtk.Matrix4x4() m.from_array(a) b = m.to_array() self.assertEqual(numpy.allclose(a, b), True) def test_property(self): """Test if Property's color works ok in all circumstances.""" p = tvtk.Property() val = (0., 1., 0.) p.color = val p.specular = 1.0 self.assertEqual(p.specular_color, val) self.assertEqual(p.diffuse_color, val) self.assertEqual(p.ambient_color, val) sc = (1., 0., 1.) p.specular_color = sc self.assertEqual(p.specular_color, sc) self.assertEqual(p.diffuse_color, val) self.assertEqual(p.ambient_color, val) self.assertEqual(p.color, (0.5, 0.5, 0.5)) # Test pickling. s = cPickle.dumps(p) del p p = cPickle.loads(s) self.assertEqual(p.specular_color, sc) self.assertEqual(p.diffuse_color, val) self.assertEqual(p.ambient_color, val) self.assertEqual(p.color, (0.5, 0.5, 0.5)) def test_collection(self): """Test if Collection objects work nicely.""" ac = tvtk.ActorCollection() self.assertEqual(len(ac), 0) self.assertRaises(IndexError, ac.__getitem__, 0) a_list = [] a = tvtk.Actor() a_list.append(a) ac.append(a) self.assertRaises(TypeError, ac.__getitem__, 's') self.assertEqual(len(ac), 1) a = tvtk.Actor() a_list.append(a) ac.append(a) self.assertEqual(len(ac), 2) # Test iterator nature. for i, j in zip(ac, a_list): self.assertEqual(i._vtk_obj, j._vtk_obj) for i, j in enumerate(ac): self.assertEqual(a_list[i]._vtk_obj, j._vtk_obj) # Test __setitem__. ac[0] = a_list[1] ac[1] = a_list[0] self.assertEqual(ac[0]._vtk_obj, a_list[1]._vtk_obj) self.assertEqual(ac[1]._vtk_obj, a_list[0]._vtk_obj) self.assertRaises(TypeError, ac.__setitem__, 's', a_list[1]) # Test __delitem__. del ac[-2] self.assertEqual(ac[0]._vtk_obj, a_list[0]._vtk_obj) self.assertEqual(len(ac), 1) self.assertRaises(TypeError, ac.__delitem__, 1.414) del ac[0] self.assertEqual(len(ac), 0) # Test __repr__. self.assertEqual(repr(ac), '[]') # test extend. ac.extend(a_list) self.assertEqual(len(ac), 2) for i, j in enumerate(ac): self.assertEqual(a_list[i]._vtk_obj, j._vtk_obj) # Test the prop collection. pc = tvtk.PropCollection() a = tvtk.Actor() pc.append(a) self.assertEqual(pc[0], a) for i in pc: self.assertEqual(i, a) def test_set_scalars(self): """Test if SetScalars works without a segfault.""" mesh = tvtk.PolyData() sc = tvtk.FloatArray() # If this does not segfault, we are OK. mesh.point_data.scalars = sc def test_data_array(self): """Test if vtkDataArrays behave in a Pythonic fashion.""" # Check a 3D array. f = tvtk.FloatArray() a = numpy.array([[0.,0,0],[1,1,1]]) f.from_array(a) self.assertEqual(f.number_of_components, 3) self.assertEqual(f.number_of_tuples, 2) self.assertEqual(mysum(f.to_array() - a), 0) for i, j in zip(a, f): self.assertEqual(mysum(i-j), 0.0) self.assertEqual(f[0], (0.0, 0.0, 0.0)) self.assertEqual(f[-1], (1.,1.,1.)) self.assertEqual(repr(f), '[(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)]') f.append((2,2,2)) f.extend([[3,3,3], [4,4,4]]) self.assertEqual(len(f), 5) self.assertEqual(f.number_of_components, 3) self.assertEqual(f.number_of_tuples, 5) f[1] = [-1,-1,-1] self.assertEqual(f[1], (-1.0, -1.0, -1.0)) self.assertRaises(IndexError, f.__getitem__, 100) self.assertRaises(IndexError, f.__setitem__, 100, 100) # Check a 5D arrray a = numpy.array([[0.,0,0, 0, 0],[1,1,1, 1, 1]]) f.from_array(a) self.assertEqual(mysum(f.to_array()- a), 0.0) for i, j in zip(a, f): self.assertEqual(mysum(i - j), 0.0) self.assertEqual(f[0], (0.0, 0.0, 0.0, 0.0, 0.0)) self.assertEqual(f[-1], (1.,1.,1., 1., 1.)) self.assertEqual(repr(f), '[(0.0, 0.0, 0.0, 0.0, 0.0), (1.0, 1.0, 1.0, 1.0, 1.0)]') f.append((2,2,2,2,2)) f.extend([[3,3,3,3,3], [4,4,4,4,4]]) self.assertEqual(len(f), 5) self.assertEqual(f.number_of_components, 5) self.assertEqual(f.number_of_tuples, 5) self.assertEqual(f[-1], (4., 4.,4.,4.,4.)) f[1] = [-1,-1,-1, -1,-1] self.assertEqual(f[1], (-1.0, -1.0, -1.0,-1.0, -1.0)) self.assertRaises(IndexError, f.__getitem__, 100) self.assertRaises(IndexError, f.__setitem__, 100, 100) def test_points(self): """Test if vtkPoints behaves in a Pythonic fashion.""" f = tvtk.Points() a = numpy.array([[0.,0,0],[1,1,1]]) f.from_array(a) self.assertEqual(mysum(f.to_array() - a), 0) for i, j in zip(a, f): self.assertEqual(mysum(i - j), 0) a[0,0] = 1.0 # Should change the VTK data! # Make sure that everything works even when the original array # is deleted. del a self.assertEqual(f[0], (1.0, 0.0, 0.0)) self.assertEqual(f[-1], (1.,1.,1.)) self.assertEqual(repr(f), '[(1.0, 0.0, 0.0), (1.0, 1.0, 1.0)]') f.append((2,2,2)) f.extend([[3,3,3], [4,4,4]]) self.assertEqual(len(f), 5) f[1] = [-1,-1,-1] self.assertEqual(f[1], (-1.0, -1.0, -1.0)) self.assertRaises(IndexError, f.__getitem__, 100) self.assertRaises(IndexError, f.__setitem__, 100, 100) def test_idlist(self): """Test if vtkIdList behaves in a Pythonic fashion.""" f = tvtk.IdList() a = numpy.array([0, 1, 2, 3]) f.from_array(a) for i, j in zip(a, f): self.assertEqual(i, j) self.assertEqual(f[-1], 3) self.assertEqual(f[0], 0) if type(f[0]) is long: self.assertEqual(repr(f), '[0L, 1L, 2L, 3L]') else: self.assertEqual(repr(f), '[0, 1, 2, 3]') f.append(4) f.extend([5, 6]) self.assertEqual(len(f), 7) f[1] = -1 self.assertEqual(f[1], -1) self.assertRaises(IndexError, f.__getitem__, 100) self.assertRaises(IndexError, f.__setitem__, 100, 100) def test_array_conversion(self): """Test if Numeric/VTK array conversion works.""" # This is only a simple test. data = numpy.array([[0,0,0,10], [1,0,0,20], [0,1,0,20], [0,0,1,30]], 'f') triangles = numpy.array([[0,1,3], [0,3,2], [1,2,3], [0,2,1]]) points = data[:,:3] temperature = data[:,-1] mesh = tvtk.PolyData() mesh.points = points mesh.polys = triangles mesh.point_data.scalars = temperature # Test if a normal float array also works. temp = tvtk.FloatArray() temp.from_array(temperature) mesh.point_data.scalars = temp def test_append_poly_data_input(self): """Test if AppendPolyData has its get_input wrapped right.""" a = tvtk.AppendPolyData() self.assertEqual(hasattr(a, 'get_input'), True) self.assertEqual(a.input, None) def test_property_change_notification(self): """Test if changes to properties generate notification events.""" # Create a dummy class to test with. class Junk: def f(self, obj, name, old, new): self.data = obj, name, old, new z = Junk() cs = tvtk.ConeSource() m = tvtk.PolyDataMapper() m.on_trait_change(z.f, 'input') m.input = cs.output self.assertEqual(z.data, (m, 'input', None, cs.output)) m.input = None self.assertEqual(z.data, (m, 'input', cs.output, None)) m.on_trait_change(z.f, 'input', remove=True) m.input = cs.output a = tvtk.Actor() a.on_trait_change(z.f, 'mapper') a.on_trait_change(z.f, 'property') a.mapper = m self.assertEqual(z.data, (a, 'mapper', None, m)) old = a.property new = tvtk.Property() a.property = new self.assertEqual(z.data, (a, 'property', old, new)) # Check if property notification occurs on add_input/remove_input a = tvtk.AppendPolyData() a.on_trait_change(z.f, 'input') pd = tvtk.PolyData() a.add_input(pd) old, new = None, pd self.assertEqual(z.data, (a, 'input', old, new)) a.remove_input(pd) old, new = pd, None self.assertEqual(z.data, (a, 'input', old, new)) a.remove_all_inputs() old, new = None, None self.assertEqual(z.data, (a, 'input', old, new)) def test_tuple_array_handling(self): """Test if methods can take any sequence rather than only tuples.""" sg = tvtk.StructuredGridGeometryFilter() # setting a bogus value triggers an error since VTK assumes # that we want the extent set in the passed object. If we use # an Array type instead of a Tuple then we can pass in # a list and it should work OK. sg.extent = [0,-1, 0,-1, 0,-1] def test_information_keys(self): """Test if vtk information objects can be created.""" s = tvtk.StructuredPoints() x = s.FIELD_ARRAY_TYPE() y = tvtk.Information() x.get(y) def test_parent_child_bounds(self): """CubeAxesActor2D's bounds should be writable.""" c = tvtk.CubeAxesActor2D() c.bounds = (0,1,0,1,0,1) def test_parent_child_input(self): """Case where parent has GetInput and child SetInput.""" vm = tvtk.VolumeTextureMapper2D() # In this case if the wrapping is not done right, the input # trait is made read-only which is a bug. We set the input # below to test this. vm.input = tvtk.ImageData() spw = tvtk.StructuredPointsWriter() spw.input = None def test_image_data_scalar_type(self): "Does ImageData support all scalar types?" img = tvtk.ImageData() # There are 22 scalar types in VTK-5.2. We should be able to # use them all. for i in range(0, 22): img.scalar_type = i def test_null_string_wrapper(self): "Check if a null string default is wrapped as a String trait." cap = tvtk.CaptionActor2D() self.assertEqual(('caption', 'GetCaption') in cap._updateable_traits_, True) self.assertEqual('caption' in cap._full_traitnames_list_, True) # This separates out any tests for the entire module that would affect # the functioning of the other tests. class TestTVTKModule(unittest.TestCase): def test_all_instantiable(self): """Test if all the TVTK classes are instantiable.""" # This is a comprehensive test that instantiates every single # non-abstract tvtk class. This takes a while. ok = True # Turn off VTK warnings. vtk.vtkObject.GlobalWarningDisplayOff() for name in dir(vtk): klass = getattr(vtk, name) if hasattr(klass, '__bases__') \ and not issubclass(klass, object): try: obj = klass() except TypeError: # These classes are abstract and can't/shouldn't # be instantiated. pass else: t_name = get_tvtk_name(name) skip = ['ObjectBase'] if t_name not in skip: k = getattr(tvtk, t_name) try: obj = k() except TraitError, msg: print "class:", t_name, msg ok = False # Now clear out the cache so other tests can run. tvtk_helper._cache.clear() # Turn on warnings. vtk.vtkObject.GlobalWarningDisplayOn() # Now raise an error if things were not OK. if not ok: raise TraitError, \ "Errors occured during this test, see printed messages." if __name__ == "__main__": unittest.main() mayavi-4.1.0/tvtk/tests/test_indenter.py0000644000175100001440000002202111674464502021344 0ustar ischnellusers00000000000000"""Tests for indenter.py.""" # Author: Prabhu Ramachandran # License: BSD style # Copyright (c) 2004, Enthought, Inc. import unittest import cStringIO from tvtk import indenter class TestIndent(unittest.TestCase): def test_basic(self): """Simple tests for indenter.""" id = indenter.Indent() self.assertEqual(str(id), '') id.incr() self.assertEqual(str(id), ' ') id.incr() self.assertEqual(str(id), ' ') id.decr() self.assertEqual(str(id), ' ') id.decr() self.assertEqual(str(id), '') id.incr(); id.incr() id.reset() self.assertEqual(str(id), '') def test_format(self): """Tests if formatting works ok.""" id = indenter.Indent() id.incr() # test one liner with trailing newlines txt = """class foo:\n\n \n \n""" t1 = id.format(txt) self.assertEqual(t1, ' class foo:\n') # test one liner with no trailing newline. txt = """class foo:""" t1 = id.format(txt) self.assertEqual(t1, ' class foo:\n') # test multi-line text. txt = """print "hi!" if name == 'hi': print "hi, hi!" """ res = """ print "hi!"\n if name == 'hi':\n print "hi, hi!"\n""" self.assertEqual(id.format(txt), res) txt = """ class Foo: def __init__(self): pass def _get_a(self): return self._a""" res = """ class Foo: def __init__(self): pass def _get_a(self): return self._a""" + '\n' self.assertEqual(id.format(txt), res) class TestVTKDocMassager(unittest.TestCase): def test_doc_massage(self): """Test massage method.""" doc = "This is a test. All VTK classes and vtk classes\n"\ "are named like this: vtkActor, vtkLODProperty,\n"\ "vtkXMLDataReader, vtk3DSImporter etc. The methods \n"\ "of a VTK object are like GetData, GetOutput, \n"\ "SetRepresentationToWireframe. Ivars are named like\n"\ "SpecularColor, Write3DPropsAsRasterImage etc." ret = "This is a test. All VTK classes and vtk classes\n"\ "are named like this: Actor, LODProperty,\n"\ "XMLDataReader, ThreeDSImporter etc. The methods \n"\ "of a VTK object are like get_data, get_output, \n"\ "set_representation_to_wireframe. Ivars are named like\n"\ "specular_color, write3d_props_as_raster_image etc." dm = indenter.VTKDocMassager() self.assertEqual(dm.massage(doc), ret) def test_rename_class(self): """Test if VTK classes are renamed correctly.""" dm = indenter.VTKDocMassager() t = 'vtkFooBar vtkXMLDataReader vtk3DSReader vtk2000Bug' r = dm._rename_class(t) correct = 'FooBar XMLDataReader ThreeDSReader Two000Bug' self.assertEqual(r, correct) def test_remove_sig(self): """Test if function signature is removed correctly.""" dm = indenter.VTKDocMassager() t = 'V.GetOutput(int) -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput (int idx);\n'\ 'V.GetOutput() -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput ();\n\n'\ ' Set/Get the output of this reader.\n' r = dm._remove_sig(t) correct = ' Set/Get the output of this reader.\n' self.assertEqual(r, correct) t = 'V.GetOutput(int) -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput (int idx);\n'\ 'V.GetOutput() -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput ();\n\n' r = dm._remove_sig(t) correct = '' self.assertEqual(r, correct) def test_class_doc(self): """Test if class docs are generated correctly.""" dm = indenter.VTKDocMassager() indent = indenter.Indent() out = cStringIO.StringIO() doc = "vtkLODProperty, vtkXMLDataReader, vtk3DSImporter\n"\ "SetRepresentationToWireframe, Write3DPropsAsRasterImage" dm.write_class_doc(doc, out, indent) out.seek(0) ret = out.read() correct = ''' """ LODProperty, XMLDataReader, ThreeDSImporter set_representation_to_wireframe, write3d_props_as_raster_image """\n''' #print ret #print correct self.assertEqual(ret, correct) # Test empty doc out = cStringIO.StringIO() doc = "" dm.write_class_doc(doc, out, indent) out.seek(0) ret = out.read() self.assertEqual(ret, ' """\n \n """\n') def test_trait_doc(self): """Test if trait docs are generated correctly.""" dm = indenter.VTKDocMassager() indent = indenter.Indent() out = cStringIO.StringIO() doc = 'V.GetOutput(int) -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput (int idx);\n'\ 'V.GetOutput() -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput ();\n\n'\ 'vtkLODProperty, vtkXMLDataReader, vtk3DSImporter\n'\ 'SetRepresentationToWireframe, Write3DPropsAsRasterImage' dm.write_trait_doc(doc, out, indent) out.seek(0) ret = out.read() correct = ''' """ LODProperty, XMLDataReader, ThreeDSImporter set_representation_to_wireframe, write3d_props_as_raster_image """\n''' #print ret #print correct self.assertEqual(ret, correct) # Test empty doc. out = cStringIO.StringIO() doc = 'V.GetOutput(int) -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput (int idx);\n'\ 'V.GetOutput() -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput ();\n\n' dm.write_trait_doc(doc, out, indent) out.seek(0) ret = out.read() self.assertEqual(ret, ' """\n \n """\n') def test_method_doc(self): """Test if method docs are generated correctly.""" dm = indenter.VTKDocMassager() indent = indenter.Indent() out = cStringIO.StringIO() doc = 'V.GetOutput(int) -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput (int idx);\n'\ 'V.GetOutput() -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput ();\n\n'\ 'vtkLODProperty, vtkXMLDataReader, vtk3DSImporter\n'\ 'SetRepresentationToWireframe, Write3DPropsAsRasterImage' dm.write_method_doc(doc, out, indent) out.seek(0) ret = out.read() correct = ''' """ V.get_output(int) -> StructuredPoints V.get_output() -> StructuredPoints LODProperty, XMLDataReader, ThreeDSImporter set_representation_to_wireframe, write3d_props_as_raster_image """\n''' #print ret #print correct self.assertEqual(ret, correct) # Test empty doc. out = cStringIO.StringIO() doc = 'V.GetOutput(int) -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput (int idx);\n'\ 'V.GetOutput() -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput ();\n\n' dm.write_method_doc(doc, out, indent) out.seek(0) ret = out.read() correct = ''' """ V.get_output(int) -> StructuredPoints V.get_output() -> StructuredPoints """\n''' #print ret #print correct self.assertEqual(ret, correct) def test_get_method_doc(self): """Test if get_method_doc works correctly.""" dm = indenter.VTKDocMassager() doc = 'V.GetOutput(int) -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput (int idx);\n'\ 'V.GetOutput() -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput ();\n\n'\ 'vtkLODProperty, vtkXMLDataReader, vtk3DSImporter\n'\ 'SetRepresentationToWireframe, Write3DPropsAsRasterImage' ret = dm.get_method_doc(doc) correct = 'V.get_output(int) -> StructuredPoints\n'\ 'V.get_output() -> StructuredPoints\n\n'\ 'LODProperty, XMLDataReader, ThreeDSImporter\n'\ 'set_representation_to_wireframe, '\ 'write3d_props_as_raster_image' #print ret #print correct self.assertEqual(ret, correct) # Test empty doc (only signature exists). doc = 'V.GetOutput(int) -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput (int idx);\n'\ 'V.GetOutput() -> vtkStructuredPoints\n'\ 'C++: vtkStructuredPoints *GetOutput ();\n\n' ret = dm.get_method_doc(doc) correct = 'V.get_output(int) -> StructuredPoints\n'\ 'V.get_output() -> StructuredPoints\n' self.assertEqual(ret, correct) if __name__ == "__main__": unittest.main() mayavi-4.1.0/tvtk/tests/test_tvtk_base.py0000644000175100001440000002376711674464502021540 0ustar ischnellusers00000000000000"""Tests for tvtk_base.py. These only test the basic functionality of the code in tvtk_base. More tests for TVTK objects should be in test_tvtk.py. """ # Author: Prabhu Ramachandran # Copyright (c) 2004, Enthought, Inc. # License: BSD Style. import unittest import cPickle import weakref import vtk import gc from traits import api as traits from tvtk import tvtk_base from tvtk.common import get_tvtk_name, camel2enthought # An elementary class based on vtkProperty that is used only for # testing. class Prop(tvtk_base.TVTKBase): def __init__(self, obj=None, update=1, **traits): tvtk_base.TVTKBase.__init__(self, vtk.vtkProperty, obj, update, **traits) edge_visibility = tvtk_base.false_bool_trait def _edge_visibility_changed(self, old_val, new_val): self._do_change(self._vtk_obj.SetEdgeVisibility, self.edge_visibility_) representation = traits.Trait('surface', tvtk_base.TraitRevPrefixMap({'points': 0, 'wireframe': 1, 'surface': 2})) def _representation_changed(self, old_val, new_val): self._do_change(self._vtk_obj.SetRepresentation, self.representation_) opacity = traits.Trait(1.0, traits.Range(0.0, 1.0)) def _opacity_changed(self, old_val, new_val): self._do_change(self._vtk_obj.SetOpacity, self.opacity) specular_color = tvtk_base.vtk_color_trait((1.0, 1.0, 1.0)) def _specular_color_changed(self, old_val, new_val): self._do_change(self._vtk_obj.SetSpecularColor, self.specular_color, 1) diffuse_color = tvtk_base.vtk_color_trait((1.0, 1.0, 1.0)) def _diffuse_color_changed(self, old_val, new_val): self._do_change(self._vtk_obj.SetDiffuseColor, self.diffuse_color, 1) color = tvtk_base.vtk_color_trait((1.0, 1.0, 1.0)) def _color_changed(self, old_val, new_val): self._do_change(self._vtk_obj.SetColor, self.color) _updateable_traits_ = (('edge_visibility', 'GetEdgeVisibility'), ('opacity', 'GetOpacity'), ('specular_color', 'GetSpecularColor'), ('color', 'GetColor'), ('diffuse_color', 'GetDiffuseColor'), ('representation', 'GetRepresentation')) class TestTVTKBase(unittest.TestCase): def test_tvtk_name(self): """Test VTK to TVTK class name conversion.""" v_name = ['vtkFooBar', 'vtkXMLDataReader', 'vtk3DSReader', 'vtk2000Bug'] t_name = ['FooBar', 'XMLDataReader', 'ThreeDSReader', 'Two000Bug'] for i, vn in enumerate(v_name): tn = get_tvtk_name(vn) self.assertEqual(tn, t_name[i]) num = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine'] for i in range(10): vn = 'vtk%dA'%i tn = get_tvtk_name(vn) self.assertEqual(tn, '%sA'%num[i]) def test_camel2enthought(self): """Test CamelCase to Enthought style name conversion.""" v_name = ['GetFooBar', 'GetOBBTree', 'XMLDataReader', 'GetFooXML', 'HTMLIsSGML', '_SetMe', '_XYZTest', 'Actor2D', 'Actor3D', 'Actor6D', 'PLOT3DReader', 'Actor61Dimension', 'GL2PSExporter', 'Volume16Reader'] t_name = ['get_foo_bar', 'get_obb_tree', 'xml_data_reader', 'get_foo_xml', 'html_is_sgml', '_set_me', '_xyz_test', 'actor2d', 'actor3d', 'actor6_d', 'plot3d_reader', 'actor61_dimension', 'gl2ps_exporter', 'volume16_reader'] for i, vn in enumerate(v_name): tn = camel2enthought(vn) self.assertEqual(tn, t_name[i]) def test_do_change(self): """Test if VTK object changes when trait is changed.""" p = Prop() p.edge_visibility = not p.edge_visibility p.representation = 'p' p.opacity = 0.5 p.color = (0,1,0) p.diffuse_color = (1,1,1) p.specular_color = (1,1,0) for t, g in p._updateable_traits_: val = getattr(p._vtk_obj, g)() if t == 'representation': self.assertEqual(val, getattr(p, t + '_')) else: self.assertEqual(val, getattr(p, t)) def test_auto_update(self): """Test trait updation when the VTK object changes.""" p = Prop() obj = p._vtk_obj obj.SetEdgeVisibility(1) self.assertEqual(p.edge_visibility, 1) obj.SetOpacity(0.5) self.assertEqual(p.opacity, 0.5) obj.SetRepresentationToPoints() self.assertEqual(p.representation, 'points') val = (1.0, 1.0, 0.0) obj.SetColor(val) self.assertEqual(p.color, val) val = (1.0, 0.0, 0.0) obj.SetDiffuseColor(val) self.assertEqual(p.diffuse_color, val) val = (0.0, 1.0, 0.0) obj.SetSpecularColor(val) self.assertEqual(p.specular_color, val) def test_setup_teardown_observers(self): """If setup_observers and teardown_observers work correctly.""" p = Prop() # Turn off the observers. p.teardown_observers() obj = p._vtk_obj obj.SetEdgeVisibility(1) self.assertEqual(p.edge_visibility, 0) obj.SetOpacity(0.5) self.assertEqual(p.opacity, 1.0) obj.SetRepresentationToPoints() self.assertEqual(p.representation, 'surface') # Setup the observers again. p.update_traits() p.setup_observers() self.assertEqual(p.edge_visibility, 1) self.assertEqual(p.opacity, 0.5) self.assertEqual(p.representation, 'points') obj.SetEdgeVisibility(0) self.assertEqual(p.edge_visibility, 0) obj.SetOpacity(1.0) self.assertEqual(p.opacity, 1.0) obj.SetRepresentationToSurface() self.assertEqual(p.representation, 'surface') def test_pickle(self): """Test if pickling works.""" p = Prop() p.edge_visibility = 1 p.representation = 'p' p.opacity = 0.5 p.color = (0,1,0) p.diffuse_color = (0,1,1) p.specular_color = (1,1,0) s = cPickle.dumps(p) del p p = cPickle.loads(s) self.assertEqual(p.edge_visibility, 1) self.assertEqual(p.opacity, 0.5) self.assertEqual(p.representation, 'points') val = (0.0, 1.0, 1.0) self.assertEqual(p.color, val) val = (0.0, 1.0, 1.0) self.assertEqual(p.diffuse_color, val) val = (1.0, 1.0, 0.0) self.assertEqual(p.specular_color, val) # test if pickling also works on an existing object. d = p.__getstate__() del p p = Prop() addr = p._vtk_obj.__this__ p.__setstate__(d) # Make sure its the same object. self.assertEqual(addr, p._vtk_obj.__this__) self.assertEqual(p.edge_visibility, 1) self.assertEqual(p.opacity, 0.5) self.assertEqual(p.representation, 'points') val = (0.0, 1.0, 1.0) self.assertEqual(p.color, val) def test_rev_prefix_map(self): """Test the reverse prefix map trait we use.""" p = Prop() p.representation = 'p' p.representation = 'wire' p.representation = 'points' p.representation = 2 self.assertEqual(p.representation, 'surface') self.assertRaises(traits.TraitError, setattr , p, 'representation', 'points1') self.assertRaises(traits.TraitError, setattr , p, 'representation', 'POINTS') def test_deref_vtk(self): """Test the `deref_vtk` function.""" p = Prop() o = tvtk_base.deref_vtk(p) self.assertEqual(o.IsA('vtkProperty'), True) o1 = tvtk_base.deref_vtk(o) self.assertEqual(o1.IsA('vtkProperty'), True) o = tvtk_base.deref_vtk(self) self.assertEqual(o.__class__.__name__, 'TestTVTKBase') def test_obj_del(self): """Test object deletion and reference cycles.""" p = Prop() p.representation = 0 ref = weakref.ref(p) del p self.assertEqual(ref(), None) def test_strict_traits(self): """Test if TVTK objects use strict traits.""" p = Prop() self.assertRaises(traits.TraitError, setattr, p, 'foo', 1) self.assertRaises(traits.TraitError, setattr, p, '_foo', 1) self.assertRaises(traits.TraitError, setattr, p, '__foo', 1) def test_init_traits(self): """Test if the objects traits can be set in __init__.""" p = Prop(opacity=0.1, color=(1,0,0), representation='p') self.assertEqual(p.opacity, 0.1) self.assertEqual(p.color, (1.0, 0.0, 0.0)) self.assertEqual(p.representation, 'points') # Test case where the object traits are wrong. self.assertRaises(traits.TraitError, Prop, foo='bar') def test_zz_object_cache(self): """Test if object cache works correctly.""" # HACK! The zz in the method name ensures that this is run # last. The reloading messes up some of the other tests # because the Prop classes base class is different. l1 = len(tvtk_base._object_cache) p = Prop() addr = p._vtk_obj.__this__ self.assertEqual(l1 + 1, len(tvtk_base._object_cache)) self.assertEqual(p, tvtk_base._object_cache.get(addr)) del p gc.collect() # Force collection. self.assertEqual(l1, len(tvtk_base._object_cache)) self.assertEqual(tvtk_base._object_cache.has_key(addr), False) # Check reload-safety. p = Prop() l1 = len(tvtk_base._object_cache) reload(tvtk_base) self.assertEqual(l1, len(tvtk_base._object_cache)) # Reloading causes havoc with nosetests based tests so we skip in # that case. Unittest will see the test just fine. test_zz_object_cache.__test__ = False if __name__ == "__main__": unittest.main() mayavi-4.1.0/tvtk/tests/test_array_ext.py0000644000175100001440000000400511674464502021534 0ustar ischnellusers00000000000000"""Unit tests for the array related extension code. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. import unittest import numpy from tvtk.array_handler import ID_TYPE_CODE from tvtk.array_ext import set_id_type_array class TestArrayExt(unittest.TestCase): def test_set_id_type_array(self): N = 5 a = numpy.zeros((N,4), ID_TYPE_CODE) a[:,1] = 1 a[:,2] = 2 a[:,3] = 3 def diff_arr(x, y): return numpy.sum(numpy.ravel(x) - numpy.ravel(y[:,1:])) # Test contiguous arrays. b = numpy.zeros((N,5), ID_TYPE_CODE) set_id_type_array(a, b) self.assertEqual(diff_arr(a, b), 0) # Test non-contiguous arrays. b = numpy.zeros((N,3), ID_TYPE_CODE) set_id_type_array(a[:,::2], b) self.assertEqual(diff_arr(a[:,::2], b), 0) # Test 1D array. b = numpy.zeros(N*5, ID_TYPE_CODE) set_id_type_array(a, b) self.assertEqual(diff_arr(a, numpy.reshape(b, (N,5))), 0) # Test assertions. d = a.astype('d') b = numpy.zeros((N, 5), ID_TYPE_CODE) self.assertRaises(AssertionError, set_id_type_array, d, b) # B should b contiguous. b = numpy.zeros((N, 10), ID_TYPE_CODE) self.assertRaises(AssertionError, set_id_type_array, a, b[:,::2]) self.assertRaises(AssertionError, set_id_type_array, a[0], b) # Test size check assertion. b = numpy.zeros((N, 4), ID_TYPE_CODE) self.assertRaises(AssertionError, set_id_type_array, a, b) b = numpy.zeros(N*6, ID_TYPE_CODE) self.assertRaises(AssertionError, set_id_type_array, a, b) # This should work! set_id_type_array(a, b[:N*5]) self.assertEqual(diff_arr(a, numpy.reshape(b[:N*5], (N,5))), 0) if __name__ == "__main__": unittest.main() mayavi-4.1.0/tvtk/tests/test_vtk_parser.py0000644000175100001440000002521111674464502021720 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # License: BSD style # Copyright (c) 2004, Enthought, Inc. """Tests for vtk_parser.py. Note that the `test_parse_all` parses every single class in VTK-Python. It organizes the methods and also tries to obtain the method signature for every method in every class. If this runs without crashing or raising any exceptions, then it shows that the vtk_parser will work for any VTK class. The test will show a few VTK error messages but they are usually harmless. """ import unittest from tvtk import vtk_parser import time # Only used when timing. import sys # Only used when debugging. import vtk # This is a little expensive to create so we cache it. _cache = vtk_parser.VTKMethodParser() class TestVTKParser(unittest.TestCase): def setUp(self): self.p = _cache def test_methods(self): """Check get_methods.""" p = self.p meths = p.get_methods(vtk.vtkFloatArray) # Check if special methods are removed. for m in meths: self.assertEqual((m.find('__') == -1), True) def test_parse(self): """Check if the methods are organized correctly.""" p = self.p # Simple case of a vtkObject. p.parse(vtk.vtkObject()) self.assertEqual(p.get_toggle_methods(), {'Debug': 0, 'GlobalWarningDisplay': 1}) self.assertEqual(p.get_state_methods(), {}) self.assertEqual(p.get_get_set_methods(), {'ReferenceCount': (1, None)}) self.assertEqual(p.get_get_methods(), ['GetMTime']) res = ['AddObserver', 'BreakOnError', 'HasObserver', 'InvokeEvent', 'IsA', 'Modified', 'NewInstance', 'Register', 'RemoveObserver', 'RemoveObservers', 'SafeDownCast', 'UnRegister', 'RemoveAllObservers'] for i in p.get_other_methods(): self.assertEqual(i in res, True) # Parse a fairly complex case of a vtkProperty with the same # parser object. p.parse(vtk.vtkProperty) self.assertEqual(p.toggle_meths, p.get_toggle_methods()) res = {'EdgeVisibility': 0, 'BackfaceCulling': 0, 'FrontfaceCulling': 0} if p.get_toggle_methods().has_key('Shading'): res['Shading'] = 0 result = p.get_toggle_methods() for key in res: self.assertEqual(key in result, True) self.assertEqual(result[key], res[key]) res = {'Interpolation': [['Gouraud', 1], ['Flat', 0], ['Gouraud', 1], ['Phong', 2]], 'Representation': [['Surface', 2], ['Points', 0], ['Surface', 2], ['Wireframe', 1]]} self.assertEqual(p.get_state_methods(), res) self.assertEqual(p.state_meths, p.get_state_methods()) obj = vtk.vtkProperty() res = {'Ambient': (0.0, (0.0, 1.0)), 'AmbientColor': ((1.0, 1.0, 1.0), None), 'Color': ((1.0, 1.0, 1.0), None), 'Diffuse': (1.0, (0.0, 1.0)), 'DiffuseColor': ((1.0, 1.0, 1.0), None), 'EdgeColor': ((1.0, 1.0, 1.0), None), 'LineStipplePattern': (65535, None), 'LineStippleRepeatFactor': (1, (1, vtk.VTK_LARGE_INTEGER)), 'LineWidth': (1.0, (0.0, vtk.VTK_LARGE_FLOAT)), 'Opacity': (1.0, (0.0, 1.0)), 'PointSize': (1.0, (0.0, vtk.VTK_LARGE_FLOAT)), 'ReferenceCount': (1, None), 'Specular': (0.0, (0.0, 1.0)), 'SpecularColor': ((1.0, 1.0, 1.0), None), 'SpecularPower': (1.0, (0.0, 100.0))} result = p.get_get_set_methods().keys() if hasattr(obj, 'GetTexture'): result.remove('Texture') self.assertEqual(sorted(res.keys()), sorted(result)) self.assertEqual(p.get_set_meths, p.get_get_set_methods()) for x in res: if res[x][1]: # This is necessary since the returned value is not # usually exactly the same as defined in the header file. default = getattr(obj, 'Get%s'%x)() val = getattr(obj, 'Get%sMinValue'%x)(), \ getattr(obj, 'Get%sMaxValue'%x)() self.assertEqual(p.get_get_set_methods()[x], (default, val)) if hasattr(obj, 'GetTexture'): expect = ['GetMaterial', 'GetMaterialName', 'GetNumberOfTextures', 'GetShaderProgram'] if hasattr(obj, 'GetMaterialName'): self.assertEqual(p.get_get_methods(), expect) else: expect.remove('GetMaterialName') self.assertEqual(p.get_get_methods(), expect) else: self.assertEqual(p.get_get_methods(), []) self.assertEqual(p.get_meths, p.get_get_methods()) res = ['BackfaceRender', 'DeepCopy', 'Render'] if hasattr(obj, 'GetTexture'): res = ['AddShaderVariable', 'BackfaceRender', 'DeepCopy', 'LoadMaterial', 'LoadMaterialFromString', 'ReleaseGraphicsResources', 'RemoveAllTextures', 'RemoveTexture', 'Render'] if hasattr(obj, 'PostRender'): res.append('PostRender') res.sort() self.assertEqual(p.get_other_methods(), res) self.assertEqual(p.other_meths, p.get_other_methods()) def test_parse_image_reslice(self): """Check if the vtkImageReslice is parsed correctly.""" p = self.p p.parse(vtk.vtkImageReslice) state_meths = p.get_state_methods() self.assertEqual('OutputSpacing' not in state_meths, True) self.assertEqual('OutputOrigin' not in state_meths, True) self.assertEqual('OutputExtent' not in state_meths, True) def test_method_signature(self): """Check if VTK method signatures are parsed correctly.""" p = self.p # Simple tests. o = vtk.vtkProperty() self.assertEqual([(['string'], None)], p.get_method_signature(o.GetClassName)) if hasattr(vtk, 'vtkArrayCoordinates'): self.assertEqual([([('float', 'float', 'float')], None), ([None], (['float', 'float', 'float'],)), ([None], ('float', 'float', 'float'))], p.get_method_signature(o.GetColor)) else: self.assertEqual([([('float', 'float', 'float')], None), ([None], (('float', 'float', 'float'),))], p.get_method_signature(o.GetColor)) if hasattr(vtk, 'vtkArrayCoordinates'): self.assertEqual([([None], ('float', 'float', 'float')), ([None], (['float', 'float', 'float'],))], p.get_method_signature(o.SetColor)) else: self.assertEqual([([None], ('float', 'float', 'float')), ([None], (('float', 'float', 'float'),))], p.get_method_signature(o.SetColor)) # Get VTK version to handle changed APIs. vtk_ver = vtk.vtkVersion().GetVTKVersion() # Test vtkObjects args. o = vtk.vtkContourFilter() sig = p.get_method_signature(o.SetInput) if len(sig) == 1: self.assertEqual([([None], ['vtkDataSet'])], sig) elif vtk_ver[:3] in ['4.2', '4.4']: self.assertEqual([([None], ['vtkDataObject']), ([None], ('int', 'vtkDataObject')), ([None], ['vtkDataSet']), ([None], ('int', 'vtkDataSet')) ], sig) elif vtk_ver[:2] == '5.' or vtk_ver[:3] == '4.5': self.assertEqual([([None], ['vtkDataObject']), ([None], ('int', 'vtkDataObject')), ], sig) self.assertEqual([(['vtkPolyData'], None), (['vtkPolyData'], ['int'])], p.get_method_signature(o.GetOutput)) # Test if function arguments work. self.assertEqual([(['int'], ('int', 'function'))], p.get_method_signature(o.AddObserver)) # This one's for completeness. self.assertEqual([([None], ['int'])], p.get_method_signature(o.RemoveObserver)) def test_special_non_state_methods(self): """Check exceptional cases that are not state methods.""" p = self.p p.parse(vtk.vtkDataObject) self.assert_('UpdateExtent' not in p.get_state_methods()) self.assert_('UpdateExtent' in p.get_get_set_methods()) p.parse(vtk.vtkImageImport) self.assert_('DataExtent' not in p.get_state_methods()) self.assert_('DataExtent' in p.get_get_set_methods()) def test_no_tree(self): """Check if parser is usable without the tree.""" p = vtk_parser.VTKMethodParser(use_tree=False) self.assertEqual(p.get_tree(), None) self.p = p self.test_methods() self.test_parse() self.test_method_signature() # Now check that it really works for abstract classes. # abstract classes that have state methods abs_class = [vtk.vtkDicer, vtk.vtkMapper, vtk.vtkScalarsToColors, vtk.vtkStreamer, vtk.vtkUnstructuredGridVolumeMapper, vtk.vtkVolumeMapper, vtk.vtkXMLWriter] for k in abs_class: p.parse(k) # Make sure we did get the state methods. self.assertEqual(len(p.get_state_methods()) > 0, True) for key, values in p.get_state_methods().items(): for val in values: # No state information is obtainable since no # class tree is created. self.assertEqual(val[1], None) def test_parse_all(self): """Check if all VTK classes are parseable.""" # This test is a tough one because every single class in the # VTK API is parsed. A few VTK error messages (not test # errors) might be seen on screen but these are normal. #t1 = time.clock() p = self.p for obj in dir(vtk): k = getattr(vtk, obj) ignore = ['mutable', 'exc', 'kits', 'util'] if hasattr(k, '__bases__') and obj not in ignore: #print k.__name__, #sys.stdout.flush() p.parse(k) for method in p.get_methods(k): #print method p.get_method_signature(getattr(k, method)) #print time.clock() - t1, 'seconds' if __name__ == "__main__": unittest.main() mayavi-4.1.0/tvtk/tests/__init__.py0000644000175100001440000000000011674464502020225 0ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/tests/test_wrapper_gen.py0000644000175100001440000000377211674464502022061 0ustar ischnellusers00000000000000"""Elementary tests for wrapper_gen.py. This test suite is not comprehensive because it is very hard to test wrapper_gen directly. """ # Author: Prabhu Ramachandran # Copyright (c) 2004, Prabhu Ramachandran, Enthought, Inc. # License: BSD Style. import unittest import vtk from tvtk import wrapper_gen _cache = wrapper_gen.WrapperGenerator() class TestWrapperGenerator(unittest.TestCase): def setUp(self): self.wg = _cache def test_find_type(self): wg = self.wg sigs = ['int', 'vtkOpenGLVolumeMapper', ('int', 'int', 'float', 'list'), ('int', 'vtkActor', 'vtkXMLReader'), ['vtkImageActor', 'vtkExporter'], ['int', 'vtkDataArray', 'vtkCellArray', 'vtkIdTypeArray'] ] expect = ['basic', 'vtk', 'basic', 'vtk', 'vtk', 'array'] for i, sig in enumerate(sigs): self.assertEqual(expect[i], wg._find_type(sig)) def test_sig_types(self): wg = self.wg meths = [vtk.vtkProperty.GetColor, vtk.vtkProperty.GetRepresentation, vtk.vtkStructuredPointsReader.GetOutput, vtk.vtkPolyData.SetPoints, vtk.vtkPolyData.SetPolys, vtk.vtkQuad.CellBoundary, vtk.vtkContourFilter.SetLocator ] expect = [('basic', 'basic'), ('basic', None), ('vtk', 'basic'), ('basic', 'array'), ('basic', 'array'), ('basic', 'array'), ('basic', 'vtk'), ] for i, meth in enumerate(meths): sig = wg.parser.get_method_signature(meth) self.assertEqual(expect[i], wg._find_sig_type(sig)) sig = [(['int'], ['int']), (['vtkStructuredPoints'], ['vtkFooClass'])] self.assertEqual(('vtk', 'vtk'), wg._find_sig_type(sig)) if __name__ == "__main__": unittest.main() mayavi-4.1.0/tvtk/tests/test_misc.py0000644000175100001440000000553611674464502020503 0ustar ischnellusers00000000000000""" Tests for enthought/tvtk/misc.py """ # Author: Prabhu Ramachandran # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. import unittest import tempfile import os.path import os from tvtk.api import tvtk, write_data class TestMisc(unittest.TestCase): def setUp(self): datasets = [tvtk.ImageData(), tvtk.StructuredPoints(), tvtk.RectilinearGrid(), tvtk.StructuredGrid(), tvtk.PolyData(), tvtk.UnstructuredGrid(), ] exts = ['.vti', '.vti', '.vtr', '.vts', '.vtp', '.vtu'] self.datasets = datasets self.exts = exts def test_write_data_xml_noext(self): "XML file writing without extensions" # Check if write_data writes out XML files with the correct # extension when none is specified. datasets = self.datasets exts = self.exts for d, ext in zip(datasets, exts): fh, fname = tempfile.mkstemp(ext) fbase = os.path.splitext(fname)[0] os.close(fh) os.remove(fname) write_data(d, fbase) self.assertEqual(os.path.exists(fname), True) os.remove(fname) def test_write_data_xml(self): "XML file writing with specified extension" datasets = self.datasets for d in datasets: fh, fname = tempfile.mkstemp('.xml') os.close(fh) os.remove(fname) self.assertEqual(os.path.exists(fname), False) write_data(d, fname) self.assertEqual(os.path.exists(fname), True) os.remove(fname) def test_write_data_xml_kwargs(self): "XML file writing with extra keyword arguments" datasets = self.datasets exts = self.exts for d, ext in zip(datasets, exts): fh, fname = tempfile.mkstemp(ext) fbase = os.path.splitext(fname)[0] os.close(fh) os.remove(fname) # Test if passing extra keyword args is supported. write_data(d, fbase, compressor=None, data_mode='ascii') self.assertEqual(os.path.exists(fname), True) os.remove(fname) def test_write_data_vtk(self): "Old-style VTK file writing with specified extension" datasets = self.datasets for d in datasets: fh, fname = tempfile.mkstemp('.vtk') os.close(fh) os.remove(fname) self.assertEqual(os.path.exists(fname), False) write_data(d, fname) self.assertEqual(os.path.exists(fname), True) r = tvtk.DataSetReader(file_name=fname) r.update() self.assertEqual(isinstance(r.output, d.__class__), True) os.remove(fname) if __name__ == '__main__': unittest.main() mayavi-4.1.0/tvtk/tests/test_class_tree.py0000644000175100001440000001042411674464502021664 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # License: BSD style # Copyright (c) 2004, Enthought, Inc. """Tests class_tree.py. Uses the vtk module to test the code. Also tests if the tree generation works for the __builtin__ module. """ import unittest from tvtk import class_tree import vtk import __builtin__ # This computation can be expensive, so we cache it. _cache = class_tree.ClassTree(vtk) _cache.create() def get_level(klass): """Gets the inheritance level of a given class.""" if not klass.__bases__: return 0 else: return max([get_level(b) for b in klass.__bases__]) + 1 class TestClassTree(unittest.TestCase): def setUp(self): self.t = _cache def test_basic_vtk(self): """Basic tests for the VTK module.""" t = self.t self.assertEqual(t.get_node('vtkObject').name, 'vtkObject') self.assertEqual(t.get_node('vtkObject').parents[0].name, 'vtkObjectBase') if (hasattr(vtk, 'vtkVector')): self.assertEqual(len(t.tree[0]), 11) names = [x.name for x in t.tree[0]] names.sort() expect = ['object', 'vtkColor3', 'vtkColor4', 'vtkDenseArray', 'vtkObjectBase', 'vtkRect', 'vtkSparseArray', 'vtkTypedArray', 'vtkVector', 'vtkVector2', 'vtkVector3'] self.assertEqual(names, expect) elif (hasattr(vtk, 'vtkArrayCoordinates') and issubclass(vtk.vtkArrayCoordinates, object)): self.assertEqual(len(t.tree[0]), 2) names = [x.name for x in t.tree[0]] names.sort() self.assertEqual(names, ['object', 'vtkObjectBase']) else: self.assertEqual(len(t.tree[0]), 1) self.assertEqual(t.tree[0][0].name, 'vtkObjectBase') def test_ancestors(self): """Check if get_ancestors is OK.""" # The parent child information is already tested so this test # needs to ensure that the method works for a few known # examples. # Simple VTK test. t = self.t n = t.get_node('vtkDataArray') x = vtk.vtkDataArray ancestors = [] while x.__name__ != 'vtkObjectBase': x = x.__bases__[0] ancestors.append(x.__name__) self.assertEqual([x.name for x in n.get_ancestors()], ancestors) # Simple __builtin__ test. t = class_tree.ClassTree(__builtin__) t.create() n = t.get_node('TabError') bases = ['IndentationError', 'SyntaxError', 'StandardError', 'Exception'] if len(Exception.__bases__) > 0: bases.extend(['BaseException', 'object']) self.assertEqual([x.name for x in n.get_ancestors()], bases) def test_parent_child(self): """Check if the node's parent and children are correct.""" t = self.t for node in t: n_class = t.get_class(node.name) base_names = [x.__name__ for x in n_class.__bases__] base_names.sort() parent_names = [x.name for x in node.parents] parent_names.sort() self.assertEqual(base_names, parent_names) for c in node.children: c_class = t.get_class(c.name) base_names = [x.__name__ for x in c_class.__bases__] self.assertEqual(node.name in base_names, True) def test_level(self): """Check the node levels.""" t = self.t for node in t: self.assertEqual(get_level(t.get_class(node.name)), node.level) def test_tree(self): """Check the tree structure.""" t = self.t n = sum([len(x) for x in t.tree]) self.assertEqual(n, len(t.nodes)) for level, nodes in enumerate(t.tree): for n in nodes: self.assertEqual(n.level, level) def test_builtin(self): """Check if tree structure for __builtin__ works.""" # This tests to see if the tree structure generation works for # the __builtin__ module. t = class_tree.ClassTree(__builtin__) t.create() self.t = t self.test_parent_child() self.test_level() self.test_tree() if __name__ == "__main__": unittest.main() mayavi-4.1.0/tvtk/vtk_parser.py0000644000175100001440000006163211674464502017526 0ustar ischnellusers00000000000000"""This module parses the VTK methods, obtains the argument and return type information, and organizes them. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2007, Enthought, Inc. # License: BSD Style. import re # Local imports (these are relative imports for a good reason). import class_tree import vtk_module as vtk class VTKMethodParser: """This class provides useful methods for parsing methods of a VTK class or instance. The class allows one to categorize the methods of the VTK class and also obtain the method signatures in a form that is easy to use. When the `parse` method is called, it in turn calls the `_organize_methods` method. This method organizes the VTK methods into different instance variables described in the following. `self.toggle_meths` contains a dictionary of all the boolean methods of the form On/Off. The dictionary keys are strings with the 's and the value of each item is the default value (0/1) of the item (the example below will clarify this). `self.state_meths` contains a dictionary which collects the SetTo type of methods. The key is the and the value is a list containing the different string 's and their corresponding mapped value. The first value in these is the default value of the . `self.get_set_meths` will contain a dictionary which collects all the methods of the form Set/Get that are not already specified in `self.toggle_meths` or `self.state_meths`. The default value of the Get is stored. If the value accepted by the method has a range (via the methods `GetMinValue` and `GetMaxValue`), then that range is computed and stored. `self.get_meths` stores the methods that are of the form `Get`. `self.other_meths` stores the remaining methods. The parsing is quite fast. Parsing every class in the VTK API takes a couple of seconds (on a Pentium III @ 450Mhz). Here is an example:: >>> import vtk >>> p = VTKMethodParser() >>> p.parse(vtk.vtkProperty) >>> print p.get_toggle_methods() {'EdgeVisibility': 0, 'BackfaceCulling': 0, 'FrontfaceCulling': 0} >>> print p.get_state_methods()['Representation'] [['Surface', 2], ['Points', 0], ['Surface', 2], ['Wireframe', 1]] >>> print p.get_get_set_methods()['Opacity'] (1.0, (0.0, 1.0)) >>> print p.get_get_methods() ['GetClassName'] >>> print p.get_other_methods()[:3] ['BackfaceRender', 'DeepCopy', 'IsA'] The class also provides a method called `get_method_signature` that obtains the Python method signature given the VTK method object. Here is an example:: >>> import vtk >>> p = VTKMethodParser() >>> o = vtk.vtkProperty >>> print p.get_method_signature(o.GetClassName) [(['string'], None)] >>> print p.get_method_signature(o.GetColor)[0] ([('float', 'float', 'float')], None) >>> print p.get_method_signature(o.GetColor)[1] ([None], (('float', 'float', 'float'),)) The `get_method_signature` is fairly efficient and obtaining the signature for every method in every class in the VTK API takes around 6 seconds (on a Pentium III @ 450Mhz). """ def __init__(self, use_tree=True): """Initializes the object. Parameters ---------- - use_tree : `bool` If True (default), use a ClassTree instance to obtain a concrete subclass for an abstract base class. This is used only to find the range and default values for some of the methods. If False, no ClassTree instance is created. This is optional because, creating a ClassTree is expensive. The parser functionality can be very useful even without the use of a ClassTree. For example, if one wants to save the state of a VTK object one only needs to know the names of the methods and not their default values, ranges etc. In that case using a parser should be cheap. """ # The ClassTree is needed to find an instantiable child class # for an abstract VTK parent class. This instance is used to # obtain the state values and the ranges of the arguments # accepted by the Get/Set methods that have a # Get{MaxValue,MinValue} method. if use_tree: self._tree = class_tree.ClassTree(vtk) self._tree.create() else: self._tree = None self._state_patn = re.compile('To[A-Z0-9]') self._initialize() ################################################################# # 'VTKMethodParser' interface. ################################################################# def parse(self, obj, no_warn=True): """Parse the methods for a given VTK object/class. Given a VTK class or object, this method parses the methods and orgaizes them into useful categories. The categories and their usage is documented in the documentation for the class. Parameters ---------- - obj : VTK class or instance - no_warn : `bool` (default: True) If True (default), it suppresses any warnings generated by the VTK object when parsing the methods. This is safe to use. """ if not hasattr(obj, '__bases__'): klass = obj.__class__ else: klass = obj methods = self.get_methods(klass) if no_warn: # Save warning setting and shut it off before parsing. warn = vtk.vtkObject.GetGlobalWarningDisplay() if klass.__name__ <> 'vtkObject': vtk.vtkObject.GlobalWarningDisplayOff() self._organize_methods(klass, methods) if no_warn: # Reset warning status. vtk.vtkObject.SetGlobalWarningDisplay(warn) def _get_parent_methods(self, klass): """Returns all the methods of the classes parents.""" methods = {} while len(klass.__bases__) > 0: klass = klass.__bases__[0] meths = dir(klass) d = methods.fromkeys(meths) methods.update(d) return methods.keys() def get_methods(self, klass): """Returns all the relevant methods of the given VTK class.""" methods = dir(klass)[:] if hasattr(klass, '__members__'): # Only VTK versions < 4.5 have these. for m in klass.__members__: methods.remove(m) # Ignore the parent methods. ignore = self._get_parent_methods(klass) # Skip some of the ignores. skip = ['GetInput', 'SetInput'] # Sometimes the child has only GetInput while the parent has # SetInput. if hasattr(klass, 'SetInput') and \ 'SetInput' not in methods and \ 'GetInput' in methods: methods.append('SetInput') # Get/set pairs that are overridden. Basically, if a parent # class has a 'GetThing' and the child overrides/has a # 'SetThing' (or vice-versa), then the removal of the parent # methods is wrong since the child changes the trait definition # which breaks things. We therefore do not remove any of the # Get/SetThings that are ignored due to them being in the # parent. However one has to be careful about cases where these are # really Toggle (ThingOn) or State (SetThingToThong) etc. methods and # in those cases we really should ignore the method. So in essence, # any Get/Set pair that is not a State or Toggle should be redefined. overrides = [] for m in methods: check = False if m.startswith('Get'): m1 = 'Set' + m[3:] check = True elif m.startswith('Set'): m1 = 'Get' + m[3:] check = True if check: if m1 in methods and (m1 in ignore or m in ignore): # Skips are stored as Set followed by Get. skip.extend(['Set' +m[3:], 'Get'+m[3:]]) for m in skip[:]: if m.startswith('Set'): base = m[3:] mg, ms = 'Get' + base, 'Set' + base m_st = 'Set' + base + 'To' m_t = base + 'Off' for method in methods: if m_st in method or m_t == method: skip.remove(ms) skip.remove(mg) break if 'GetViewProp' in methods and 'GetProp' in methods: ignore.extend(['GetProp', 'SetProp']) if 'GetViewProps' in methods and 'GetProps' in methods: ignore.extend(['GetProps', 'SetProps']) # Remove any deprecated traits. if 'GetScaledText' in methods and 'GetTextScaleMode' in methods: ignore.extend(['GetScaledText', 'SetScaledText', 'ScaledTextOn', 'ScaledTextOff']) # Now we can safely remove the methods. for m in methods[:]: if m in ignore and m not in skip: methods.remove(m) return methods def get_toggle_methods(self): """Returns a dictionary of the parsed On/Off methods along with the default value. """ return self.toggle_meths def get_state_methods(self): """Returns a dict of the parsed SetTo. The keys are the string with a list of the different strings along with their corresponding value (if obtainable). The first value is the default value of the state. """ return self.state_meths def get_get_set_methods(self): """Returns a dict of the parsed Get/Set methods. The keys of the dict are the strings and contain a two-tuple containing the default value (or None if it is not obtainable for some reason) and a pair of numbers specifying an acceptable range of values (or None if not obtainable). """ return self.get_set_meths def get_get_methods(self): """Return a list of parsed Get methods. All of these methods do NOT have a corresponding Set. """ return self.get_meths def get_other_methods(self): """Return list of all other methods, that are not categorizable. """ return self.other_meths def get_method_signature(self, method): """Returns information on the Python method signature given the VTK method. The doc string of the given method object to get the method signature. The method returns a list of tuples, each of which contains 2 items, the first is a list representing the return value the second represents the arguments to be passed to the function. If the method supports different return values and arguments, this function returns all of their signatures. Parameters ---------- - method : `method` A VTK method object. """ # Remove all the C++ function signatures. doc = method.__doc__ doc = doc[:doc.find('\n\n')] sig = [] c_sig = [] # The C++ signature in_sig = False in_c_sig = False counter = 0 for line in doc.split('\n'): if line.startswith('V.'): in_sig = True in_c_sig = False sig.append(line.strip()) elif line.startswith('C++:'): in_sig = False in_c_sig = True c_sig.append(line.strip()) counter += 1 elif in_sig: sig[counter] = sig[counter] + line.strip() elif in_c_sig: c_sig[counter-1] = c_sig[counter-1] + line.strip() # Remove the V. sig = [x.replace('V.' + method.__name__, '') for x in sig] c_sig = [x[x.find('('):] for x in c_sig] pat = re.compile(r'\b') # Split into [return_value, arguments] after processing them. tmp = list(sig) sig = [] for sig_idx, i in enumerate(tmp): # Split to get return values. x = i.split('->') # Strip each part. x = [y.strip() for y in x] if len(x) == 1: # No return value x = [None, x[0]] else: x.reverse() ret, arg = x # Remove leading and trailing parens for arguments. arg = arg[1:-1] if not arg: arg = None if arg and arg[-1] in [')', ']']: arg = arg + ',' # Check if we are able to parse all the arguments -- some # unstable versions of VTK have problems generating the # docstring and in this case we will try to use the C++ # docstring signature. n_arg = 0 arg_map = {'unsigned int': 'int', 'unsigned char': 'int', 'unsigned long': 'long', 'unsigned short': 'int'} if arg is not None and c_sig: n_arg = arg.count(',') + 1 # The carguments have parenthesis like: (int, int) carg = c_sig[sig_idx][1:-1].split(',') if n_arg > 0: args = [] if len(carg) == n_arg: for idx, x in enumerate(arg.split(',')): if len(x.strip()) == 0: carg_val = carg[idx].strip() if 'unsigned' in carg_val and \ carg_val in arg_map: args.append(arg_map[carg_val]) elif 'void' in carg_val: args.append("string") else: args.append(x) else: args.append(x) arg = ', '.join(args) if ret is not None and ret.startswith('(') and '...' in ret: # A tuple (new in VTK-5.7) ret = "tuple" if arg is not None: if '[float, ...]' in arg: arg = arg.replace('[float, ...]', 'tuple') elif '(float, ...)' in arg: arg = arg.replace('(float, ...)', 'tuple') if ret == '(, )': ret = None # Now quote the args and eval them. Easy! try: if ret: ret = eval(pat.sub('\"', ret)) if arg: arg = eval(pat.sub('\"', arg)) if type(arg) == type('str'): arg = [arg] except SyntaxError: pass else: sig.append(([ret], arg)) return sig def get_tree(self): """Return the ClassTree instance used by this class.""" return self._tree ################################################################# # Non-public interface. ################################################################# def _initialize(self): """Initializes the method categories.""" # Collects the On/Off methods. self.toggle_meths = {} # Collects the SetTo methods. self.state_meths = {} # Collects the Set/Get pairs. self.get_set_meths = {} # Collects the Get methods. self.get_meths = [] # Collects all the remaining methods. self.other_meths = [] def _organize_methods(self, klass, methods): """Organizes the given methods of a VTK class into different categories. Parameters ---------- - klass : A VTK class - methods : `list` of `str` A list of the methods to be categorized. """ self._initialize() meths = methods[:] meths = self._find_toggle_methods(klass, meths) meths = self._find_state_methods(klass, meths) meths = self._find_get_set_methods(klass, meths) meths = self._find_get_methods(klass, meths) self.other_meths = [x for x in meths \ if callable(getattr(klass, x))] def _remove_method(self, meths, method): try: meths.remove(method) except ValueError: pass def _find_toggle_methods(self, klass, methods): """Find/store methods of the form {On,Off} in the given `methods`. Returns the remaining list of methods. """ meths = methods[:] tm = self.toggle_meths klass_name = klass.__name__ problem_methods = ['CopyVectors', 'CopyTensors', 'CopyTCoords', 'CopyScalars', 'CopyNormals', 'CopyGlobalIds', 'CopyPedigreeIds'] for method in meths[:]: if klass_name == 'vtkDataSetAttributes' and \ method[:-2] in problem_methods: continue if method[-2:] == 'On': key = method[:-2] if (key + 'Off') in meths and ('Get' + key) in meths: tm[key] = None meths.remove(method) meths.remove(key + 'Off') self._remove_method(meths, 'Set' + key) self._remove_method(meths, 'Get' + key) # get defaults if tm: obj = self._get_instance(klass) if obj: for key in tm: try: tm[key] = getattr(obj, 'Get%s'%key)() except (TypeError, AttributeError): print klass.__name__, key pass return meths def _find_state_methods(self, klass, methods): """Find/store methods of the form SetTo in the given `methods`. Returns the remaining list of methods. The method also computes the mapped value of the different . """ # These ignored ones are really not state methods. ignore = ['SetUpdateExtentToWholeExtent', 'SetDataExtentToWholeExtent', 'SetOutputSpacingToDefault', # In vtkImageReslice. 'SetOutputOriginToDefault', # In vtkImageReslice 'SetOutputExtentToDefault' # In vtkImageReslice ] meths = methods[:] sm = self.state_meths for method in meths[:]: if method not in ignore and method[:3] == 'Set': # Methods of form SetTo match = self._state_patn.search(method) # Second cond. ensures that this is not an accident. if match and (('Get'+method[3:]) not in meths): key = method[3:match.start()] # The part. if (('Get' + key) in methods): val = method[match.start()+2:] # part. meths.remove(method) if sm.has_key(key): sm[key].append([val, None]) else: sm[key] = [[val, None]] meths.remove('Get'+ key) self._remove_method(meths, 'Set'+ key) if ('Get' + key + 'MaxValue') in meths: meths.remove('Get' + key + 'MaxValue') meths.remove('Get' + key + 'MinValue') try: meths.remove('Get' + key + 'AsString') except ValueError: pass # Find the values for each of the states, i.e. find that # vtkProperty.SetRepresentationToWireframe() corresponds to # vtkProperty.SetRepresentation(1). if sm: obj = self._get_instance(klass) klass_name = klass.__name__ if obj and not klass_name.endswith('Viewer'): # We do not try to inspect viewers, because they'll # trigger segfaults during the inspection for key, values in sm.items(): default = getattr(obj, 'Get%s'%key)() for x in values[:]: try: getattr(obj, 'Set%sTo%s'%(key, x[0]))() except TypeError: # vtkRenderedGraphRepresentation has some of # its SetIvarToState methods that have # non-standard arguments, this throws off # the parser and we ignore these. #print klass.__name__, key pass else: val = getattr(obj, 'Get%s'%key)() x[1] = val if val == default: values.insert(0, [x[0], val]) return meths def _find_get_set_methods(self, klass, methods): """Find/store methods of the form {Get,Set}Prop in the given `methods` and returns the remaining list of methods. Note that it makes sense to call this *after* `_find_state_methods` is called in order to avoid incorrect duplication. This method also computes the default value and the ranges of the arguments (when possible) by using the Get{MaxValue,MinValue} methods. """ meths = methods[:] gsm = self.get_set_meths for method in meths[:]: # Methods of the Set/Get form. if method in ['Get', 'Set']: # This occurs with the vtkInformation class. continue elif (method[:3] == 'Set') and ('Get' + method[3:]) in methods: key = method[3:] meths.remove('Set' + key) meths.remove('Get' + key) if ('Get' + key + 'MaxValue') in meths: meths.remove('Get' + key + 'MaxValue') meths.remove('Get' + key + 'MinValue') gsm[key] = 1 else: gsm[key] = None # Find the default and range of the values. if gsm: obj = self._get_instance(klass) if obj: klass_name = klass.__name__ for key, value in gsm.items(): if klass_name in ['vtkPolyData', 'vtkContext2D']: # Evil hack, these classes segfault! default = None elif klass_name == 'vtkHyperOctree' and \ key == 'Dimension': # This class breaks standard VTK conventions. gsm[key] = (3, (1, 3)) continue else: try: default = getattr(obj, 'Get%s'%key)() except TypeError: default = None if value: low = getattr(obj, 'Get%sMinValue'%key)() high = getattr(obj, 'Get%sMaxValue'%key)() gsm[key] = (default, (low, high)) else: gsm[key] = (default, None) else: # We still might have methods that have a default range. for key, value in gsm.items(): if value == 1: gsm[key] = None return meths def _find_get_methods(self, klass, methods): """Find/store methods of the form Get in the given `methods` and returns the remaining list of methods. """ meths = methods[:] gm = self.get_meths for method in meths[:]: if method == 'Get': # Occurs with vtkInformation continue elif method[:3] == 'Get': gm.append(method) meths.remove(method) return meths def _get_instance(self, klass): """Given a VTK class, `klass`, returns an instance of the class. If the class is abstract, it uses the class tree to return an instantiable subclass. This is necessary to get the values of the 'state' methods and the ranges for the Get/Set methods. """ obj = None try: obj = klass() except TypeError: if self._tree: t = self._tree n = t.get_node(klass.__name__) for c in n.children: obj = self._get_instance(t.get_class(c.name)) if obj: break return obj mayavi-4.1.0/tvtk/common.py0000644000175100001440000000407011674464502016627 0ustar ischnellusers00000000000000"""Common functions and classes that do not require any external dependencies (apart from the standard library of course). """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. import string import re ###################################################################### # Utility functions. ###################################################################### def get_tvtk_name(vtk_name): """Converts a VTK class name to a TVTK class name. This function additionally converts any leading digits into a suitable string. For example: >>> get_tvtk_name('vtk3DSImporter') 'ThreeDSImporter' >>> get_tvtk_name('vtkXMLDataReader') 'XMLDataReader' """ if vtk_name[:3] == 'vtk': name = vtk_name[3:] dig2name = {'1':'One', '2':'Two', '3':'Three', '4':'Four', '5':'Five', '6': 'Six', '7':'Seven', '8':'Eight', '9': 'Nine', '0':'Zero'} if name[0] in string.digits: return dig2name[name[0]] + name[1:] else: return name else: return vtk_name class _Camel2Enthought: """Simple functor class to convert names from CamelCase to Enthought compatible names. For example:: >>> camel2enthought = _Camel2Enthought() >>> camel2enthought('XMLActor2DToSGML') 'xml_actor2d_to_sgml' """ def __init__(self): self.patn = re.compile(r'([A-Z0-9]+)([a-z0-9]*)') self.nd_patn = re.compile(r'(\D[123])_D') def __call__(self, name): ret = self.patn.sub(self._repl, name) ret = self.nd_patn.sub(r'\1d', ret) if ret[0] == '_': ret = ret[1:] return ret.lower() def _repl(self, m): g1 = m.group(1) g2 = m.group(2) if len(g1) > 1: if g2: return '_' + g1[:-1] + '_' + g1[-1] + g2 else: return '_' + g1 else: return '_' + g1 + g2 # Instantiate a converter. camel2enthought = _Camel2Enthought() mayavi-4.1.0/tvtk/api.py0000644000175100001440000000055511674464502016114 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # License: BSD style # The external API for tvtk. # The version of TVTK that is installed from tvtk.version import version, version as __version__ # The TVTK pseudo-module. from tvtk.tvtk_access import tvtk # Handy colors from VTK. from vtk.util import colors # Some miscellaneous functionality. from tvtk.misc import write_data mayavi-4.1.0/tvtk/pyface/0000755000175100001440000000000011674464502016233 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/pyface/scene.py0000644000175100001440000000174211674464502017706 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: #------------------------------------------------------------------------------ """A VTK interactor scene widget for the PyFace wxPython backend. See the class docs for more details. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2007, Enthought, Inc. # License: BSD Style. # Import the toolkit specific version. from tvtk.pyface.toolkit import toolkit_object Scene = toolkit_object('scene:Scene') mayavi-4.1.0/tvtk/pyface/tvtk_scene.py0000644000175100001440000010371111674464502020755 0ustar ischnellusers00000000000000"""A TVTK interactor scene widget. This class only uses TVTK and Traits. It does not even use any Pyface widgets. This is nice when you want to create a raw TVTK window but with some nice funcationality that is similar to the widgets. It is also the base class for the toolkit specific classes since it implements the core functionality. See the class docs for more details. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. import os.path from apptools.persistence import state_pickler from tvtk.api import tvtk from tvtk import messenger from tvtk.tvtk_base import vtk_color_trait from traits.api import HasPrivateTraits, HasTraits, Any, Int, \ Property, Instance, Event, Range, Bool, Trait, Str from tvtk.pyface import light_manager VTK_VER = tvtk.Version().vtk_version ###################################################################### # `TVTKScene` class. ###################################################################### class TVTKScene(HasPrivateTraits): """A TVTK interactor scene widget. This widget uses a RenderWindowInteractor and therefore supports interaction with VTK widgets. The widget uses TVTK. The widget also supports the following: - Save the scene to a bunch of common (and not so common) image formats. - save the rendered scene to the clipboard. - adding/removing lists/tuples of actors - setting the view to useful predefined views (just like in MayaVi). - If one passes `stereo=1` to the constructor, stereo rendering is enabled. By default this is disabled. Changing the stereo trait has no effect during runtime. - One can disable rendering by setting `disable_render` to True. """ # The version of this class. Used for persistence. __version__ = 0 ########################################################################### # Traits. ########################################################################### # Turn on/off stereo rendering. This is set on initialization and # has no effect once the widget is realized. stereo = Bool(False) # Perform line smoothing for all renderered lines. This produces # much nicer looking lines but renders slower. This setting works # only when called before the first render. line_smoothing = Bool(False) # Perform point smoothing for all renderered points. This # produces much nicer looking points but renders slower. This # setting works only when called before the first render. point_smoothing = Bool(False) # Perform polygon smoothing (anti-aliasing) for all rendered # polygons. This produces much nicer looking points but renders # slower. This setting works only when called before the first # render. polygon_smoothing = Bool(False) # Enable parallel projection. This trait is synchronized with # that of the camera. parallel_projection = Bool(False, desc='if the camera uses parallel projection') # Disable rendering. disable_render = Bool(False, desc='if rendering is to be disabled') # Enable off-screen rendering. This allows a user to render the # scene to an image without the need to have the window active. # For example, the application can be minimized and the saved # scene should be generated correctly. This is handy for batch # scripts and the like. This works under Win32. Under Mac OS X # and Linux it requires a recent VTK version (later than Oct 2005 # and ideally later than March 2006) to work correctly. off_screen_rendering = Bool(False, desc='if off-screen rendering is enabled') # The background color of the window. This is really a shadow # trait of the renderer's background. Delegation does not seem to # work nicely for this. background = Trait(vtk_color_trait((0.5, 0.5, 0.5)), desc='the background color of the window') # The default foreground color of any actors. This basically # saves the preference and actors will listen to changes -- # the scene itself does not use this. foreground = Trait(vtk_color_trait((1.0, 1.0, 1.0)), desc='the default foreground color of actors') # The magnification to use when generating images from the render # window. magnification = Range(1, 2048, 1, desc='the magnification used when the screen is saved to an image') # Specifies the number of frames to use for anti-aliasing when # saving a scene. This basically increases # `self.render_window.aa_frames` in order to produce anti-aliased # figures when a scene is saved to an image. It then restores the # `aa_frames` in order to get interactive rendering rates. anti_aliasing_frames = Range(0, 20, 8, desc='number of frames to use for anti-aliasing when saving a scene') # Default JPEG quality. jpeg_quality = Range(10, 100, 95, desc='the quality of the JPEG image to produce') # Default JPEG progressive setting. jpeg_progressive = Bool(True, desc='if the generated JPEG should be progressive') # The light manager. light_manager = Instance(light_manager.LightManager, record=True) # Is the scene busy or not. busy = Property(Bool, record=False) ######################################## # Events # Lifecycle events: there are no opening/opened events since the # control is actually created in __init__. # The control is going to be closed. closing = Event(record=False) # The control has been closed. closed = Event(record=False) # Event fired when an actor is added to the scene. actor_added = Event(record=False) # Event fired when any actor is removed from the scene. actor_removed = Event(record=False) ######################################## # Properties. # The interactor used by the scene. interactor = Property(Instance(tvtk.GenericRenderWindowInteractor)) # The render_window. render_window = Property(Instance(tvtk.RenderWindow)) # The renderer. renderer = Property(Instance(tvtk.Renderer)) # The camera. camera = Property(Instance(tvtk.Camera)) # The control to mimic the Widget behavior. control = Any ######################################## # Private traits. # A recorder for script recording. recorder = Instance(HasTraits, record=False, transient=True) # Cached last camera state. _last_camera_state = Any(transient=True) _camera_observer_id = Int(transient=True) _script_id = Str(transient=True) # The renderer instance. _renderer = Instance(tvtk.Renderer) _renwin = Instance(tvtk.RenderWindow) _interactor = Instance(tvtk.RenderWindowInteractor) _camera = Instance(tvtk.Camera) _busy_count = Int(0) ########################################################################### # 'object' interface. ########################################################################### def __init__(self, parent=None, **traits): """ Initializes the object. """ # Base class constructor. super(TVTKScene, self).__init__(**traits) # Used to set the view of the scene. self._def_pos = 1 self.control = self._create_control(parent) self._renwin.update_traits() def __get_pure_state__(self): """Allows us to pickle the scene.""" # The control attribute is not picklable since it is a VTK # object so we remove it. d = self.__dict__.copy() for x in ['control', '_renwin', '_interactor', '_camera', '_busy_count', '__sync_trait__', 'recorder', '_last_camera_state', '_camera_observer_id', '_script_id', '__traits_listener__']: d.pop(x, None) # Additionally pickle these. d['camera'] = self.camera return d def __getstate__(self): return state_pickler.dumps(self) def __setstate__(self, str_state): # This method is unnecessary since this object will almost # never be pickled by itself and only via an object that # contains it, therefore __init__ will be called when the # scene is constructed. However, setstate is defined just for # completeness. state_pickler.set_state(self, state_pickler.loads_state(str_state)) ########################################################################### # 'Scene' interface. ########################################################################### def render(self): """ Force the scene to be rendered. Nothing is done if the `disable_render` trait is set to True.""" if not self.disable_render: self._renwin.render() def add_actors(self, actors): """ Adds a single actor or a tuple or list of actors to the renderer.""" # Reset the zoom if this is the first actor. reset_zoom = (len(self._renderer.actors) == 0 and len(self._renderer.volumes)==0) if hasattr(actors, '__iter__'): for actor in actors: self._renderer.add_actor(actor) else: self._renderer.add_actor(actors) self.actor_added = actors if reset_zoom: self.reset_zoom() else: self.render() def remove_actors(self, actors): """ Removes a single actor or a tuple or list of actors from the renderer.""" if hasattr(actors, '__iter__'): for actor in actors: self._renderer.remove_actor(actor) else: self._renderer.remove_actor(actors) self.actor_removed = actors self.render() # Conevenience methods. add_actor = add_actors remove_actor = remove_actors def add_widgets(self, widgets, enabled=True): """Adds a single 3D widget or a sequence of widgets to the renderer. If `enabled` is True the widget is also enabled once it is added.""" if not hasattr(widgets, '__iter__'): widgets = [widgets] iren = self._interactor for widget in widgets: widget.interactor = iren widget.enabled = enabled self.render() def remove_widgets(self, widgets): """Removes a single 3D widget or a sequence of widgets from the renderer.""" if not hasattr(widgets, '__iter__'): widgets = [widgets] iren = self._interactor for widget in widgets: if widget.interactor is not None: widget.enabled = False widget.interactor = None self.render() def close(self): """Close the scene cleanly. This ensures that the scene is shutdown cleanly. This should be called if you are getting async errors when closing a scene from a UI. This is based on the observations of Charl Botha here: http://public.kitware.com/pipermail/vtkusers/2008-May/095291.html """ # Return if we are already closed. if self._renwin is None: return # Fire the "closing" event. self.closing = True # Disable any renders through traits listner callbacks. self.disable_render = True # Remove sync trait listeners. self.sync_trait('background', self._renderer, remove=True) self.sync_trait('parallel_projection', self.camera, remove=True) self.sync_trait('off_screen_rendering', self._renwin, remove=True) # Remove all the renderer's props. self._renderer.remove_all_view_props() # Set the renderwindow to release all resources and the OpenGL # context. self._renwin.finalize() # Disconnect the interactor from the renderwindow. self._interactor.render_window = None # Remove the reference to the render window. del self._renwin # Fire the "closed" event. self.closed = True def x_plus_view(self): """View scene down the +X axis. """ self._update_view(self._def_pos, 0, 0, 0, 0, 1) self._record_methods('x_plus_view()') def x_minus_view(self): """View scene down the -X axis. """ self._update_view(-self._def_pos, 0, 0, 0, 0, 1) self._record_methods('x_minus_view()') def z_plus_view(self): """View scene down the +Z axis. """ self._update_view(0, 0, self._def_pos, 0, 1, 0) self._record_methods('z_plus_view()') def z_minus_view(self): """View scene down the -Z axis. """ self._update_view(0, 0, -self._def_pos, 0, 1, 0) self._record_methods('z_minus_view()') def y_plus_view(self): """View scene down the +Y axis. """ self._update_view(0, self._def_pos, 0, 1, 0, 0) self._record_methods('y_plus_view()') def y_minus_view(self): """View scene down the -Y axis. """ self._update_view(0, -self._def_pos, 0, 1, 0, 0) self._record_methods('y_minus_view()') def isometric_view(self): """Set the view to an iso-metric view. """ self._update_view(self._def_pos, self._def_pos, self._def_pos, 0, 0, 1) self._record_methods('isometric_view()') def reset_zoom(self): """Reset the camera so everything in the scene fits.""" self._renderer.reset_camera() self.render() self._record_methods('reset_zoom()') def save(self, file_name, size=None, **kw_args): """Saves rendered scene to one of several image formats depending on the specified extension of the filename. If an additional size (2-tuple) argument is passed the window is resized to the specified size in order to produce a suitably sized output image. Please note that when the window is resized, the window may be obscured by other widgets and the camera zoom is not reset which is likely to produce an image that does not reflect what is seen on screen. Any extra keyword arguments are passed along to the respective image format's save method. """ ext = os.path.splitext(file_name)[1] meth_map = {'.ps': 'ps', '.bmp': 'bmp', '.tiff': 'tiff', '.png': 'png', '.jpg': 'jpg', '.jpeg': 'jpg', '.iv': 'iv', '.wrl': 'vrml', '.vrml':'vrml', '.oogl': 'oogl', '.rib': 'rib', '.obj': 'wavefront', '.eps': 'gl2ps', '.pdf':'gl2ps', '.tex': 'gl2ps', '.x3d': 'x3d', '.pov': 'povray'} if ext.lower() not in meth_map.keys(): raise ValueError, \ 'Unable to find suitable image type for given file extension.' meth = getattr(self, 'save_' + meth_map[ext]) if size is not None: orig_size = self.get_size() self.set_size(size) meth(file_name, **kw_args) self.set_size(orig_size) self._record_methods('save(%r, %r)'%(file_name, size)) else: meth(file_name, **kw_args) self._record_methods('save(%r)'%(file_name)) def save_ps(self, file_name): """Saves the rendered scene to a rasterized PostScript image. For vector graphics use the save_gl2ps method.""" if len(file_name) != 0: w2if = tvtk.WindowToImageFilter(read_front_buffer=False) w2if.magnification = self.magnification self._lift() w2if.input = self._renwin ex = tvtk.PostScriptWriter() ex.file_name = file_name ex.input = w2if.output self._exporter_write(ex) def save_bmp(self, file_name): """Save to a BMP image file.""" if len(file_name) != 0: w2if = tvtk.WindowToImageFilter(read_front_buffer=False) w2if.magnification = self.magnification self._lift() w2if.input = self._renwin ex = tvtk.BMPWriter() ex.file_name = file_name ex.input = w2if.output self._exporter_write(ex) def save_tiff(self, file_name): """Save to a TIFF image file.""" if len(file_name) != 0: w2if = tvtk.WindowToImageFilter(read_front_buffer=False) w2if.magnification = self.magnification self._lift() w2if.input = self._renwin ex = tvtk.TIFFWriter() ex.file_name = file_name ex.input = w2if.output self._exporter_write(ex) def save_png(self, file_name): """Save to a PNG image file.""" if len(file_name) != 0: w2if = tvtk.WindowToImageFilter(read_front_buffer=False) w2if.magnification = self.magnification self._lift() w2if.input = self._renwin ex = tvtk.PNGWriter() ex.file_name = file_name ex.input = w2if.output self._exporter_write(ex) def save_jpg(self, file_name, quality=None, progressive=None): """Arguments: file_name if passed will be used, quality is the quality of the JPEG(10-100) are valid, the progressive arguments toggles progressive jpegs.""" if len(file_name) != 0: if not quality and not progressive: quality, progressive = self.jpeg_quality, self.jpeg_progressive w2if = tvtk.WindowToImageFilter(read_front_buffer=False) w2if.magnification = self.magnification self._lift() w2if.input = self._renwin ex = tvtk.JPEGWriter() ex.quality = quality ex.progressive = progressive ex.file_name = file_name ex.input = w2if.output self._exporter_write(ex) def save_iv(self, file_name): """Save to an OpenInventor file.""" if len(file_name) != 0: ex = tvtk.IVExporter() self._lift() ex.input = self._renwin ex.file_name = file_name self._exporter_write(ex) def save_vrml(self, file_name): """Save to a VRML file.""" if len(file_name) != 0: ex = tvtk.VRMLExporter() self._lift() ex.input = self._renwin ex.file_name = file_name self._exporter_write(ex) def save_oogl(self, file_name): """Saves the scene to a Geomview OOGL file. Requires VTK 4 to work.""" if len(file_name) != 0: ex = tvtk.OOGLExporter() self._lift() ex.input = self._renwin ex.file_name = file_name self._exporter_write(ex) def save_rib(self, file_name, bg=0, resolution=None, resfactor=1.0): """Save scene to a RenderMan RIB file. Keyword Arguments: file_name -- File name to save to. bg -- Optional background option. If 0 then no background is saved. If non-None then a background is saved. If left alone (defaults to None) it will result in a pop-up window asking for yes/no. resolution -- Specify the resolution of the generated image in the form of a tuple (nx, ny). resfactor -- The resolution factor which scales the resolution. """ if resolution == None: # get present window size Nx, Ny = self.render_window.size else: try: Nx, Ny = resolution except TypeError: raise TypeError, \ "Resolution (%s) should be a sequence with two elements"%resolution if len(file_name) == 0: return f_pref = os.path.splitext(file_name)[0] ex = tvtk.RIBExporter() ex.size = int(resfactor*Nx), int(resfactor*Ny) ex.file_prefix = f_pref ex.texture_prefix = f_pref + "_tex" self._lift() ex.render_window = self._renwin ex.background = bg if VTK_VER[:3] in ['4.2', '4.4']: # The vtkRIBExporter is broken in respect to VTK light # types. Therefore we need to convert all lights into # scene lights before the save and later convert them # back. ######################################## # Internal functions def x3to4(x): # convert 3-vector to 4-vector (w=1 -> point in space) return (x[0], x[1], x[2], 1.0 ) def x4to3(x): # convert 4-vector to 3-vector return (x[0], x[1], x[2]) def cameralight_transform(light, xform, light_type): # transform light by 4x4 matrix xform origin = x3to4(light.position) focus = x3to4(light.focal_point) neworigin = xform.multiply_point(origin) newfocus = xform.multiply_point(focus) light.position = x4to3(neworigin) light.focal_point = x4to3(newfocus) light.light_type = light_type ######################################## save_lights_type=[] for light in self.light_manager.lights: save_lights_type.append(light.source.light_type) # Convert lights to scene lights. cam = self.camera xform = tvtk.Matrix4x4() xform.deep_copy(cam.camera_light_transform_matrix) for light in self.light_manager.lights: cameralight_transform(light.source, xform, "scene_light") # Write the RIB file. self._exporter_write(ex) # Now re-convert lights to camera lights. xform.invert() for i,light in enumerate(self.light_manager.lights): cameralight_transform(light.source, xform, save_lights_type[i]) # Change the camera position. Otherwise VTK would render # one broken frame after the export. cam.roll(0.5) cam.roll(-0.5) else: self._exporter_write(ex) def save_wavefront(self, file_name): """Save scene to a Wavefront OBJ file. Two files are generated. One with a .obj extension and another with a .mtl extension which contains the material proerties. Keyword Arguments: file_name -- File name to save to """ if len(file_name) != 0: ex = tvtk.OBJExporter() self._lift() ex.input = self._renwin f_pref = os.path.splitext(file_name)[0] ex.file_prefix = f_pref self._exporter_write(ex) def save_gl2ps(self, file_name, exp=None): """Save scene to a vector PostScript/EPS/PDF/TeX file using GL2PS. If you choose to use a TeX file then note that only the text output is saved to the file. You will need to save the graphics separately. Keyword Arguments: file_name -- File name to save to. exp -- Optionally configured vtkGL2PSExporter object. Defaults to None and this will use the default settings with the output file type chosen based on the extention of the file name. """ # Make sure the exporter is available. if not hasattr(tvtk, 'GL2PSExporter'): msg = "Saving as a vector PS/EPS/PDF/TeX file using GL2PS is "\ "either not supported by your version of VTK or "\ "you have not configured VTK to work with GL2PS -- read "\ "the documentation for the vtkGL2PSExporter class." print msg return if len(file_name) != 0: f_prefix, f_ext = os.path.splitext(file_name) ex = None if exp: ex = exp if not isinstance(exp, tvtk.GL2PSExporter): msg = "Need a vtkGL2PSExporter you passed a "\ "%s"%exp.__class__.__name__ raise TypeError, msg ex.file_prefix = f_prefix else: ex = tvtk.GL2PSExporter() # defaults ex.file_prefix = f_prefix if f_ext == ".ps": ex.file_format = 'ps' elif f_ext == ".tex": ex.file_format = 'tex' elif f_ext == ".pdf": ex.file_format = 'pdf' else: ex.file_format = 'eps' ex.sort = 'bsp' ex.compress = 1 ex.edit_traits(kind='livemodal') self._lift() ex.render_window = self._renwin if ex.write3d_props_as_raster_image: self._exporter_write(ex) else: ex.write() def save_x3d(self, file_name): """Save scene to an X3D file (http://www.web3d.org/x3d/). Keyword Arguments: file_name -- File name to save to. """ # Make sure the exporter is available. if not hasattr(tvtk, 'X3DExporter'): msg = "Saving as a X3D file does not appear to be "\ "supported by your version of VTK." print msg return if len(file_name) != 0: ex = tvtk.X3DExporter() ex.input = self._renwin ex.file_name = file_name ex.update() ex.write() def save_povray(self, file_name): """Save scene to a POVRAY (Persistance of Vision Raytracer), file (http://www.povray.org). Keyword Arguments: file_name -- File name to save to. """ # Make sure the exporter is available. if not hasattr(tvtk, 'POVExporter'): msg = "Saving as a POVRAY file does not appear to be "\ "supported by your version of VTK." print msg return if len(file_name) != 0: ex = tvtk.POVExporter() ex.input = self._renwin if hasattr(ex, 'file_name'): ex.file_name = file_name else: ex.file_prefix = os.path.splitext(file_name)[0] ex.update() ex.write() def get_size(self): """Return size of the render window.""" return self._interactor.size def set_size(self, size): """Set the size of the window.""" self._interactor.size = size self._renwin.size = size ########################################################################### # Properties. ########################################################################### def _get_interactor(self): """Returns the vtkRenderWindowInteractor of the parent class""" return self._interactor def _get_render_window(self): """Returns the scene's render window.""" return self._renwin def _get_renderer(self): """Returns the scene's renderer.""" return self._renderer def _get_camera(self): """ Returns the active camera. """ return self._renderer.active_camera def _get_busy(self): return self._busy_count > 0 def _set_busy(self, value): """The `busy` trait is either `True` or `False`. However, this could be problematic since we could have two methods `foo` and `bar that both set `scene.busy = True`. As soon as `bar` is done it sets `busy` back to `False`. This is wrong since the UI is still busy as `foo` is not done yet. We therefore store the number of busy calls and either increment it or decrement it and change the state back to `False` only when the count is zero. """ bc = self._busy_count if value: bc += 1 else: bc -= 1 bc = max(0, bc) self._busy_count = bc if bc == 1: self.trait_property_changed('busy', False, True) if bc == 0: self.trait_property_changed('busy', True, False) ########################################################################### # Non-public interface. ########################################################################### def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ # Create the renderwindow. renwin = self._renwin = tvtk.RenderWindow() # If we are doing offscreen rendering we set the window size to # (1,1) so the window does not appear at all if self.off_screen_rendering: renwin.size = (1,1) renwin.set(point_smoothing=self.point_smoothing, line_smoothing=self.line_smoothing, polygon_smoothing=self.polygon_smoothing) # Create a renderer and add it to the renderwindow self._renderer = tvtk.Renderer() renwin.add_renderer(self._renderer) self._interactor = tvtk.RenderWindowInteractor(render_window=renwin) # Save a reference to our camera so it is not GC'd -- needed for # the sync_traits to work. self._camera = self.camera # Sync various traits. self._renderer.background = self.background self.sync_trait('background', self._renderer) self._renderer.on_trait_change(self.render, 'background') self._camera.parallel_projection = self.parallel_projection self.sync_trait('parallel_projection', self._camera) renwin.off_screen_rendering = self.off_screen_rendering self.sync_trait('off_screen_rendering', self._renwin) self.render_window.on_trait_change(self.render, 'off_screen_rendering') self.render_window.on_trait_change(self.render, 'stereo_render') self.render_window.on_trait_change(self.render, 'stereo_type') self.camera.on_trait_change(self.render, 'parallel_projection') self._interactor.initialize() self._interactor.render() self.light_manager = light_manager.LightManager(self) if self.off_screen_rendering: # We want the default size to be the normal (300, 300). # Setting the size now should not resize the window if # offscreen is working properly in VTK. renwin.size = (300, 300) return self._interactor def _lift(self): """Lift the window to the top. Useful when saving screen to an image.""" return def _exporter_write(self, ex): """Abstracts the exporter's write method.""" # Bumps up the anti-aliasing frames when the image is saved so # that the saved picture looks nicer. rw = self.render_window aa_frames = rw.aa_frames rw.aa_frames = self.anti_aliasing_frames rw.render() ex.write() # Set the frames back to original setting. rw.aa_frames = aa_frames rw.render() def _update_view(self, x, y, z, vx, vy, vz): """Used internally to set the view.""" camera = self.camera camera.focal_point = 0.0, 0.0, 0.0 camera.position = x, y, z camera.view_up = vx, vy, vz self._renderer.reset_camera() self.render() def _disable_render_changed(self, val): if not val and self._renwin is not None: self.render() def _record_methods(self, calls): """A method to record a simple method called on self. We need a more powerful and less intrusive way like decorators to do this. Note that calls can be a string with new lines in which case we interpret this as multiple calls. """ r = self.recorder if r is not None: sid = self._script_id for call in calls.split('\n'): r.record('%s.%s'%(sid, call)) def _record_camera_position(self, vtk_obj=None, event=None): """Callback to record the camera position.""" r = self.recorder if r is not None: state = self._get_camera_state() lcs = self._last_camera_state if state != lcs: self._last_camera_state = state sid = self._script_id for key, value in state: r.record('%s.camera.%s = %r'%(sid, key, value)) r.record('%s.camera.compute_view_plane_normal()'%sid) r.record('%s.render()'%sid) def _get_camera_state(self): c = self.camera state = [] state.append(('position', list(c.position))) state.append(('focal_point', list(c.focal_point))) state.append(('view_angle', c.view_angle)) state.append(('view_up', list(c.view_up))) state.append(('clipping_range', list(c.clipping_range))) return state def _recorder_changed(self, r): """When the recorder is set we add an event handler so we can record the change to the camera position after the interaction. """ iren = self._interactor if r is not None: self._script_id = r.get_script_id(self) id = iren.add_observer('EndInteractionEvent', messenger.send) self._camera_observer_id = id i_vtk = tvtk.to_vtk(iren) messenger.connect(i_vtk, 'EndInteractionEvent', self._record_camera_position) else: self._script_id = '' iren.remove_observer(self._camera_observer_id) i_vtk = tvtk.to_vtk(iren) messenger.disconnect(i_vtk, 'EndInteractionEvent', self._record_camera_position) ###################################################################### # `TVTKScene` class. ###################################################################### class TVTKWindow(HasTraits): """A basic TVTK window class that can be used in the MayaVi engine for visualization without envisage/pyface etc. Technically we could just have used the `TVTKScene` class but we want to support the closing and activated events since they are used to notify the MayaVi engine if the window is closed or activated. In this case we do nothing but honour the interface. """ closing = Event activated = Event def __init__(self, **traits): """All the keyword arguments are passed on to the `TVTKScene` instance created.""" self.scene = TVTKScene(**traits) mayavi-4.1.0/tvtk/pyface/ui/0000755000175100001440000000000011674464502016650 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/pyface/ui/qt4/0000755000175100001440000000000011674464502017360 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/pyface/ui/qt4/scene.py0000644000175100001440000004455611674464502021045 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: #------------------------------------------------------------------------------ """A VTK interactor scene widget for the PyFace PyQt backend. See the class docs for more details. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2007, Enthought, Inc. # License: BSD Style. import os import tempfile import os from pyface.qt import QtCore, QtGui from tvtk.api import tvtk from tvtk import messenger from traits.api import Instance, Button, Any from traitsui.api import View, Group, Item, InstanceEditor from pyface.api import Widget, GUI, FileDialog, OK from tvtk.pyface import picker from tvtk.pyface import light_manager from tvtk.pyface.tvtk_scene import TVTKScene from QVTKRenderWindowInteractor import QVTKRenderWindowInteractor ###################################################################### # `_VTKRenderWindowInteractor` class. ###################################################################### class _VTKRenderWindowInteractor(QVTKRenderWindowInteractor): """ This is a thin wrapper around the standard VTK PyQt interactor. """ def __init__(self, scene, parent, **kwargs): QVTKRenderWindowInteractor.__init__(self, parent, **kwargs) self._scene = scene self._interacting = False def resizeEvent(self, e): """ Reimplemented to refresh the traits of the render window. """ QVTKRenderWindowInteractor.resizeEvent(self, e) self._scene._renwin.update_traits() def paintEvent(self, e): """ Reimplemented to create the light manager only when needed. This is necessary because it makes sense to create the light manager only when the widget is realized. Only when the widget is realized is the VTK render window created and only then are the default lights all setup correctly. """ QVTKRenderWindowInteractor.paintEvent(self, e) scene = self._scene if scene.light_manager is None: scene.light_manager = light_manager.LightManager(scene) renwin = scene._renwin renwin.update_traits() vtk_rw = tvtk.to_vtk(renwin) renwin.add_observer('StartEvent', messenger.send) messenger.connect(vtk_rw, 'StartEvent', self._start_event_callback) renwin.add_observer('EndEvent', messenger.send) messenger.connect(vtk_rw, 'EndEvent', self._end_event_callback) def keyPressEvent(self, e): """ This method is overridden to prevent the 's'/'w'/'e'/'q' keys from doing the default thing which is generally useless. It also handles the 'p' and 'l' keys so the picker and light manager are called. """ key = e.key() modifiers = e.modifiers() scene = self._scene camera = scene.camera if key in [QtCore.Qt.Key_Minus]: camera.zoom(0.8) scene.render() scene._record_methods('camera.zoom(0.8)\nrender()') return if key in [QtCore.Qt.Key_Equal, QtCore.Qt.Key_Plus]: camera.zoom(1.25) scene.render() scene._record_methods('camera.zoom(1.25)\nrender()') return if key in [QtCore.Qt.Key_E, QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape]: return if key in [QtCore.Qt.Key_W]: return if key in [QtCore.Qt.Key_R]: scene._record_methods('reset_zoom()') return if key in [QtCore.Qt.Key_P] and modifiers == QtCore.Qt.NoModifier: pos = self.mapFromGlobal(QtGui.QCursor.pos()) x = pos.x() y = self.height() - pos.y() scene.picker.pick(x, y) return if key in [QtCore.Qt.Key_F] and modifiers == QtCore.Qt.NoModifier: pos = self.mapFromGlobal(QtGui.QCursor.pos()) x = pos.x() y = self.height() - pos.y() data = scene.picker.pick_world(x, y) coord = data.coordinate if coord is not None: camera.focal_point = coord scene.render() scene._record_methods('camera.focal_point = %r\n'\ 'render()'%list(coord)) return if key in [QtCore.Qt.Key_L] and modifiers == QtCore.Qt.NoModifier: scene.light_manager.configure() return if key in [QtCore.Qt.Key_S] and modifiers == QtCore.Qt.NoModifier: fname = popup_save(self.parent()) if len(fname) != 0: self.save(fname) return shift = ((modifiers & QtCore.Qt.ShiftModifier) == QtCore.Qt.ShiftModifier) if key == QtCore.Qt.Key_Left: if shift: camera.yaw(-5) scene._record_methods('camera.yaw(-5)') else: camera.azimuth(5) scene._record_methods('camera.azimuth(5)') scene.render() scene._record_methods('render()') return if key == QtCore.Qt.Key_Right: if shift: camera.yaw(5) scene._record_methods('camera.yaw(5)') else: camera.azimuth(-5) scene._record_methods('camera.azimuth(-5)') scene.render() scene._record_methods('render()') return if key == QtCore.Qt.Key_Up: if shift: camera.pitch(-5) scene._record_methods('camera.pitch(-5)') else: camera.elevation(-5) scene._record_methods('camera.elevation(-5)') camera.orthogonalize_view_up() scene.render() scene._record_methods('camera.orthogonalize_view_up()\nrender()') return if key == QtCore.Qt.Key_Down: if shift: camera.pitch(5) scene._record_methods('camera.pitch(5)') else: camera.elevation(5) scene._record_methods('camera.elevation(5)') camera.orthogonalize_view_up() scene.render() scene._record_methods('camera.orthogonalize_view_up()\nrender()') return QVTKRenderWindowInteractor.keyPressEvent(self, e) def mousePressEvent(self, ev): """Override for mouse presses.""" self._interacting = True QVTKRenderWindowInteractor.mousePressEvent(self, ev) def mouseReleaseEvent(self, ev): """Override for mouse releases.""" self._interacting = False QVTKRenderWindowInteractor.mouseReleaseEvent(self, ev) def _start_event_callback(self, obj, event): if self._interacting: return else: self._scene.busy = True def _end_event_callback(self, obj, event): if self._interacting: return else: self._scene.busy = False ###################################################################### # Utility functions. ###################################################################### def popup_save(parent=None): """Popup a dialog asking for an image name to save the scene to. This is used mainly to save a scene in full screen mode. Returns a filename, returns empty string if action was cancelled. `parent` is the parent widget over which the dialog will be popped up. """ extns = ['*.png', '*.jpg', '*.jpeg', '*.tiff', '*.bmp', '*.ps', '*.eps', '*.tex', '*.rib', '*.wrl', '*.oogl', '*.pdf', '*.vrml', '*.obj', '*.iv'] wildcard='|'.join(extns) dialog = FileDialog( parent = parent, title='Save scene to image', action='save as', wildcard=wildcard ) if dialog.open() == OK: return dialog.path else: return '' ###################################################################### # `FullScreen` class. ###################################################################### class FullScreen(object): """Creates a full screen interactor widget. This will use VTK's event loop until the user presses 'q'/'e' on the full screen window. This does not yet support interacting with any widgets on the renderered scene. This class is really meant to be used for VTK versions earlier than 5.1 where there was a bug with reparenting a window. """ def __init__(self, scene): self.scene = scene self.old_rw = scene.render_window self.ren = scene.renderer def run(self): # Remove the renderer from the current render window. self.old_rw.remove_renderer(self.ren) # Creates renderwindow that should be used ONLY for # visualization in full screen full_rw = tvtk.RenderWindow(stereo_capable_window=True, full_screen=True ) # add the current visualization full_rw.add_renderer(self.ren) # provides a simple interactor style = tvtk.InteractorStyleTrackballCamera() self.iren = tvtk.RenderWindowInteractor(render_window=full_rw, interactor_style=style) # Gets parameters for stereo visualization if self.old_rw.stereo_render: full_rw.set(stereo_type=self.old_rw.stereo_type, stereo_render=True) # Starts the interactor self.iren.initialize() self.iren.render() self.iren.start() # Once the full screen window is quit this releases the # renderer before it is destroyed, and return it to the main # renderwindow. full_rw.remove_renderer(self.ren) self.old_rw.add_renderer(self.ren) self.old_rw.render() self.iren.disable() ###################################################################### # `Scene` class. ###################################################################### class Scene(TVTKScene, Widget): """A VTK interactor scene widget for pyface and PyQt. This widget uses a RenderWindowInteractor and therefore supports interaction with VTK widgets. The widget uses TVTK. In addition to the features that the base TVTKScene provides this widget supports: - saving the rendered scene to the clipboard. - picking data on screen. Press 'p' or 'P' when the mouse is over a point that you need to pick. - The widget also uses a light manager to manage the lighting of the scene. Press 'l' or 'L' to activate a GUI configuration dialog for the lights. - Pressing the left, right, up and down arrow let you rotate the camera in those directions. When shift-arrow is pressed then the camera is panned. Pressing the '+' (or '=') and '-' keys let you zoom in and out. - full screen rendering via the full_screen button on the UI. """ # The version of this class. Used for persistence. __version__ = 0 ########################################################################### # Traits. ########################################################################### # Turn on full-screen rendering. full_screen = Button('Full Screen') # The picker handles pick events. picker = Instance(picker.Picker) ######################################## # Render_window's view. _stereo_view = Group(Item(name='stereo_render'), Item(name='stereo_type'), show_border=True, label='Stereo rendering', ) # The default view of this object. default_view = View(Group( Group(Item(name='background'), Item(name='foreground'), Item(name='parallel_projection'), Item(name='disable_render'), Item(name='off_screen_rendering'), Item(name='jpeg_quality'), Item(name='jpeg_progressive'), Item(name='magnification'), Item(name='anti_aliasing_frames'), Item(name='full_screen', show_label=False), ), Group(Item(name='render_window', style='custom', visible_when='object.stereo', editor=InstanceEditor(view=View(_stereo_view)), show_label=False), ), label='Scene'), Group( Item(name='light_manager', style='custom', show_label=False), label='Lights'), buttons=['OK', 'Cancel'] ) ######################################## # Private traits. _vtk_control = Instance(_VTKRenderWindowInteractor) _fullscreen = Any ########################################################################### # 'object' interface. ########################################################################### def __init__(self, parent=None, **traits): """ Initializes the object. """ # Base class constructor. super(Scene, self).__init__(parent, **traits) # Setup the default picker. self.picker = picker.Picker(self) # The light manager needs creating. self.light_manager = None self._cursor = QtCore.Qt.ArrowCursor def __get_pure_state__(self): """Allows us to pickle the scene.""" # The control attribute is not picklable since it is a VTK # object so we remove it. d = super(Scene, self).__get_pure_state__() for x in ['_vtk_control', '_fullscreen']: d.pop(x, None) return d ########################################################################### # 'Scene' interface. ########################################################################### def render(self): """ Force the scene to be rendered. Nothing is done if the `disable_render` trait is set to True.""" if not self.disable_render: self._vtk_control.Render() def get_size(self): """Return size of the render window.""" sz = self._vtk_control.size() return (sz.width(), sz.height()) def set_size(self, size): """Set the size of the window.""" self._vtk_control.resize(*size) def hide_cursor(self): """Hide the cursor.""" self._cursor = self._vtk_control.cursor().shape() self._vtk_control.setCursor(QtCore.Qt.BlankCursor) def show_cursor(self): """Show the cursor.""" self._vtk_control.setCursor(self._cursor) ########################################################################### # 'TVTKScene' interface. ########################################################################### def save_to_clipboard(self): """Saves a bitmap of the scene to the clipboard.""" handler, name = tempfile.mkstemp() self.save_bmp(name) QtGui.QApplication.clipboard().setImage(QtGui.QImage(name)) os.close(handler) os.unlink(name) ########################################################################### # Non-public interface. ########################################################################### def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ # Create the VTK widget. self._vtk_control = window = _VTKRenderWindowInteractor(self, parent, stereo=self.stereo) # Switch the default interaction style to the trackball one. window.GetInteractorStyle().SetCurrentStyleToTrackballCamera() # Grab the renderwindow. renwin = self._renwin = tvtk.to_tvtk(window.GetRenderWindow()) renwin.set(point_smoothing=self.point_smoothing, line_smoothing=self.line_smoothing, polygon_smoothing=self.polygon_smoothing) # Create a renderer and add it to the renderwindow self._renderer = tvtk.Renderer() renwin.add_renderer(self._renderer) # Save a reference to our camera so it is not GC'd -- needed for # the sync_traits to work. self._camera = self.camera # Sync various traits. self._renderer.background = self.background self.sync_trait('background', self._renderer) self.renderer.on_trait_change(self.render, 'background') renwin.off_screen_rendering = self.off_screen_rendering self._camera.parallel_projection = self.parallel_projection self.sync_trait('parallel_projection', self._camera) self.sync_trait('off_screen_rendering', self._renwin) self.render_window.on_trait_change(self.render, 'off_screen_rendering') self.render_window.on_trait_change(self.render, 'stereo_render') self.render_window.on_trait_change(self.render, 'stereo_type') self.camera.on_trait_change(self.render, 'parallel_projection') self._interactor = tvtk.to_tvtk(window._Iren) return window def _lift(self): """Lift the window to the top. Useful when saving screen to an image.""" if self.render_window.off_screen_rendering: # Do nothing if off screen rendering is being used. return self._vtk_control.window().raise_() QtCore.QCoreApplication.processEvents() def _full_screen_fired(self): fs = self._fullscreen if fs is None: f = FullScreen(self) f.run() # This will block. self._fullscreen = None def _busy_changed(self, val): GUI.set_busy(val) mayavi-4.1.0/tvtk/pyface/ui/qt4/init.py0000644000175100001440000000270111674464502020675 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2007, Riverbank Computing Limited # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license. # # Author: Riverbank Computing Limited # Description: # # In an e-mail to enthought-dev on 2008.09.12 at 2:49 AM CDT, Phil Thompson said: # The advantage is that all of the PyQt code in ETS can now be re-licensed to # use the BSD - and I hereby give my permission for that to be done. It's # been on my list of things to do. #------------------------------------------------------------------------------ # Standard library imports. import sys # Major package imports. import os # PyQt4 version check crash PySide toolkit from pyface.qt import qt_api if qt_api == 'pyqt': from PyQt4 import QtGui, QtCore # Check the version numbers are late enough. if QtCore.QT_VERSION < 0x040200: raise RuntimeError, "Need Qt v4.2 or higher, but got v%s" % QtCore.QT_VERSION_STR if QtCore.PYQT_VERSION < 0x040100: raise RuntimeError, "Need PyQt v4.1 or higher, but got v%s" % QtCore.PYQT_VERSION_STR else: from PySide import QtGui, QtCore # It's possible that it has already been initialised. _app = QtGui.QApplication.instance() if _app is None: _app = QtGui.QApplication(sys.argv) #### EOF ###################################################################### mayavi-4.1.0/tvtk/pyface/ui/qt4/QVTKRenderWindowInteractor.py0000644000175100001440000004110011674464502025076 0ustar ischnellusers00000000000000""" A simple VTK widget for PyQt v4, the Qt v4 bindings for Python. See http://www.trolltech.com for Qt documentation, and http://www.riverbankcomputing.co.uk for PyQt. This class is based on the vtkGenericRenderWindowInteractor and is therefore fairly powerful. It should also play nicely with the vtk3DWidget code. Created by Prabhu Ramachandran, May 2002 Based on David Gobbi's QVTKRenderWidget.py Changes by Gerard Vermeulen Feb. 2003 Win32 support. Changes by Gerard Vermeulen, May 2003 Bug fixes and better integration with the Qt framework. Changes by Phil Thompson, Nov. 2006 Ported to PyQt v4. Added support for wheel events. Changes by Phil Thompson, Oct. 2007 Bug fixes. Changes by Phil Thompson, Mar. 2008 Added cursor support. """ import sys import os from pyface.qt import qt_api if qt_api == 'pyqt': from PyQt4 import QtGui, QtCore else: from PySide import QtGui, QtCore from ctypes import pythonapi, c_void_p, py_object pythonapi.PyCObject_AsVoidPtr.restype = c_void_p pythonapi.PyCObject_AsVoidPtr.argtypes = [ py_object ] import vtk from tvtk import messenger class QVTKRenderWindowInteractor(QtGui.QWidget): """ A QVTKRenderWindowInteractor for Python and Qt. Uses a vtkGenericRenderWindowInteractor to handle the interactions. Use GetRenderWindow() to get the vtkRenderWindow. Create with the keyword stereo=1 in order to generate a stereo-capable window. The user interface is summarized in vtkInteractorStyle.h: - Keypress j / Keypress t: toggle between joystick (position sensitive) and trackball (motion sensitive) styles. In joystick style, motion occurs continuously as long as a mouse button is pressed. In trackball style, motion occurs when the mouse button is pressed and the mouse pointer moves. - Keypress c / Keypress o: toggle between camera and object (actor) modes. In camera mode, mouse events affect the camera position and focal point. In object mode, mouse events affect the actor that is under the mouse pointer. - Button 1: rotate the camera around its focal point (if camera mode) or rotate the actor around its origin (if actor mode). The rotation is in the direction defined from the center of the renderer's viewport towards the mouse position. In joystick mode, the magnitude of the rotation is determined by the distance the mouse is from the center of the render window. - Button 2: pan the camera (if camera mode) or translate the actor (if object mode). In joystick mode, the direction of pan or translation is from the center of the viewport towards the mouse position. In trackball mode, the direction of motion is the direction the mouse moves. (Note: with 2-button mice, pan is defined as -Button 1.) - Button 3: zoom the camera (if camera mode) or scale the actor (if object mode). Zoom in/increase scale if the mouse position is in the top half of the viewport; zoom out/decrease scale if the mouse position is in the bottom half. In joystick mode, the amount of zoom is controlled by the distance of the mouse pointer from the horizontal centerline of the window. - Keypress 3: toggle the render window into and out of stereo mode. By default, red-blue stereo pairs are created. Some systems support Crystal Eyes LCD stereo glasses; you have to invoke SetStereoTypeToCrystalEyes() on the rendering window. Note: to use stereo you also need to pass a stereo=1 keyword argument to the constructor. - Keypress e: exit the application. - Keypress f: fly to the picked point - Keypress p: perform a pick operation. The render window interactor has an internal instance of vtkCellPicker that it uses to pick. - Keypress r: reset the camera view along the current view direction. Centers the actors and moves the camera so that all actors are visible. - Keypress s: modify the representation of all actors so that they are surfaces. - Keypress u: invoke the user-defined function. Typically, this keypress will bring up an interactor that you can type commands in. - Keypress w: modify the representation of all actors so that they are wireframe. """ # Map between VTK and Qt cursors. _CURSOR_MAP = { 0: QtCore.Qt.ArrowCursor, # VTK_CURSOR_DEFAULT 1: QtCore.Qt.ArrowCursor, # VTK_CURSOR_ARROW 2: QtCore.Qt.SizeBDiagCursor, # VTK_CURSOR_SIZENE 3: QtCore.Qt.SizeFDiagCursor, # VTK_CURSOR_SIZENWSE 4: QtCore.Qt.SizeBDiagCursor, # VTK_CURSOR_SIZESW 5: QtCore.Qt.SizeFDiagCursor, # VTK_CURSOR_SIZESE 6: QtCore.Qt.SizeVerCursor, # VTK_CURSOR_SIZENS 7: QtCore.Qt.SizeHorCursor, # VTK_CURSOR_SIZEWE 8: QtCore.Qt.SizeAllCursor, # VTK_CURSOR_SIZEALL 9: QtCore.Qt.PointingHandCursor, # VTK_CURSOR_HAND 10: QtCore.Qt.CrossCursor, # VTK_CURSOR_CROSSHAIR } # Map from Qt key codes to VTK key names _KEY_MAP = { QtCore.Qt.Key_Escape: "Esc", QtCore.Qt.Key_Tab: "Tab", QtCore.Qt.Key_Backtab: "Backtab", QtCore.Qt.Key_Backspace: "Backspace", QtCore.Qt.Key_Return: "Return", QtCore.Qt.Key_Enter: "Enter", QtCore.Qt.Key_Insert: "Insert", QtCore.Qt.Key_Delete: "Delete", QtCore.Qt.Key_Pause: "Pause", QtCore.Qt.Key_Print: "Print", QtCore.Qt.Key_SysReq: "Sysreq", QtCore.Qt.Key_Clear: "Clear", QtCore.Qt.Key_Home: "Home", QtCore.Qt.Key_End: "End", QtCore.Qt.Key_Left: "Left", QtCore.Qt.Key_Up: "Up", QtCore.Qt.Key_Right: "Right", QtCore.Qt.Key_Down: "Down", QtCore.Qt.Key_PageUp: "Prior", QtCore.Qt.Key_PageDown: "Next", QtCore.Qt.Key_Meta: "Meta", QtCore.Qt.Key_CapsLock: "Caps_Lock", QtCore.Qt.Key_NumLock: "Num_Lock", QtCore.Qt.Key_ScrollLock: "Scroll_Lock", QtCore.Qt.Key_F1: "F1", QtCore.Qt.Key_F2: "F2", QtCore.Qt.Key_F3: "F3", QtCore.Qt.Key_F4: "F4", QtCore.Qt.Key_F5: "F5", QtCore.Qt.Key_F6: "F6", QtCore.Qt.Key_F7: "F7", QtCore.Qt.Key_F8: "F8", QtCore.Qt.Key_F9: "F9", QtCore.Qt.Key_F10: "F10", QtCore.Qt.Key_F11: "F11", QtCore.Qt.Key_F12: "F12", } def __init__(self, parent=None, wflags=QtCore.Qt.WindowFlags(), **kw): # the current button self._ActiveButton = QtCore.Qt.NoButton # private attributes self.__oldFocus = None self.__saveX = 0 self.__saveY = 0 self.__saveModifiers = QtCore.Qt.NoModifier self.__saveButtons = QtCore.Qt.NoButton # do special handling of some keywords: # stereo, rw stereo = 0 if kw.has_key('stereo'): if kw['stereo']: stereo = 1 rw = None if kw.has_key('rw'): rw = kw['rw'] # create qt-level widget QtGui.QWidget.__init__(self, parent, wflags|QtCore.Qt.MSWindowsOwnDC) if rw: # user-supplied render window self._RenderWindow = rw else: self._RenderWindow = vtk.vtkRenderWindow() if qt_api == 'pyqt' or sys.platform != 'win32': self._RenderWindow.SetWindowInfo(str(int(self.winId()))) else: # On Windows PySide has a bug with winID() function, so this is fix: self._RenderWindow.SetWindowInfo( str(int(pythonapi.PyCObject_AsVoidPtr(self.winId())))) self._should_set_parent_info = (sys.platform == 'win32') if stereo: # stereo mode self._RenderWindow.StereoCapableWindowOn() self._RenderWindow.SetStereoTypeToCrystalEyes() self._Iren = vtk.vtkGenericRenderWindowInteractor() self._Iren.SetRenderWindow(self._RenderWindow) # do all the necessary qt setup self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent) self.setAttribute(QtCore.Qt.WA_PaintOnScreen) self.setMouseTracking(True) # get all mouse events self.setFocusPolicy(QtCore.Qt.WheelFocus) self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)) self._Timer = QtCore.QTimer(self) self.connect(self._Timer, QtCore.SIGNAL('timeout()'), self.TimerEvent) self._Iren.AddObserver('CreateTimerEvent', messenger.send) messenger.connect(self._Iren, 'CreateTimerEvent', self.CreateTimer) self._Iren.AddObserver('DestroyTimerEvent', messenger.send) messenger.connect(self._Iren, 'DestroyTimerEvent', self.DestroyTimer) render_window = self._Iren.GetRenderWindow() render_window.AddObserver('CursorChangedEvent', messenger.send) messenger.connect(render_window, 'CursorChangedEvent', self.CursorChangedEvent) def __getattr__(self, attr): """Makes the object behave like a vtkGenericRenderWindowInteractor""" if attr == '__vtk__': return lambda t=self._Iren: t elif hasattr(self._Iren, attr): return getattr(self._Iren, attr) else: raise AttributeError, self.__class__.__name__ + \ " has no attribute named " + attr def CreateTimer(self, obj, evt): self._Timer.start(10) def DestroyTimer(self, obj, evt): self._Timer.stop() return 1 def TimerEvent(self): self._Iren.TimerEvent() def CursorChangedEvent(self, obj, evt): """Called when the CursorChangedEvent fires on the render window.""" # This indirection is needed since when the event fires, the current # cursor is not yet set so we defer this by which time the current # cursor should have been set. QtCore.QTimer.singleShot(0, self.ShowCursor) def HideCursor(self): """Hides the cursor.""" self.setCursor(QtCore.Qt.BlankCursor) def ShowCursor(self): """Shows the cursor.""" vtk_cursor = self._Iren.GetRenderWindow().GetCurrentCursor() qt_cursor = self._CURSOR_MAP.get(vtk_cursor, QtCore.Qt.ArrowCursor) self.setCursor(qt_cursor) def sizeHint(self): return QtCore.QSize(400, 400) def paintEngine(self): return None def paintEvent(self, ev): self._RenderWindow.Render() def resizeEvent(self, ev): if self._should_set_parent_info: # Set the window info and parent info on every resize. # vtkWin32OpenGLRenderWindow will render using incorrect offsets if # the parent info is not given to it because it assumes that it # needs to make room for the title bar. if qt_api == 'pyqt' or sys.platform != 'win32': self._RenderWindow.SetWindowInfo(str(int(self.winId()))) else: # On Windows PySide has a bug with winID() function, so this is fix: self._RenderWindow.SetWindowInfo( str(int(pythonapi.PyCObject_AsVoidPtr(self.winId())))) parent = self.parent() if parent is not None: if qt_api == 'pyqt' or sys.platform != 'win32': self._RenderWindow.SetParentInfo(str(int(self.winId()))) else: # On Windows PySide has a bug with winID() function, so this is fix: self._RenderWindow.SetParentInfo( str(int(pythonapi.PyCObject_AsVoidPtr(self.winId())))) else: self._RenderWindow.SetParentInfo('') w = self.width() h = self.height() self._RenderWindow.SetSize(w, h) self._Iren.SetSize(w, h) def _GetCtrlShift(self, ev): ctrl = shift = False if hasattr(ev, 'modifiers'): if ev.modifiers() & QtCore.Qt.ShiftModifier: shift = True if ev.modifiers() & QtCore.Qt.ControlModifier: ctrl = True else: if self.__saveModifiers & QtCore.Qt.ShiftModifier: shift = True if self.__saveModifiers & QtCore.Qt.ControlModifier: ctrl = True return ctrl, shift def enterEvent(self, ev): if not self.hasFocus(): self.__oldFocus = self.focusWidget() self.setFocus() ctrl, shift = self._GetCtrlShift(ev) self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY, ctrl, shift, chr(0), 0, None) self._Iren.EnterEvent() def leaveEvent(self, ev): if self.__saveButtons == QtCore.Qt.NoButton and self.__oldFocus: self.__oldFocus.setFocus() self.__oldFocus = None ctrl, shift = self._GetCtrlShift(ev) self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY, ctrl, shift, chr(0), 0, None) self._Iren.LeaveEvent() def mousePressEvent(self, ev): ctrl, shift = self._GetCtrlShift(ev) repeat = 0 if ev.type() == QtCore.QEvent.MouseButtonDblClick: repeat = 1 self._Iren.SetEventInformationFlipY(ev.x(), ev.y(), ctrl, shift, chr(0), repeat, None) self._ActiveButton = ev.button() if self._ActiveButton == QtCore.Qt.LeftButton: self._Iren.LeftButtonPressEvent() elif self._ActiveButton == QtCore.Qt.RightButton: self._Iren.RightButtonPressEvent() elif self._ActiveButton == QtCore.Qt.MidButton: self._Iren.MiddleButtonPressEvent() def mouseReleaseEvent(self, ev): ctrl, shift = self._GetCtrlShift(ev) self._Iren.SetEventInformationFlipY(ev.x(), ev.y(), ctrl, shift, chr(0), 0, None) if self._ActiveButton == QtCore.Qt.LeftButton: self._Iren.LeftButtonReleaseEvent() elif self._ActiveButton == QtCore.Qt.RightButton: self._Iren.RightButtonReleaseEvent() elif self._ActiveButton == QtCore.Qt.MidButton: self._Iren.MiddleButtonReleaseEvent() def mouseMoveEvent(self, ev): self.__saveModifiers = ev.modifiers() self.__saveButtons = ev.buttons() self.__saveX = ev.x() self.__saveY = ev.y() ctrl, shift = self._GetCtrlShift(ev) self._Iren.SetEventInformationFlipY(ev.x(), ev.y(), ctrl, shift, chr(0), 0, None) self._Iren.MouseMoveEvent() def keyPressEvent(self, ev): ctrl, shift = self._GetCtrlShift(ev) key_sym = self._KEY_MAP.get(ev.key(), None) if ev.key() < 256: if ev.text(): key = str(ev.text()) else: # Has modifiers, but an ASCII key code. key = chr(ev.key()) else: key = chr(0) self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY, ctrl, shift, key, 0, key_sym) self._Iren.KeyPressEvent() self._Iren.CharEvent() def keyReleaseEvent(self, ev): ctrl, shift = self._GetCtrlShift(ev) key_sym = self._KEY_MAP.get(ev.key(), None) if ev.key() < 256: key = chr(ev.key()) else: key = chr(0) self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY, ctrl, shift, key, 0, key_sym) self._Iren.KeyReleaseEvent() def wheelEvent(self, ev): if ev.delta() >= 0: self._Iren.MouseWheelForwardEvent() else: self._Iren.MouseWheelBackwardEvent() def GetRenderWindow(self): return self._RenderWindow def Render(self): self.update() def QVTKRenderWidgetConeExample(): """A simple example that uses the QVTKRenderWindowInteractor class.""" # every QT app needs an app app = QtGui.QApplication(['QVTKRenderWindowInteractor']) # create the widget widget = QVTKRenderWindowInteractor() widget.Initialize() widget.Start() # if you dont want the 'q' key to exit comment this. widget.AddObserver("ExitEvent", lambda o, e, a=app: a.quit()) ren = vtk.vtkRenderer() widget.GetRenderWindow().AddRenderer(ren) cone = vtk.vtkConeSource() cone.SetResolution(8) coneMapper = vtk.vtkPolyDataMapper() coneMapper.SetInput(cone.GetOutput()) coneActor = vtk.vtkActor() coneActor.SetMapper(coneMapper) ren.AddActor(coneActor) # show the widget widget.show() # start event processing app.exec_() if __name__ == "__main__": QVTKRenderWidgetConeExample() mayavi-4.1.0/tvtk/pyface/ui/qt4/scene_editor.py0000644000175100001440000002125511674464502022402 0ustar ischnellusers00000000000000"""A `SceneEditor` for the `SceneModel`. """ # Authors: Prabhu Ramachandran # Robert Kern # # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. import os from pyface.qt import QtGui # Enthought library imports. from traits.api import Any, Bool, Callable from traitsui.qt4.editor import Editor from traitsui.basic_editor_factory import BasicEditorFactory from decorated_scene import DecoratedScene ##################################################################### # `_SceneEditor` class ##################################################################### class _SceneEditor(Editor): """ An editor for SceneModels. """ # The editor is scrollable, so override the default. scrollable = Bool(True) # Internal GUI traits. _scene = Any() #### Public 'Editor' interface ############################################# def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory self.control = QtGui.QWidget() lay = QtGui.QVBoxLayout(self.control) lay.setContentsMargins(0, 0, 0, 0) assert self.value.scene_editor is None, \ "The SceneModel may only have one active editor!" self._create_scene() self.value.activated = True def update_editor(self): """ Updates the editor when the object trait changes external to the editor. """ # Everything should really be handled elsewhere in trait notifications. # Just pass here. pass def dispose(self): """ Disposes of the contents of an editor. """ # Remove notifications. self.value.closing = True self.value.scene_editor = None self._setup_scene_notifications(remove=True) # Remove the current scene. if self._scene is not None: self._scene.close() self._scene = None # This will destroy self.control and all of its children, including the # scene's control. super(_SceneEditor, self).dispose() #### Private '_SceneEditor' interface ################################## def _create_scene(self): """ Create the TVTK scene widget. """ factory = self.factory self._scene = factory.scene_class(self.control) scene = self._scene self.value.scene_editor = scene # Disable rendering on the scene until we're finished. scene.disable_render = True # Add all of the actors in the current actor map. for obj, actors in self.value.actor_map.items(): self._add_actors_widgets(actors) # Add all of the actors in the current actor map. self._add_actors_widgets(self.value.actor_list) # Set up Traits notifications. self._setup_scene_notifications() # Re-enable rendering. scene.disable_render = False self.control.layout().addWidget(scene.control) # Force a render. scene.render() def _setup_scene_notifications(self, remove=False): """ Set up or remove all of the Trait notifications that control the scene widget. """ traits_to_sync = ['foreground', 'anti_aliasing_frames', 'stereo', 'background', 'off_screen_rendering', 'polygon_smoothing', 'jpeg_progressive', 'point_smoothing', 'busy', 'disable_render', 'magnification', 'jpeg_quality', 'parallel_projection', 'line_smoothing'] model = self.value scene = self._scene if not remove: scene.set(**model.get(traits_to_sync)) for trait in traits_to_sync: scene.sync_trait(trait, model, mutual=True, remove=remove) model.on_trait_change( scene.render, name='do_render', remove=remove, ) model.on_trait_change( self._actors_changed, name='actor_map_items', remove=remove, ) model.on_trait_change( self._actor_map_changed, name='actor_map', remove=remove, ) model.on_trait_change( self._actor_list_items_changed, name='actor_list_items', remove=remove, ) model.on_trait_change( self._actor_list_changed, name='actor_list', remove=remove, ) def _actors_changed(self, event): """ Handle the event of the actors in the actor map changing. """ scene = self._scene # Temporarily turn off rendering. We (re)store the old value of # disable_render because it may already be True. old_disable_render = scene.disable_render scene.disable_render = True try: for obj, actors in event.removed.items(): self._remove_actors_widgets(actors) for obj, actors in event.added.items(): self._add_actors_widgets(actors) for obj, actors in event.changed.items(): # The actors in the event are the old ones. Grab the new ones # from the actor map itself. self._remove_actors_widgets(actors) self._add_actors_widgets(self.value.actor_map[obj]) finally: scene.disable_render = old_disable_render scene.render() def _actor_map_changed(self, object, name, old, new): """ Handle the case when the entire actor map is set to something else. """ scene = self._scene # Temporarily turn off rendering. We (re)store the old value of # disable_render because it may already be True. old_disable_render = scene.disable_render scene.disable_render = True try: for obj, actors in old.items(): self._remove_actors_widgets(actors) for obj, actors in new.items(): self._add_actors_widgets(actors) finally: scene.disable_render = old_disable_render scene.render() def _actor_list_items_changed(self, event): self._actor_list_changed(self.value, 'actor_list', event.removed, event.added) def _actor_list_changed(self, object, name, old, new): """ Handle the event of the actors in the actor map changing. """ scene = self._scene # Temporarily turn off rendering. We (re)store the old value of # disable_render because it may already be True. old_disable_render = scene.disable_render scene.disable_render = True try: self._remove_actors_widgets(old) self._add_actors_widgets(new) finally: scene.disable_render = old_disable_render scene.render() def _separate_actors_widgets(self, actors_widgets): """Given a sequence (or single) of actors or widgets, this returns a list of just the actors and another of just the widgets. """ if not hasattr(actors_widgets, '__getitem__'): actors_widgets = [actors_widgets] actors = [] widgets = [] for actor in actors_widgets: if actor.is_a('vtk3DWidget') or actor.is_a('vtkInteractorObserver'): widgets.append(actor) else: actors.append(actor) return actors, widgets def _add_actors_widgets(self, actors_widgets): """Add actors and widgets to scene.""" scene = self._scene actors, widgets = self._separate_actors_widgets(actors_widgets) scene.add_actors(actors) enabled_info = self.value.enabled_info for widget in widgets: scene.add_widgets(widget, enabled_info.get(widget, True)) def _remove_actors_widgets(self, actors_widgets): """Remove actors and widgets from scene.""" scene = self._scene actors, widgets = self._separate_actors_widgets(actors_widgets) scene.remove_actors(actors) scene.remove_widgets(widgets) ##################################################################### # `SceneEditor` class ##################################################################### class SceneEditor(BasicEditorFactory): """ A TraitsUI editor factory for SceneModel instances. """ # The class of the editor object to be constructed. klass = _SceneEditor # The class or factory function for creating the actual scene object. scene_class = Callable(DecoratedScene) #### EOF ####################################################################### mayavi-4.1.0/tvtk/pyface/ui/qt4/decorated_scene.py0000644000175100001440000002602511674464502023046 0ustar ischnellusers00000000000000"""A VTK interactor scene which provides a convenient toolbar that allows the user to set the camera view, turn on the axes indicator etc. """ # Authors: Prabhu Ramachandran , # Dave Peterson # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # System imports. from os.path import dirname import os from pyface.qt import QtGui # Enthought library imports. from pyface.api import ImageResource, FileDialog, OK from pyface.action.api import ToolBarManager, Group, Action from tvtk.api import tvtk from traits.api import Instance, false, Either, List # Local imports. from scene import Scene ########################################################################### # 'DecoratedScene' class ########################################################################### class DecoratedScene(Scene): """A VTK interactor scene which provides a convenient toolbar that allows the user to set the camera view, turn on the axes indicator etc. """ ####################################################################### # Traits ####################################################################### if hasattr(tvtk, 'OrientationMarkerWidget'): # The tvtk orientation marker widget. This only exists in VTK # 5.x. marker = Instance(tvtk.OrientationMarkerWidget, ()) # The tvtk axes that will be shown for the orientation. axes = Instance(tvtk.AxesActor, ()) else: marker = None axes = None # Determine if the orientation axis is shown or not. show_axes = false # The list of actions represented in the toolbar actions = List(Either(Action, Group)) ########################################################################## # `object` interface ########################################################################## def __init__(self, parent, **traits): super(DecoratedScene, self).__init__(parent, **traits) self._setup_axes_marker() def __get_pure_state__(self): """Allows us to pickle the scene.""" # The control attribute is not picklable since it is a VTK # object so we remove it. d = super(DecoratedScene, self).__get_pure_state__() for x in ['_content', '_panel', '_tool_bar', 'actions']: d.pop(x, None) return d ########################################################################## # Non-public interface. ########################################################################## def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. Overridden to wrap the Scene control within a panel that also contains a toolbar. """ # Create a panel as a wrapper of the scene toolkit control. This # allows us to also add additional controls. self._panel = QtGui.QMainWindow() # Add our toolbar to the panel. tbm = self._get_tool_bar_manager() self._tool_bar = tbm.create_tool_bar(self._panel) self._panel.addToolBar(self._tool_bar) # Create the actual scene content self._content = super(DecoratedScene, self)._create_control(self._panel) self._panel.setCentralWidget(self._content) return self._panel def _setup_axes_marker(self): axes = self.axes if axes is None: # For VTK versions < 5.0. return axes.set( normalized_tip_length=(0.4, 0.4, 0.4), normalized_shaft_length=(0.6, 0.6, 0.6), shaft_type='cylinder' ) p = axes.x_axis_caption_actor2d.caption_text_property axes.y_axis_caption_actor2d.caption_text_property = p axes.z_axis_caption_actor2d.caption_text_property = p p.set(color=(1,1,1), shadow=False, italic=False) self._background_changed(self.background) self.marker.set(key_press_activation=False) self.marker.orientation_marker = axes def _get_tool_bar_manager(self): """ Returns the tool_bar_manager for this scene. """ tbm = ToolBarManager( *self.actions ) return tbm def _get_image_path(self): """Returns the directory which contains the images used by the toolbar.""" # So that we can find the images. import tvtk.pyface.api return dirname(tvtk.pyface.api.__file__) def _toggle_projection(self): """ Toggle between perspective and parallel projection, this is used for the toolbar. """ if self._panel is not None: self.parallel_projection = not self.parallel_projection def _toggle_axes(self, *args): """Used by the toolbar to turn on/off the axes indicator. """ if self._panel is not None: self.show_axes = not self.show_axes def _save_snapshot(self): """Invoked by the toolbar menu to save a snapshot of the scene to an image. Note that the extension of the filename determines what image type is saved. The default is PNG. """ if self._panel is not None: wildcard = "PNG images (*.png)|*.png|Determine by extension (*.*)|*.*" dialog = FileDialog( parent = self._panel, title = 'Save scene to image', action = 'save as', default_filename = "snapshot.png", wildcard = wildcard ) if dialog.open() == OK: # The extension of the path will determine the actual # image type saved. self.save(dialog.path) def _configure_scene(self): """Invoked when the toolbar icon for configuration is clicked. """ self.edit_traits() ###################################################################### # Trait handlers. ###################################################################### def _show_axes_changed(self): marker = self.marker if (self._vtk_control is not None) and (marker is not None): if not self.show_axes: marker.interactor = None marker.enabled = False else: marker.interactor = self.interactor marker.enabled = True self.render() def _background_changed(self, value): # Depending on the background, this sets the axes text and # outline color to something that should be visible. axes = self.axes if (self._vtk_control is not None) and (axes is not None): p = self.axes.x_axis_caption_actor2d.caption_text_property m = self.marker s = value[0] + value[1] + value[2] if s <= 1.0: p.color = (1,1,1) m.set_outline_color(1,1,1) else: p.color = (0,0,0) m.set_outline_color(0,0,0) self.render() def _actions_default(self): return [ Group( Action( image = ImageResource('16x16/x-axis', search_path = [self._get_image_path()], ), tooltip = "View along the -X axis", on_perform = self.x_minus_view, ), Action( image = ImageResource('16x16/x-axis', search_path = [self._get_image_path()], ), tooltip = "View along the +X axis", on_perform = self.x_plus_view, ), Action( image = ImageResource('16x16/y-axis', search_path = [self._get_image_path()], ), tooltip = "View along the -Y axis", on_perform = self.y_minus_view, ), Action( image = ImageResource('16x16/y-axis', search_path = [self._get_image_path()], ), tooltip = "View along the +Y axis", on_perform = self.y_plus_view, ), Action( image = ImageResource('16x16/z-axis', search_path = [self._get_image_path()], ), tooltip = "View along the -Z axis", on_perform = self.z_minus_view, ), Action( image = ImageResource('16x16/z-axis', search_path = [self._get_image_path()], ), tooltip = "View along the +Z axis", on_perform = self.z_plus_view, ), Action( image = ImageResource('16x16/isometric', search_path = [self._get_image_path()], ), tooltip = "Obtain an isometric view", on_perform = self.isometric_view, ), ), Group( Action( image = ImageResource('16x16/parallel', search_path = [self._get_image_path()], ), tooltip = 'Toggle parallel projection', style="toggle", on_perform = self._toggle_projection, checked = self.parallel_projection, ), Action( image = ImageResource('16x16/origin_glyph', search_path = [self._get_image_path()], ), tooltip = 'Toggle axes indicator', style="toggle", enabled=(self.marker is not None), on_perform = self._toggle_axes, checked = self.show_axes, ), Action( image = ImageResource('16x16/fullscreen', search_path = [self._get_image_path()], ), tooltip = 'Full Screen (press "q" or "e" or Esc to exit fullscreen)', style="push", on_perform = self._full_screen_fired, ), ), Group( Action( image = ImageResource('16x16/save', search_path = [self._get_image_path()], ), tooltip = "Save a snapshot of this scene", on_perform = self._save_snapshot, ), Action( image = ImageResource('16x16/configure', search_path = [self._get_image_path()], ), tooltip = 'Configure the scene', style="push", on_perform = self._configure_scene, ), ), ] mayavi-4.1.0/tvtk/pyface/ui/qt4/__init__.py0000644000175100001440000000064711674464502021500 0ustar ischnellusers00000000000000# Copyright (c) 2005-2011, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. mayavi-4.1.0/tvtk/pyface/ui/qt4/actor_editor.py0000644000175100001440000001632011674464502022412 0ustar ischnellusers00000000000000""" A mostly-general Traits UI editor for viewing things in TVTK scenes. """ # Authors: Robert Kern # Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Major library imports. import os from pyface.qt import QtGui # Enthought library imports. from traits.api import Any, Bool, Callable, Dict, Str from traitsui.qt4.editor import Editor from traitsui.basic_editor_factory import BasicEditorFactory from decorated_scene import DecoratedScene ##################################################################### # `_ActorEditor` class ##################################################################### class _ActorEditor(Editor): """ An editor for TVTK scenes. """ # The editor is scrollable, so override the default. scrollable = Bool(True) # Internal GUI traits. _scene = Any() #### Public 'Editor' interface ############################################# def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory self.control = QtGui.QWidget() lay = QtGui.QVBoxLayout(self.control) lay.setContentsMargins(0, 0, 0, 0) self._create_scene() def update_editor(self): """ Updates the editor when the object trait changes external to the editor. """ # Everything should really be handled elsewhere in trait notifications. # Just pass here. pass def dispose(self): """ Disposes of the contents of an editor. """ # Remove notifications. self._setup_scene_notifications(remove=True) # Remove the current scene. if self._scene is not None: self._scene.close() self._scene = None # This will destroy self.control and all of its children, including the # scene's control. super(_ActorEditor, self).dispose() #### Private '_ActorEditor' interface ################################## def _create_scene(self): """ Create the TVTK scene widget. """ factory = self.factory self._scene = factory.scene_class(self.control, **factory.scene_kwds) scene = self._scene # Disable rendering on the scene until we're finished. scene.disable_render = True # Add all of the actors in the current actor map. for obj, actors in self.value.items(): self._add_actors_widgets(actors) # Set up Traits notifications. self._setup_scene_notifications() # Re-enable rendering. scene.disable_render = False self.control.layout().addWidget(scene.control) # Force a render. scene.render() def _setup_scene_notifications(self, remove=False): """ Set up or remove all of the Trait notifications that control the scene widget. """ self.object.on_trait_change( self._set_scene_disable_render, name=self.factory.disable_render_name, remove=remove, ) self.object.on_trait_event( self._scene.render, name=self.factory.do_render_name, remove=remove, ) self.object.on_trait_change( self._actors_changed, name=self.name+'_items', remove=remove, ) self.object.on_trait_change( self._actor_map_changed, name=self.name, remove=remove, ) def _set_scene_disable_render(self, new): """ A callback for Traits notifications. """ self._scene.disable_render = new def _actors_changed(self, event): """ Handle the event of the actors in the actor map changing. """ scene = self._scene # Temporarily turn off rendering. We (re)store the old value of # disable_render because it may already be True. old_disable_render = scene.disable_render scene.disable_render = True try: for obj, actors in event.removed.items(): self._remove_actors_widgets(actors) for obj, actors in event.added.items(): self._add_actors_widgets(actors) for obj, actors in event.changed.items(): # The actors in the event are the old ones. Grab the new ones # from the actor map itself. self._remove_actors_widgets(actors) self._add_actors_widgets(self.value[obj]) finally: scene.disable_render = old_disable_render scene.render() def _actor_map_changed(self, object, name, old, new): """ Handle the case when the entire actor map is set to something else. """ scene = self._scene # Temporarily turn off rendering. We (re)store the old value of # disable_render because it may already be True. old_disable_render = scene.disable_render scene.disable_render = True try: for obj, actors in old.items(): self._remove_actors_widgets(actors) for obj, actors in new.items(): self._add_actors_widgets(actors) finally: scene.disable_render = old_disable_render scene.render() def _separate_actors_widgets(self, actors_widgets): """Given a sequence (or single) of actors or widgets, this returns a list of just the actors and another of just the widgets. """ if not hasattr(actors_widgets, '__getitem__'): actors_widgets = [actors_widgets] actors = [] widgets = [] for actor in actors_widgets: if actor.is_a('vtk3DWidget'): widgets.append(actor) else: actors.append(actor) return actors, widgets def _add_actors_widgets(self, actors_widgets): """Add actors and widgets to scene.""" scene = self._scene actors, widgets = self._separate_actors_widgets(actors_widgets) scene.add_actors(actors) scene.add_widgets(widgets) def _remove_actors_widgets(self, actors_widgets): """Remove actors and widgets from scene.""" scene = self._scene actors, widgets = self._separate_actors_widgets(actors_widgets) scene.remove_actors(actors) scene.remove_widgets(widgets) ##################################################################### # `ActorEditor` class ##################################################################### class ActorEditor(BasicEditorFactory): """ An editor factory for TVTK scenes. """ # The class of the editor object to be constructed. klass = _ActorEditor # The class or factory function for creating the actual scene object. scene_class = Callable(DecoratedScene) # Keyword arguments to pass to the scene factory. scene_kwds = Dict() # The name of the trait used for ITVTKActorModel.disable_render. disable_render_name = Str('disable_render') # The name of the trait used for ITVTKActorModel.do_render. do_render_name = Str('do_render') #### EOF ####################################################################### mayavi-4.1.0/tvtk/pyface/ui/wx/0000755000175100001440000000000011674464502017306 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/pyface/ui/wx/scene.py0000644000175100001440000006654111674464502020771 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: #------------------------------------------------------------------------------ """A VTK interactor scene widget for the PyFace wxPython backend. See the class docs for more details. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2008, Enthought, Inc. # License: BSD Style. import sys import os import tempfile import wx from tvtk.api import tvtk from tvtk import messenger from traits.api import Instance, Button, Any, Bool from traitsui.api import View, Group, Item, InstanceEditor from pyface.api import Widget, GUI, FileDialog, OK from tvtk.pyface import picker from tvtk.pyface import light_manager from tvtk.pyface.tvtk_scene import TVTKScene from wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor ###################################################################### # Utility functions. ###################################################################### def popup_save(parent=None): """Popup a dialog asking for an image name to save the scene to. This is used mainly to save a scene in full screen mode. Returns a filename, returns empty string if action was cancelled. `parent` is the parent widget over which the dialog will be popped up. """ extns = ['*.png', '*.jpg', '*.jpeg', '*.tiff', '*.bmp', '*.ps', '*.eps', '*.tex', '*.rib', '*.wrl', '*.oogl', '*.pdf', '*.vrml', '*.obj', '*.iv'] wildcard='|'.join(extns) dialog = FileDialog( parent = parent, title='Save scene to image', action='save as', wildcard=wildcard ) if dialog.open() == OK: return dialog.path else: return '' ###################################################################### # `FullScreen` class. ###################################################################### class FullScreen(object): """Creates a full screen interactor widget. This will use VTK's event loop until the user presses 'q'/'e' on the full screen window. This does not yet support interacting with any widgets on the renderered scene. This class is really meant to be used for VTK versions earlier than 5.1 where there was a bug with reparenting a window. """ def __init__(self, scene): self.scene = scene self.old_rw = scene.render_window self.ren = scene.renderer def run(self): # Remove the renderer from the current render window. self.old_rw.remove_renderer(self.ren) # Creates renderwindow tha should be used ONLY for # visualization in full screen full_rw = tvtk.RenderWindow(stereo_capable_window=True, full_screen=True ) # add the current visualization full_rw.add_renderer(self.ren) # Under OS X there is no support for creating a full screen # window so we set the size of the window here. if sys.platform == 'darwin': full_rw.size = tuple(wx.GetDisplaySize()) # provides a simple interactor style = tvtk.InteractorStyleTrackballCamera() self.iren = tvtk.RenderWindowInteractor(render_window=full_rw, interactor_style=style) # Gets parameters for stereo visualization if self.old_rw.stereo_render: full_rw.set(stereo_type=self.old_rw.stereo_type, stereo_render=True) # Starts the interactor self.iren.initialize() self.iren.render() self.iren.start() # Once the full screen window is quit this releases the # renderer before it is destroyed, and return it to the main # renderwindow. full_rw.remove_renderer(self.ren) self.old_rw.add_renderer(self.ren) self.old_rw.render() self.iren.disable() ###################################################################### # `PopupScene` class. ###################################################################### class PopupScene(object): """Pops up a Scene instance with an independent `wx.Frame` in order to produce either a standalone window or usually a full screen view with *complete* interactivity (including widget interaction). """ def __init__(self, scene): self.orig_parent = None self.orig_size = None self.orig_pos = None self.frame = None self.scene = scene self.vtk_control = self.scene._vtk_control def _setup_frame(self): vtk_control = self.vtk_control self.orig_parent = vtk_control.GetParent() self.orig_size = vtk_control.GetSize() self.orig_pos = vtk_control.GetPosition() f = self.frame = wx.Frame(None, -1) return f def reparent_vtk(self, widget): """Reparent VTK control to another widget. """ scene = self.scene vc = self.vtk_control # We want to disable any rendering at this time so we override # the original render with a dummy after saving it. orig_disable_render = scene.disable_render scene.disable_render = True orig_render = vc.Render vc.Render = lambda : None rw = vc.GetRenderWindow() if sys.platform != 'darwin' and wx.Platform != '__WXMSW__': rw.SetNextWindowInfo(str(widget.GetHandle())) rw.WindowRemap() vc.Reparent(widget) wx.GetApp().Yield(True) # Restore the original render. vc.Render = orig_render vc.Render() scene.disable_render = orig_disable_render def popup(self, size=None): """Create a popup window of scene and set its default size. """ vc = self.vtk_control f = self._setup_frame() if size is None: f.SetSize(vc.GetSize()) else: f.SetSize(size) f.Show(True) self.reparent_vtk(f) def fullscreen(self): """Create a popup window of scene. """ f = self._setup_frame() f.Show(True) self.reparent_vtk(f) f.ShowFullScreen(True) def close(self): """Close the window and reparent the TVTK scene. """ f = self.frame if f is None: return vc = self.vtk_control self.reparent_vtk(self.orig_parent) vc.SetSize(self.orig_size) vc.SetPosition(self.orig_pos) f.ShowFullScreen(False) f.Show(False) f.Close() self.frame = None ###################################################################### # `Scene` class. ###################################################################### class Scene(TVTKScene, Widget): """A VTK interactor scene widget for pyface and wxPython. This widget uses a RenderWindowInteractor and therefore supports interaction with VTK widgets. The widget uses TVTK. In addition to the features that the base TVTKScene provides this widget supports: - saving the rendered scene to the clipboard. - picking data on screen. Press 'p' or 'P' when the mouse is over a point that you need to pick. - The widget also uses a light manager to manage the lighting of the scene. Press 'l' or 'L' to activate a GUI configuration dialog for the lights. - Pressing the left, right, up and down arrow let you rotate the camera in those directions. When shift-arrow is pressed then the camera is panned. Pressing the '+' (or '=') and '-' keys let you zoom in and out. - Pressing the 'f' key will set the camera focal point to the current point. - full screen rendering via the full_screen button on the UI. """ # The version of this class. Used for persistence. __version__ = 0 ########################################################################### # Traits. ########################################################################### # Turn on full-screen rendering. full_screen = Button('Full Screen') # The picker handles pick events. picker = Instance(picker.Picker) ######################################## # Render_window's view. _stereo_view = Group(Item(name='stereo_render'), Item(name='stereo_type'), show_border=True, label='Stereo rendering', ) # The default view of this object. default_view = View(Group( Group(Item(name='background'), Item(name='foreground'), Item(name='parallel_projection'), Item(name='disable_render'), Item(name='off_screen_rendering'), Item(name='jpeg_quality'), Item(name='jpeg_progressive'), Item(name='magnification'), Item(name='anti_aliasing_frames'), Item(name='full_screen', show_label=False), ), Group(Item(name='render_window', style='custom', visible_when='object.stereo', editor=InstanceEditor(view=View(_stereo_view)), show_label=False), ), label='Scene'), Group( Item(name='light_manager', style='custom', show_label=False), label='Lights'), buttons=['OK', 'Cancel'] ) ######################################## # Private traits. _vtk_control = Instance(wxVTKRenderWindowInteractor) _fullscreen = Any _interacting = Bool ########################################################################### # 'object' interface. ########################################################################### def __init__(self, parent=None, **traits): """ Initializes the object. """ # Base class constructor. super(Scene, self).__init__(parent, **traits) # Setup the default picker. self.picker = picker.Picker(self) def __get_pure_state__(self): """Allows us to pickle the scene.""" # The control attribute is not picklable since it is a VTK # object so we remove it. d = super(Scene, self).__get_pure_state__() for x in ['_vtk_control', '_fullscreen', '_interacting']: d.pop(x, None) return d ########################################################################### # 'Scene' interface. ########################################################################### def render(self): """ Force the scene to be rendered. Nothing is done if the `disable_render` trait is set to True.""" if not self.disable_render: self._vtk_control.Render() def get_size(self): """Return size of the render window.""" return self._vtk_control.GetSize() def set_size(self, size): """Set the size of the window.""" self._vtk_control.SetSize(size) def hide_cursor(self): """Hide the cursor.""" self._vtk_control.HideCursor() def show_cursor(self): """Show the cursor.""" self._vtk_control.ShowCursor() ########################################################################### # 'TVTKScene' interface. ########################################################################### def save_to_clipboard(self): """Saves a bitmap of the scene to the clipboard.""" handler, name = tempfile.mkstemp() self.save_bmp(name) bmp = wx.Bitmap(name, wx.BITMAP_TYPE_BMP) bmpdo = wx.BitmapDataObject(bmp) wx.TheClipboard.Open() wx.TheClipboard.SetData(bmpdo) wx.TheClipboard.Close() os.close(handler) os.unlink(name) ########################################################################### # `wxVTKRenderWindowInteractor` interface. ########################################################################### def OnKeyDown(self, event): """This method is overridden to prevent the 's'/'w'/'e'/'q' keys from doing the default thing which is generally useless. It also handles the 'p' and 'l' keys so the picker and light manager are called. """ keycode = event.GetKeyCode() modifiers = event.HasModifiers() camera = self.camera if keycode < 256: key = chr(keycode) if key == '-': camera.zoom(0.8) self.render() self._record_methods('camera.zoom(0.8)\nrender()') return if key in ['=', '+']: camera.zoom(1.25) self.render() self._record_methods('camera.zoom(1.25)\nrender()') return if key.lower() in ['q', 'e'] or keycode == wx.WXK_ESCAPE: self._disable_fullscreen() if key.lower() in ['w']: event.Skip() return if key.lower() in ['r']: self._record_methods('reset_zoom()') # Handle picking. if key.lower() in ['p']: # In wxPython-2.6, there appears to be a bug in # EVT_CHAR so that event.GetX() and event.GetY() are # not correct. Therefore the picker is called on # KeyUp. event.Skip() return # Camera focal point. if key.lower() in ['f']: event.Skip() return # Light configuration. if key.lower() in ['l'] and not modifiers: self.light_manager.configure() return if key.lower() in ['s'] and not modifiers: parent = self._vtk_control.GetParent() fname = popup_save(parent) if len(fname) != 0: self.save(fname) return shift = event.ShiftDown() if keycode == wx.WXK_LEFT: if shift: camera.yaw(-5) self._record_methods('camera.yaw(-5)') else: camera.azimuth(5) self._record_methods('camera.azimuth(5)') self.render() self._record_methods('render()') return elif keycode == wx.WXK_RIGHT: if shift: camera.yaw(5) self._record_methods('camera.yaw(5)') else: camera.azimuth(-5) self._record_methods('camera.azimuth(-5)') self.render() self._record_methods('render()') return elif keycode == wx.WXK_UP: if shift: camera.pitch(-5) self._record_methods('camera.pitch(-5)') else: camera.elevation(-5) self._record_methods('camera.elevation(-5)') camera.orthogonalize_view_up() self.render() self._record_methods('camera.orthogonalize_view_up()\nrender()') return elif keycode == wx.WXK_DOWN: if shift: camera.pitch(5) self._record_methods('camera.pitch(5)') else: camera.elevation(5) self._record_methods('camera.elevation(5)') camera.orthogonalize_view_up() self.render() self._record_methods('camera.orthogonalize_view_up()\nrender()') return self._vtk_control.OnKeyDown(event) # Skipping the event is not ideal but necessary because we # have no way of knowing of the event was really handled or # not and not skipping will break any keyboard accelerators. # In practice this does not seem to pose serious problems. event.Skip() def OnKeyUp(self, event): """This method is overridden to prevent the 's'/'w'/'e'/'q' keys from doing the default thing which is generally useless. It also handles the 'p' and 'l' keys so the picker and light manager are called. The 'f' key sets the camera focus. """ keycode = event.GetKeyCode() modifiers = event.HasModifiers() if keycode < 256: key = chr(keycode) if key.lower() in ['s', 'w', 'e', 'q']: event.Skip() return # Set camera focal point. if key.lower() in ['f']: if not modifiers: if sys.platform == 'darwin': x, y = self._interactor.event_position else: x = event.GetX() y = self._vtk_control.GetSize()[1] - event.GetY() data = self.picker.pick_world(x, y) coord = data.coordinate if coord is not None: self.camera.focal_point = coord self.render() self._record_methods('camera.focal_point = %r\n'\ 'render()'%list(coord)) return # Handle picking. if key.lower() in ['p']: if not modifiers: if sys.platform == 'darwin': x, y = self._interactor.event_position else: x = event.GetX() y = self._vtk_control.GetSize()[1] - event.GetY() self.picker.pick(x, y) return else: # This is here to disable VTK's own pick handler # which can get called when you press Alt/Ctrl + # 'p'. event.Skip() return # Light configuration. if key.lower() in ['l']: event.Skip() return self._vtk_control.OnKeyUp(event) event.Skip() def OnPaint(self, event): """This method is overridden temporarily in order to create the light manager. This is necessary because it makes sense to create the light manager only when the widget is realized. Only when the widget is realized is the VTK render window created and only then are the default lights all setup correctly. This handler is removed on the first Paint event and the default paint handler of the wxVTKRenderWindowInteractor is used instead.""" # Call the original handler (this will Show the widget) self._vtk_control.OnPaint(event) if len(self.renderer.lights) == 0: # The renderer is not ready yet, we do not do anything, and # we do not remove this callback, so that it will be called # later. return # Now create the light manager. self.light_manager = light_manager.LightManager(self) renwin = self._renwin renwin.update_traits() vtk_rw = tvtk.to_vtk(renwin) renwin.add_observer('StartEvent', messenger.send) messenger.connect(vtk_rw, 'StartEvent', self._start_event_callback) renwin.add_observer('EndEvent', messenger.send) messenger.connect(vtk_rw, 'EndEvent', self._end_event_callback) # Reset the event handler to the default since our job is done. wx.EVT_PAINT(self._vtk_control, None) # Remove the default handler. wx.EVT_PAINT(self._vtk_control, self._vtk_control.OnPaint) def OnSize(self, event): """Overrides the default OnSize in order to refresh the traits of the render window.""" if self._renwin is not None: self._vtk_control.OnSize(event) self._renwin.update_traits() def OnButtonDown(self, event): """Overrides the default on button down method. """ self._interacting = True self._vtk_control.OnButtonDown(event) def OnButtonUp(self, event): self._interacting = False self._vtk_control.OnButtonUp(event) ########################################################################### # Non-public interface. ########################################################################### def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ # Create the VTK widget. self._vtk_control = window = wxVTKRenderWindowInteractor(parent, -1, stereo=self.stereo) # Override these handlers. wx.EVT_CHAR(window, None) # Remove the default handler. wx.EVT_CHAR(window, self.OnKeyDown) wx.EVT_KEY_UP(window, None) # Remove the default handler. wx.EVT_KEY_UP(window, self.OnKeyUp) wx.EVT_PAINT(window, None) # Remove the default handler. wx.EVT_PAINT(window, self.OnPaint) wx.EVT_SIZE(window, None) # Remove the default handler. wx.EVT_SIZE(window, self.OnSize) # Override the button down and up handlers as well to note the # interaction. This is to toggle the busy status nicely. for evt in (wx.EVT_LEFT_DOWN, wx.EVT_RIGHT_DOWN, wx.EVT_MIDDLE_DOWN): evt(window, None) evt(window, self.OnButtonDown) for evt in (wx.EVT_LEFT_UP, wx.EVT_RIGHT_UP, wx.EVT_MIDDLE_UP): evt(window, None) evt(window, self.OnButtonUp) # Enable the widget. window.Enable(1) # Switch the default interaction style to the trackball one. window.GetInteractorStyle().SetCurrentStyleToTrackballCamera() # Grab the renderwindow. renwin = self._renwin = tvtk.to_tvtk(window.GetRenderWindow()) renwin.set(point_smoothing=self.point_smoothing, line_smoothing=self.line_smoothing, polygon_smoothing=self.polygon_smoothing) # Create a renderer and add it to the renderwindow self._renderer = tvtk.Renderer() renwin.add_renderer(self._renderer) # Save a reference to our camera so it is not GC'd -- needed for # the sync_traits to work. self._camera = self.camera # Sync various traits. self._renderer.background = self.background self.sync_trait('background', self._renderer) self.renderer.on_trait_change(self.render, 'background') self._camera.parallel_projection = self.parallel_projection self.sync_trait('parallel_projection', self._camera) renwin.off_screen_rendering = self.off_screen_rendering self.sync_trait('off_screen_rendering', self._renwin) self.render_window.on_trait_change(self.render, 'off_screen_rendering') self.render_window.on_trait_change(self.render, 'stereo_render') self.render_window.on_trait_change(self.render, 'stereo_type') self.camera.on_trait_change(self.render, 'parallel_projection') def _show_parent_hack(window, parent): """A hack to get the VTK scene properly setup for use.""" # Force the parent to show itself. parent.Show(1) # on some platforms, this SetSize() is necessary to cause # an OnPaint() when the event loop begins; else we get an # empty window until we force a redraw. window.SetSize(parent.GetSize()) # This is necessary on slow machines in order to force the # wx events to be handled. wx.GetApp().Yield(True) window.Render() if wx.Platform == '__WXMSW__': _show_parent_hack(window, parent) else: if (wx.VERSION[0] == 2) and (wx.VERSION[1] < 5): _show_parent_hack(window, parent) window.Update() # Because of the way the VTK widget is setup, and because we # set the size above, the window sizing is usually completely # messed up when the application window is shown. To work # around this a dynamic IDLE event handler is added and # immediately removed once it executes. This event handler # simply forces a resize to occur. The _idle_count allows us # to execute the idle function a few times (this seems to work # better). def _do_idle(event, window=window): w = wx.GetTopLevelParent(window) # Force a resize sz = w.GetSize() w.SetSize((sz[0]-1, sz[1]-1)) w.SetSize(sz) window._idle_count -= 1 if window._idle_count < 1: wx.EVT_IDLE(window, None) del window._idle_count window._idle_count = 2 wx.EVT_IDLE(window, _do_idle) self._interactor = tvtk.to_tvtk(window._Iren) return window def _lift(self): """Lift the window to the top. Useful when saving screen to an image.""" if self.render_window.off_screen_rendering: # Do nothing if off screen rendering is being used. return w = self._vtk_control while w and not w.IsTopLevel(): w = w.GetParent() if w: w.Raise() wx.GetApp().Yield(True) self.render() def _start_event_callback(self, obj, event): if self._interacting: return else: self.busy = True def _end_event_callback(self, obj, event): if self._interacting: return else: self.busy = False def _busy_changed(self, val): GUI.set_busy(val) def _full_screen_fired(self): fs = self._fullscreen if isinstance(fs, PopupScene): fs.close() self._fullscreen = None elif fs is None: ver = tvtk.Version() popup = False if wx.Platform == '__WXMSW__': popup = True elif ver.vtk_major_version > 5: popup = True elif (ver.vtk_major_version == 5) and \ ((ver.vtk_minor_version >= 1) or \ (ver.vtk_build_version > 2)): popup = True if popup: # There is a bug with earlier versions of VTK that # breaks reparenting a window which is why we test for # the version above. f = PopupScene(self) self._fullscreen = f f.fullscreen() else: f = FullScreen(self) f.run() # This will block. self._fullscreen = None def _disable_fullscreen(self): fs = self._fullscreen if isinstance(fs, PopupScene): fs.close() self._fullscreen = None mayavi-4.1.0/tvtk/pyface/ui/wx/init.py0000644000175100001440000000203611674464502020624 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Riverbank Computing Limited # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # #------------------------------------------------------------------------------ # Major package imports. import wx # Check the version number is late enough. if wx.VERSION < (2, 6): raise RuntimeError, "Need wx version 2.6 or higher, but got %s" % str(wx.VERSION) # It's possible that it has already been initialised. _app = wx.GetApp() if _app is None: _app = wx.PySimpleApp() # Before we can load any images we have to initialize wxPython's image # handlers. wx.InitAllImageHandlers() #### EOF ###################################################################### mayavi-4.1.0/tvtk/pyface/ui/wx/scene_editor.py0000644000175100001440000002275111674464502022332 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Authors: Prabhu Ramachandran # Robert Kern # #------------------------------------------------------------------------------ """ A `SceneEditor` for the `SceneModel`. """ import wx # Enthought library imports. from traits.api import Any, Bool, Callable from traitsui.wx.editor import Editor from traitsui.basic_editor_factory import BasicEditorFactory from decorated_scene import DecoratedScene ##################################################################### # `_SceneEditor` class ##################################################################### class _SceneEditor(Editor): """ An editor for SceneModels. """ # The editor is scrollable, so override the default. scrollable = Bool(True) # Internal GUI traits. _sizer = Any() _scene = Any() #### Public 'Editor' interface ############################################# def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory self.control = wx.Panel(parent, -1) self._sizer = wx.BoxSizer(wx.VERTICAL) self.control.SetSizer(self._sizer) assert self.value.scene_editor is None, \ "The SceneModel may only have one active editor!" self._create_scene() self.value.activated = True def update_editor(self): """ Updates the editor when the object trait changes external to the editor. """ # Everything should really be handled elsewhere in trait notifications. # Just pass here. pass def dispose(self): """ Disposes of the contents of an editor. """ # Remove notifications. self.value.closing = True self.value.scene_editor = None self._setup_scene_notifications(remove=True) # Remove the current scene. if self._scene is not None: self._scene.close() self._scene = None self._sizer = None # This will destroy self.control and all of its children, including the # scene's control. super(_SceneEditor, self).dispose() #### Private '_SceneEditor' interface ################################## def _create_scene(self): """ Create the TVTK scene widget. """ factory = self.factory self._scene = factory.scene_class(self.control) scene = self._scene self.value.scene_editor = scene # Disable rendering on the scene until we're finished. scene.disable_render = True # Add all of the actors in the current actor map. for obj, actors in self.value.actor_map.items(): self._add_actors_widgets(actors) # Add all of the actors in the current actor map. self._add_actors_widgets(self.value.actor_list) # Set up Traits notifications. self._setup_scene_notifications() # Re-enable rendering. scene.disable_render = False # Ensure the scene's wx control is sized to fill our view's area. Note # that the sizer doesn't automatically layout its contents upon adding # a new child so we have to force it to do a layout. self._sizer.Add(scene.control, 1, wx.EXPAND) self._sizer.Layout() wx.EVT_IDLE(scene.control, None) # Force a render. scene.render() def _setup_scene_notifications(self, remove=False): """ Set up or remove all of the Trait notifications that control the scene widget. """ traits_to_sync = ['foreground', 'anti_aliasing_frames', 'stereo', 'background', 'off_screen_rendering', 'polygon_smoothing', 'jpeg_progressive', 'point_smoothing', 'busy', 'disable_render', 'magnification', 'jpeg_quality', 'parallel_projection', 'line_smoothing'] model = self.value scene = self._scene if not remove: scene.set(**model.get(traits_to_sync)) for trait in traits_to_sync: scene.sync_trait(trait, model, mutual=True, remove=remove) model.on_trait_change( scene.render, name='do_render', remove=remove, ) model.on_trait_change( self._actors_changed, name='actor_map_items', remove=remove, ) model.on_trait_change( self._actor_map_changed, name='actor_map', remove=remove, ) model.on_trait_change( self._actor_list_items_changed, name='actor_list_items', remove=remove, ) model.on_trait_change( self._actor_list_changed, name='actor_list', remove=remove, ) def _actors_changed(self, event): """ Handle the event of the actors in the actor map changing. """ scene = self._scene # Temporarily turn off rendering. We (re)store the old value of # disable_render because it may already be True. old_disable_render = scene.disable_render scene.disable_render = True try: for obj, actors in event.removed.items(): self._remove_actors_widgets(actors) for obj, actors in event.added.items(): self._add_actors_widgets(actors) for obj, actors in event.changed.items(): # The actors in the event are the old ones. Grab the new ones # from the actor map itself. self._remove_actors_widgets(actors) self._add_actors_widgets(self.value.actor_map[obj]) finally: scene.disable_render = old_disable_render scene.render() def _actor_map_changed(self, object, name, old, new): """ Handle the case when the entire actor map is set to something else. """ scene = self._scene # Temporarily turn off rendering. We (re)store the old value of # disable_render because it may already be True. old_disable_render = scene.disable_render scene.disable_render = True try: for obj, actors in old.items(): self._remove_actors_widgets(actors) for obj, actors in new.items(): self._add_actors_widgets(actors) finally: scene.disable_render = old_disable_render scene.render() def _actor_list_items_changed(self, event): self._actor_list_changed(self.value, 'actor_list', event.removed, event.added) def _actor_list_changed(self, object, name, old, new): """ Handle the event of the actors in the actor map changing. """ scene = self._scene # Temporarily turn off rendering. We (re)store the old value of # disable_render because it may already be True. old_disable_render = scene.disable_render scene.disable_render = True try: self._remove_actors_widgets(old) self._add_actors_widgets(new) finally: scene.disable_render = old_disable_render scene.render() def _separate_actors_widgets(self, actors_widgets): """Given a sequence (or single) of actors or widgets, this returns a list of just the actors and another of just the widgets. """ if not hasattr(actors_widgets, '__getitem__'): actors_widgets = [actors_widgets] actors = [] widgets = [] for actor in actors_widgets: if actor.is_a('vtk3DWidget') or actor.is_a('vtkInteractorObserver'): widgets.append(actor) else: actors.append(actor) return actors, widgets def _add_actors_widgets(self, actors_widgets): """Add actors and widgets to scene.""" scene = self._scene actors, widgets = self._separate_actors_widgets(actors_widgets) scene.add_actors(actors) enabled_info = self.value.enabled_info for widget in widgets: scene.add_widgets(widget, enabled_info.get(widget, True)) def _remove_actors_widgets(self, actors_widgets): """Remove actors and widgets from scene.""" scene = self._scene actors, widgets = self._separate_actors_widgets(actors_widgets) scene.remove_actors(actors) scene.remove_widgets(widgets) ##################################################################### # `SceneEditor` class ##################################################################### class SceneEditor(BasicEditorFactory): """ A TraitsUI editor factory for SceneModel instances. """ # The class of the editor object to be constructed. klass = _SceneEditor # The class or factory function for creating the actual scene object. scene_class = Callable(DecoratedScene) #### EOF ####################################################################### mayavi-4.1.0/tvtk/pyface/ui/wx/decorated_scene.py0000644000175100001440000002733611674464502023002 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2006, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Authors: Prabhu Ramachandran , # Dave Peterson # #------------------------------------------------------------------------------ """ A VTK interactor scene which provides a convenient toolbar that allows the user to set the camera view, turn on the axes indicator, etc. """ # System imports. from os.path import dirname import wx # Enthought library imports. from pyface.api import ImageResource, FileDialog, OK from pyface.action.api import ToolBarManager, Group, Action from tvtk.api import tvtk from traits.api import Instance, false, List, Either # Local imports. from scene import Scene ########################################################################### # 'DecoratedScene' class ########################################################################### class DecoratedScene(Scene): """A VTK interactor scene which provides a convenient toolbar that allows the user to set the camera view, turn on the axes indicator etc. """ ####################################################################### # Traits ####################################################################### if hasattr(tvtk, 'OrientationMarkerWidget'): # The tvtk orientation marker widget. This only exists in VTK # 5.x. marker = Instance(tvtk.OrientationMarkerWidget, ()) # The tvtk axes that will be shown for the orientation. axes = Instance(tvtk.AxesActor, ()) else: marker = None axes = None # Determine if the orientation axis is shown or not. show_axes = false # The list of actions represented in the toolbar actions = List(Either(Action, Group)) ########################################################################## # `object` interface ########################################################################## def __init__(self, parent, **traits): super(DecoratedScene, self).__init__(parent, **traits) self._setup_axes_marker() def __get_pure_state__(self): """Allows us to pickle the scene.""" # The control attribute is not picklable since it is a VTK # object so we remove it. d = super(DecoratedScene, self).__get_pure_state__() for x in ['_content', '_panel', '_sizer', '_tool_bar', 'actions']: d.pop(x, None) return d ########################################################################## # Non-public interface. ########################################################################## def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. Overridden to wrap the Scene control within a panel that also contains a toolbar. """ # Create a panel as a wrapper of the scene toolkit control. This # allows us to also add additional controls. self._panel = wx.Panel(parent, -1, style=wx.CLIP_CHILDREN) self._sizer = wx.BoxSizer(wx.VERTICAL) self._panel.SetSizer(self._sizer) # Add our toolbar to the panel. tbm = self._get_tool_bar_manager() self._tool_bar = tbm.create_tool_bar(self._panel) self._sizer.Add(self._tool_bar, 0, wx.EXPAND) # Create the actual scene content self._content = super(DecoratedScene, self)._create_control( self._panel) self._sizer.Add(self._content, 1, wx.EXPAND) # Ensure the child controls are laid-out. self._sizer.Layout() return self._panel def _setup_axes_marker(self): axes = self.axes if axes is None: # For VTK versions < 5.0. return axes.set( normalized_tip_length=(0.4, 0.4, 0.4), normalized_shaft_length=(0.6, 0.6, 0.6), shaft_type='cylinder' ) p = axes.x_axis_caption_actor2d.caption_text_property axes.y_axis_caption_actor2d.caption_text_property = p axes.z_axis_caption_actor2d.caption_text_property = p p.set(color=(1,1,1), shadow=False, italic=False) self._background_changed(self.background) self.marker.set(key_press_activation=False) self.marker.orientation_marker = axes def _get_tool_bar_manager(self): """ Returns the tool_bar_manager for this scene. """ tbm = ToolBarManager( *self.actions ) return tbm def _get_image_path(self): """Returns the directory which contains the images used by the toolbar.""" # So that we can find the images. import tvtk.pyface.api return dirname(tvtk.pyface.api.__file__) def _toggle_projection(self): """ Toggle between perspective and parallel projection, this is used for the toolbar. """ if self._panel is not None: self.parallel_projection = not self.parallel_projection def _toggle_axes(self): """Used by the toolbar to turn on/off the axes indicator. """ if self._panel is not None: self.show_axes = not self.show_axes def _save_snapshot(self): """Invoked by the toolbar menu to save a snapshot of the scene to an image. Note that the extension of the filename determines what image type is saved. The default is PNG. """ if self._panel is not None: wildcard = "PNG images (*.png)|*.png|Determine by extension (*.*)|*.*" dialog = FileDialog( parent = self._panel, title = 'Save scene to image', action = 'save as', default_filename = "snapshot.png", wildcard = wildcard ) if dialog.open() == OK: # The extension of the path will determine the actual # image type saved. self.save(dialog.path) def _configure_scene(self): """Invoked when the toolbar icon for configuration is clicked. """ self.edit_traits() ###################################################################### # Trait handlers. ###################################################################### def _show_axes_changed(self): marker = self.marker if (self._vtk_control is not None) and (marker is not None): if not self.show_axes: marker.interactor = None marker.enabled = False else: marker.interactor = self.interactor marker.enabled = True self.render() def _background_changed(self, value): # Depending on the background, this sets the axes text and # outline color to something that should be visible. axes = self.axes if (self._vtk_control is not None) and (axes is not None): p = self.axes.x_axis_caption_actor2d.caption_text_property m = self.marker s = value[0] + value[1] + value[2] if s <= 1.0: p.color = (1,1,1) m.set_outline_color(1,1,1) else: p.color = (0,0,0) m.set_outline_color(0,0,0) self.render() def _actions_default(self): return [ Group( Action( image = ImageResource('16x16/x-axis', search_path = [self._get_image_path()], ), tooltip = "View along the -X axis", on_perform = self.x_minus_view, ), Action( image = ImageResource('16x16/x-axis', search_path = [self._get_image_path()], ), tooltip = "View along the +X axis", on_perform = self.x_plus_view, ), Action( image = ImageResource('16x16/y-axis', search_path = [self._get_image_path()], ), tooltip = "View along the -Y axis", on_perform = self.y_minus_view, ), Action( image = ImageResource('16x16/y-axis', search_path = [self._get_image_path()], ), tooltip = "View along the +Y axis", on_perform = self.y_plus_view, ), Action( image = ImageResource('16x16/z-axis', search_path = [self._get_image_path()], ), tooltip = "View along the -Z axis", on_perform = self.z_minus_view, ), Action( image = ImageResource('16x16/z-axis', search_path = [self._get_image_path()], ), tooltip = "View along the +Z axis", on_perform = self.z_plus_view, ), Action( image = ImageResource('16x16/isometric', search_path = [self._get_image_path()], ), tooltip = "Obtain an isometric view", on_perform = self.isometric_view, ), ), Group( Action( image = ImageResource('16x16/parallel', search_path = [self._get_image_path()], ), tooltip = 'Toggle parallel projection', style="toggle", on_perform = self._toggle_projection, checked = self.parallel_projection, ), Action( image = ImageResource('16x16/origin_glyph', search_path = [self._get_image_path()], ), tooltip = 'Toggle axes indicator', style="toggle", enabled=(self.marker is not None), on_perform = self._toggle_axes, checked = self.show_axes, ), Action( image = ImageResource('16x16/fullscreen', search_path = [self._get_image_path()], ), tooltip = 'Full Screen (press "q" or "e" or ESC to exit fullscreen)', style="push", on_perform = self._full_screen_fired, ), ), Group( Action( image = ImageResource('16x16/save', search_path = [self._get_image_path()], ), tooltip = "Save a snapshot of this scene", on_perform = self._save_snapshot, ), Action( image = ImageResource('16x16/configure', search_path = [self._get_image_path()], ), tooltip = 'Configure the scene', style="push", on_perform = self._configure_scene, ), ), ] mayavi-4.1.0/tvtk/pyface/ui/wx/__init__.py0000644000175100001440000000120711674464502021417 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: Enthought, Inc. # #------------------------------------------------------------------------------ """ Enthought pyface package component """ mayavi-4.1.0/tvtk/pyface/ui/wx/wxVTKRenderWindowInteractor.py0000644000175100001440000006174011674464502025276 0ustar ischnellusers00000000000000""" A VTK RenderWindowInteractor widget for wxPython. Find wxPython info at http://wxPython.org Created by Prabhu Ramachandran, April 2002 Based on wxVTKRenderWindow.py Fixes and updates by Charl P. Botha 2003-2008 Updated to new wx namespace and some cleaning up by Andrea Gavana, December 2006 """ """ Please see the example at the end of this file. ---------------------------------------- Creation: wxVTKRenderWindowInteractor(parent, ID, stereo=0, [wx keywords]): You should create a wx.PySimpleApp() or some other wx**App before creating the window. Behaviour: Uses __getattr__ to make the wxVTKRenderWindowInteractor behave just like a vtkGenericRenderWindowInteractor. ---------------------------------------- """ # import usual libraries import wx import vtk # wxPython 2.4.0.4 and newer prefers the use of True and False, standard # booleans in Python 2.2 but not earlier. Here we define these values if # they don't exist so that we can use True and False in the rest of the # code. At the time of this writing, that happens exactly ONCE in # CreateTimer() try: True except NameError: True = 1 False = 0 # a few configuration items, see what works best on your system # Use GLCanvas as base class instead of wx.Window. # This is sometimes necessary under wxGTK or the image is blank. # (in wxWindows 2.3.1 and earlier, the GLCanvas had scroll bars) baseClass = wx.Window if wx.Platform == "__WXGTK__": import wx.glcanvas baseClass = wx.glcanvas.GLCanvas # Keep capturing mouse after mouse is dragged out of window # (in wxGTK 2.3.2 there is a bug that keeps this from working, # but it is only relevant in wxGTK if there are multiple windows) _useCapture = (wx.Platform == "__WXMSW__") # for wx < 2.9.2, CmdDown is preferred to ControlDown since it catches the # meta key on OS X, but in later versions, CmdDown is depricated and its # functionality moved to ControlDown _useCmdDown = (wx.VERSION[0] < 2) or \ (wx.VERSION[0] == 2 and wx.VERSION[1] < 9) or \ (wx.VERSION[0] == 2 and wx.VERSION[1] == 9 and wx.VERSION[2] < 2) # end of configuration items class EventTimer(wx.Timer): """Simple wx.Timer class. """ def __init__(self, iren): """Default class constructor. @param iren: current render window """ wx.Timer.__init__(self) self.iren = iren def Notify(self): """ The timer has expired. """ self.iren.TimerEvent() class wxVTKRenderWindowInteractor(baseClass): """ A wxRenderWindow for wxPython. Use GetRenderWindow() to get the vtkRenderWindow. Create with the keyword stereo=1 in order to generate a stereo-capable window. """ # class variable that can also be used to request instances that use # stereo; this is overridden by the stereo=1/0 parameter. If you set # it to True, the NEXT instantiated object will attempt to allocate a # stereo visual. E.g.: # wxVTKRenderWindowInteractor.USE_STEREO = True # myRWI = wxVTKRenderWindowInteractor(parent, -1) USE_STEREO = False def __init__(self, parent, ID, *args, **kw): """Default class constructor. Parameters ---------- parent parent window ID window identifier kw wxPython keywords (position, size, style) plus the 'stereo' keyword """ # private attributes self.__RenderWhenDisabled = 0 # First do special handling of some keywords: # stereo, position, size, style stereo = 0 if kw.has_key('stereo'): if kw['stereo']: stereo = 1 del kw['stereo'] elif self.USE_STEREO: stereo = 1 position, size = wx.DefaultPosition, wx.DefaultSize if kw.has_key('position'): position = kw['position'] del kw['position'] if kw.has_key('size'): size = kw['size'] del kw['size'] # wx.WANTS_CHARS says to give us e.g. TAB # wx.NO_FULL_REPAINT_ON_RESIZE cuts down resize flicker under GTK style = wx.WANTS_CHARS | wx.NO_FULL_REPAINT_ON_RESIZE if kw.has_key('style'): style = style | kw['style'] del kw['style'] # the enclosing frame must be shown under GTK or the windows # don't connect together properly if wx.Platform != '__WXMSW__': l = [] p = parent while p: # make a list of all parents l.append(p) p = p.GetParent() l.reverse() # sort list into descending order for p in l: p.Show(1) if baseClass.__name__ == 'GLCanvas': # code added by cpbotha to enable stereo and double # buffering correctly where the user requests this; remember # that the glXContext in this case is NOT allocated by VTK, # but by WX, hence all of this. # Initialize GLCanvas with correct attriblist attribList = [wx.glcanvas.WX_GL_RGBA, wx.glcanvas.WX_GL_MIN_RED, 1, wx.glcanvas.WX_GL_MIN_GREEN, 1, wx.glcanvas.WX_GL_MIN_BLUE, 1, wx.glcanvas.WX_GL_DEPTH_SIZE, 16, wx.glcanvas.WX_GL_DOUBLEBUFFER] if stereo: attribList.append(wx.glcanvas.WX_GL_STEREO) try: baseClass.__init__(self, parent, ID, position, size, style, attribList=attribList) except wx.PyAssertionError: # visual couldn't be allocated, so we go back to default baseClass.__init__(self, parent, ID, position, size, style) if stereo: # and make sure everyone knows that the stereo # visual wasn't set. stereo = 0 else: baseClass.__init__(self, parent, ID, position, size, style) # create the RenderWindow and initialize it self._Iren = vtk.vtkGenericRenderWindowInteractor() self._Iren.SetRenderWindow( vtk.vtkRenderWindow() ) self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer) self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer) self._Iren.GetRenderWindow().AddObserver('CursorChangedEvent', self.CursorChangedEvent) try: self._Iren.GetRenderWindow().SetSize(size.width, size.height) except AttributeError: self._Iren.GetRenderWindow().SetSize(size[0], size[1]) if stereo: self._Iren.GetRenderWindow().StereoCapableWindowOn() self._Iren.GetRenderWindow().SetStereoTypeToCrystalEyes() self.__handle = None self.BindEvents() # with this, we can make sure that the reparenting logic in # Render() isn't called before the first OnPaint() has # successfully been run (and set up the VTK/WX display links) self.__has_painted = False # set when we have captured the mouse. self._own_mouse = False # used to store WHICH mouse button led to mouse capture self._mouse_capture_button = 0 # A mapping for cursor changes. self._cursor_map = {0: wx.CURSOR_ARROW, # VTK_CURSOR_DEFAULT 1: wx.CURSOR_ARROW, # VTK_CURSOR_ARROW 2: wx.CURSOR_SIZENESW, # VTK_CURSOR_SIZENE 3: wx.CURSOR_SIZENWSE, # VTK_CURSOR_SIZENWSE 4: wx.CURSOR_SIZENESW, # VTK_CURSOR_SIZESW 5: wx.CURSOR_SIZENWSE, # VTK_CURSOR_SIZESE 6: wx.CURSOR_SIZENS, # VTK_CURSOR_SIZENS 7: wx.CURSOR_SIZEWE, # VTK_CURSOR_SIZEWE 8: wx.CURSOR_SIZING, # VTK_CURSOR_SIZEALL 9: wx.CURSOR_HAND, # VTK_CURSOR_HAND 10: wx.CURSOR_CROSS, # VTK_CURSOR_CROSSHAIR } def BindEvents(self): """Binds all the necessary events for navigation, sizing, drawing. """ # refresh window by doing a Render self.Bind(wx.EVT_PAINT, self.OnPaint) # turn off background erase to reduce flicker self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None) # Bind the events to the event converters self.Bind(wx.EVT_RIGHT_DOWN, self.OnButtonDown) self.Bind(wx.EVT_LEFT_DOWN, self.OnButtonDown) self.Bind(wx.EVT_MIDDLE_DOWN, self.OnButtonDown) self.Bind(wx.EVT_RIGHT_UP, self.OnButtonUp) self.Bind(wx.EVT_LEFT_UP, self.OnButtonUp) self.Bind(wx.EVT_MIDDLE_UP, self.OnButtonUp) self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.Bind(wx.EVT_MOTION, self.OnMotion) # Bind the double clicks as well. self.Bind(wx.EVT_LEFT_DCLICK, self.OnButtonDown) self.Bind(wx.EVT_MIDDLE_DCLICK, self.OnButtonDown) self.Bind(wx.EVT_RIGHT_DCLICK, self.OnButtonDown) self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) # If we use EVT_KEY_DOWN instead of EVT_CHAR, capital versions # of all characters are always returned. EVT_CHAR also performs # other necessary keyboard-dependent translations. self.Bind(wx.EVT_CHAR, self.OnKeyDown) self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) self.Bind(wx.EVT_SIZE, self.OnSize) # the wx 2.8.7.1 documentation states that you HAVE to handle # this event if you make use of CaptureMouse, which we do. if _useCapture and hasattr(wx, 'EVT_MOUSE_CAPTURE_LOST'): self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.OnMouseCaptureLost) def __getattr__(self, attr): """Makes the object behave like a vtkGenericRenderWindowInteractor. """ if attr == '__vtk__': return lambda t=self._Iren: t elif hasattr(self._Iren, attr): return getattr(self._Iren, attr) else: raise AttributeError, self.__class__.__name__ + \ " has no attribute named " + attr def CreateTimer(self, obj, evt): """ Creates a timer. """ self._timer = EventTimer(self) self._timer.Start(10, True) def DestroyTimer(self, obj, evt): """The timer is a one shot timer so will expire automatically. """ return 1 def _CursorChangedEvent(self, obj, evt): """Change the wx cursor if the renderwindow's cursor was changed. """ cur = self._cursor_map[obj.GetCurrentCursor()] c = wx.StockCursor(cur) self.SetCursor(c) def CursorChangedEvent(self, obj, evt): """Called when the CursorChangedEvent fires on the render window.""" # This indirection is needed since when the event fires, the # current cursor is not yet set so we defer this by which time # the current cursor should have been set. wx.CallAfter(self._CursorChangedEvent, obj, evt) def HideCursor(self): """Hides the cursor.""" c = wx.StockCursor(wx.CURSOR_BLANK) self.SetCursor(c) def ShowCursor(self): """Shows the cursor.""" rw = self._Iren.GetRenderWindow() cur = self._cursor_map[rw.GetCurrentCursor()] c = wx.StockCursor(cur) self.SetCursor(c) def GetDisplayId(self): """Function to get X11 Display ID from WX and return it in a format that can be used by VTK Python. We query the X11 Display with a new call that was added in wxPython 2.6.0.1. The call returns a SWIG object which we can query for the address and subsequently turn into an old-style SWIG-mangled string representation to pass to VTK. """ d = None try: d = wx.GetXDisplay() except NameError: # wx.GetXDisplay was added by Robin Dunn in wxPython 2.6.0.1 # if it's not available, we can't pass it. In general, # things will still work; on some setups, it'll break. pass else: # wx returns None on platforms where wx.GetXDisplay is not relevant if d: d = hex(d) # On wxPython-2.6.3.2 and above there is no leading '0x'. if not d.startswith('0x'): d = '0x' + d # we now have 0xdeadbeef # VTK wants it as: _deadbeef_void_p (pre-SWIG-1.3 style) d = '_%s_%s\0' % (d[2:], 'void_p') return d def OnMouseCaptureLost(self, event): """This is signalled when we lose mouse capture due to an external event, such as when a dialog box is shown. See the wx documentation. """ # the documentation seems to imply that by this time we've # already lost capture. I have to assume that we don't need # to call ReleaseMouse ourselves. if _useCapture and self._own_mouse: self._own_mouse = False def OnPaint(self,event): """Handles the wx.EVT_PAINT event for wxVTKRenderWindowInteractor. """ # wx should continue event processing after this handler. # We call this BEFORE Render(), so that if Render() raises # an exception, wx doesn't re-call OnPaint repeatedly. event.Skip() dc = wx.PaintDC(self) # Check that a renderwindow exists, if not do nothing. rw = self._Iren.GetRenderWindow() if rw is None: return # make sure the RenderWindow is sized correctly rw.SetSize(*self.GetSizeTuple()) # Tell the RenderWindow to render inside the wx.Window. if not self.__handle: # on relevant platforms, set the X11 Display ID d = self.GetDisplayId() if d: self._Iren.GetRenderWindow().SetDisplayId(d) # store the handle self.__handle = self.GetHandle() # and give it to VTK self._Iren.GetRenderWindow().SetWindowInfo(str(self.__handle)) # now that we've painted once, the Render() reparenting logic # is safe self.__has_painted = True self.Render() def OnSize(self,event): """Handles the wx.EVT_SIZE event for wxVTKRenderWindowInteractor. """ # event processing should continue (we call this before the # Render(), in case it raises an exception) event.Skip() try: width, height = event.GetSize() except: width = event.GetSize().width height = event.GetSize().height self._Iren.SetSize(width, height) self._Iren.ConfigureEvent() # this will check for __handle self.Render() def CommandDown(self, event): if _useCmdDown: return event.CmdDown() else: return event.ControlDown() def OnMotion(self,event): """Handles the wx.EVT_MOTION event for wxVTKRenderWindowInteractor. """ # event processing should continue # we call this early in case any of the VTK code raises an # exception. event.Skip() self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(), self.CommandDown(event), event.ShiftDown(), chr(0), 0, None) self._Iren.MouseMoveEvent() def OnEnter(self,event): """Handles the wx.EVT_ENTER_WINDOW event for wxVTKRenderWindowInteractor. """ # event processing should continue event.Skip() self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(), self.CommandDown(event), event.ShiftDown(), chr(0), 0, None) self._Iren.EnterEvent() def OnLeave(self,event): """Handles the wx.EVT_LEAVE_WINDOW event for wxVTKRenderWindowInteractor. """ # event processing should continue event.Skip() self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(), self.CommandDown(event), event.ShiftDown(), chr(0), 0, None) self._Iren.LeaveEvent() def OnButtonDown(self,event): """Handles the wx.EVT_LEFT/RIGHT/MIDDLE_DOWN events for wxVTKRenderWindowInteractor. """ # allow wx event processing to continue # on wxPython 2.6.0.1, omitting this will cause problems with # the initial focus, resulting in the wxVTKRWI ignoring keypresses # until we focus elsewhere and then refocus the wxVTKRWI frame # we do it this early in case any of the following VTK code # raises an exception. event.Skip() ctrl, shift = self.CommandDown(event), event.ShiftDown() self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(), ctrl, shift, chr(0), 0, None) button = 0 if event.RightIsDown(): self._Iren.RightButtonPressEvent() button = 'Right' elif event.LeftIsDown(): self._Iren.LeftButtonPressEvent() button = 'Left' elif event.MiddleIsDown(): self._Iren.MiddleButtonPressEvent() button = 'Middle' # save the button and capture mouse until the button is released # we only capture the mouse if it hasn't already been captured if _useCapture and not self._own_mouse: self._own_mouse = True self._mouse_capture_button = button self.CaptureMouse() def OnButtonUp(self,event): """Handles the wx.EVT_LEFT/RIGHT/MIDDLE_UP events for wxVTKRenderWindowInteractor. """ # event processing should continue event.Skip() button = 0 if event.RightUp(): button = 'Right' elif event.LeftUp(): button = 'Left' elif event.MiddleUp(): button = 'Middle' # if the same button is released that captured the mouse, and # we have the mouse, release it. # (we need to get rid of this as soon as possible; if we don't # and one of the event handlers raises an exception, mouse # is never released.) if _useCapture and self._own_mouse and \ button==self._mouse_capture_button: self.ReleaseMouse() self._own_mouse = False ctrl, shift = self.CommandDown(event), event.ShiftDown() self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(), ctrl, shift, chr(0), 0, None) if button == 'Right': self._Iren.RightButtonReleaseEvent() elif button == 'Left': self._Iren.LeftButtonReleaseEvent() elif button == 'Middle': self._Iren.MiddleButtonReleaseEvent() def OnMouseWheel(self,event): """Handles the wx.EVT_MOUSEWHEEL event for wxVTKRenderWindowInteractor. """ # event processing should continue event.Skip() ctrl, shift = self.CommandDown(event), event.ShiftDown() self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(), ctrl, shift, chr(0), 0, None) if event.GetWheelRotation() > 0: self._Iren.MouseWheelForwardEvent() else: self._Iren.MouseWheelBackwardEvent() def OnKeyDown(self,event): """Handles the wx.EVT_KEY_DOWN event for wxVTKRenderWindowInteractor. """ # event processing should continue event.Skip() ctrl, shift = self.CommandDown(event), event.ShiftDown() keycode, keysym = event.GetKeyCode(), None key = chr(0) if keycode < 256: key = chr(keycode) # wxPython 2.6.0.1 does not return a valid event.Get{X,Y}() # for this event, so we use the cached position. (x,y)= self._Iren.GetEventPosition() self._Iren.SetEventInformation(x, y, ctrl, shift, key, 0, keysym) self._Iren.KeyPressEvent() self._Iren.CharEvent() def OnKeyUp(self,event): """Handles the wx.EVT_KEY_UP event for wxVTKRenderWindowInteractor. """ # event processing should continue event.Skip() ctrl, shift = self.CommandDown(event), event.ShiftDown() keycode, keysym = event.GetKeyCode(), None key = chr(0) if keycode < 256: key = chr(keycode) self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(), ctrl, shift, key, 0, keysym) self._Iren.KeyReleaseEvent() def GetRenderWindow(self): """Returns the render window (vtkRenderWindow). """ return self._Iren.GetRenderWindow() def Render(self): """Actually renders the VTK scene on screen. """ RenderAllowed = 1 if not self.__RenderWhenDisabled: # the user doesn't want us to render when the toplevel frame # is disabled - first find the top level parent topParent = wx.GetTopLevelParent(self) if topParent: # if it exists, check whether it's enabled # if it's not enabeld, RenderAllowed will be false RenderAllowed = topParent.IsEnabled() if RenderAllowed: if self.__handle and self.__handle == self.GetHandle(): self._Iren.GetRenderWindow().Render() elif self.GetHandle() and self.__has_painted: # this means the user has reparented us; let's adapt to the # new situation by doing the WindowRemap dance self._Iren.GetRenderWindow().SetNextWindowInfo( str(self.GetHandle())) # make sure the DisplayId is also set correctly d = self.GetDisplayId() if d: self._Iren.GetRenderWindow().SetDisplayId(d) # do the actual remap with the new parent information self._Iren.GetRenderWindow().WindowRemap() # store the new situation self.__handle = self.GetHandle() self._Iren.GetRenderWindow().Render() def SetRenderWhenDisabled(self, newValue): """Change value of __RenderWhenDisabled ivar. If __RenderWhenDisabled is false (the default), this widget will not call Render() on the RenderWindow if the top level frame (i.e. the containing frame) has been disabled. This prevents recursive rendering during wx.SafeYield() calls. wx.SafeYield() can be called during the ProgressMethod() callback of a VTK object to have progress bars and other GUI elements updated - it does this by disabling all windows (disallowing user-input to prevent re-entrancy of code) and then handling all outstanding GUI events. However, this often triggers an OnPaint() method for wxVTKRWIs, resulting in a Render(), resulting in Update() being called whilst still in progress. """ self.__RenderWhenDisabled = bool(newValue) #-------------------------------------------------------------------- def wxVTKRenderWindowInteractorConeExample(): """Like it says, just a simple example """ # every wx app needs an app app = wx.PySimpleApp() # create the top-level frame, sizer and wxVTKRWI frame = wx.Frame(None, -1, "wxVTKRenderWindowInteractor", size=(400,400)) widget = wxVTKRenderWindowInteractor(frame, -1) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(widget, 1, wx.EXPAND) frame.SetSizer(sizer) frame.Layout() # It would be more correct (API-wise) to call widget.Initialize() and # widget.Start() here, but Initialize() calls RenderWindow.Render(). # That Render() call will get through before we can setup the # RenderWindow() to render via the wxWidgets-created context; this # causes flashing on some platforms and downright breaks things on # other platforms. Instead, we call widget.Enable(). This means # that the RWI::Initialized ivar is not set, but in THIS SPECIFIC CASE, # that doesn't matter. widget.Enable(1) widget.AddObserver("ExitEvent", lambda o,e,f=frame: f.Close()) ren = vtk.vtkRenderer() widget.GetRenderWindow().AddRenderer(ren) cone = vtk.vtkConeSource() cone.SetResolution(8) coneMapper = vtk.vtkPolyDataMapper() coneMapper.SetInput(cone.GetOutput()) coneActor = vtk.vtkActor() coneActor.SetMapper(coneMapper) ren.AddActor(coneActor) # show the window frame.Show() app.MainLoop() if __name__ == "__main__": wxVTKRenderWindowInteractorConeExample() mayavi-4.1.0/tvtk/pyface/ui/wx/actor_editor.py0000644000175100001440000002001611674464502022335 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2007, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Authors: Robert Kern # Prabhu Ramachandran # #------------------------------------------------------------------------------ """ A mostly-general Traits UI editor for viewing things in TVTK scenes. """ # Major library imports. import wx # Enthought library imports. from traits.api import Any, Bool, Callable, Dict, Str from traitsui.wx.editor import Editor from traitsui.basic_editor_factory import BasicEditorFactory from decorated_scene import DecoratedScene ##################################################################### # `_ActorEditor` class ##################################################################### class _ActorEditor(Editor): """ An editor for TVTK scenes. """ # The editor is scrollable, so override the default. scrollable = Bool(True) # Internal GUI traits. _sizer = Any() _scene = Any() #### Public 'Editor' interface ############################################# def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory self.control = wx.Panel(parent, -1) self._sizer = wx.BoxSizer(wx.VERTICAL) self.control.SetSizer(self._sizer) self._create_scene() def update_editor(self): """ Updates the editor when the object trait changes external to the editor. """ # Everything should really be handled elsewhere in trait notifications. # Just pass here. pass def dispose(self): """ Disposes of the contents of an editor. """ # Remove notifications. self._setup_scene_notifications(remove=True) # Remove the current scene. if self._scene is not None: self._scene.close() self._scene = None self._sizer = None # This will destroy self.control and all of its children, including the # scene's control. super(_ActorEditor, self).dispose() #### Private '_ActorEditor' interface ################################## def _create_scene(self): """ Create the TVTK scene widget. """ factory = self.factory self._scene = factory.scene_class(self.control, **factory.scene_kwds) scene = self._scene # Disable rendering on the scene until we're finished. scene.disable_render = True # Add all of the actors in the current actor map. for obj, actors in self.value.items(): self._add_actors_widgets(actors) # Set up Traits notifications. self._setup_scene_notifications() # Re-enable rendering. scene.disable_render = False # Ensure the scene's wx control is sized to fill our view's area. Note # that the sizer doesn't automatically layout its contents upon adding # a new child so we have to force it to do a layout. self._sizer.Add(scene.control, 1, wx.EXPAND) self._sizer.Layout() wx.EVT_IDLE(scene.control, None) # Force a render. scene.render() def _setup_scene_notifications(self, remove=False): """ Set up or remove all of the Trait notifications that control the scene widget. """ self.object.on_trait_change( self._set_scene_disable_render, name=self.factory.disable_render_name, remove=remove, ) self.object.on_trait_event( self._scene.render, name=self.factory.do_render_name, remove=remove, ) self.object.on_trait_change( self._actors_changed, name=self.name+'_items', remove=remove, ) self.object.on_trait_change( self._actor_map_changed, name=self.name, remove=remove, ) def _set_scene_disable_render(self, new): """ A callback for Traits notifications. """ self._scene.disable_render = new def _actors_changed(self, event): """ Handle the event of the actors in the actor map changing. """ scene = self._scene # Temporarily turn off rendering. We (re)store the old value of # disable_render because it may already be True. old_disable_render = scene.disable_render scene.disable_render = True try: for obj, actors in event.removed.items(): self._remove_actors_widgets(actors) for obj, actors in event.added.items(): self._add_actors_widgets(actors) for obj, actors in event.changed.items(): # The actors in the event are the old ones. Grab the new ones # from the actor map itself. self._remove_actors_widgets(actors) self._add_actors_widgets(self.value[obj]) finally: scene.disable_render = old_disable_render scene.render() def _actor_map_changed(self, object, name, old, new): """ Handle the case when the entire actor map is set to something else. """ scene = self._scene # Temporarily turn off rendering. We (re)store the old value of # disable_render because it may already be True. old_disable_render = scene.disable_render scene.disable_render = True try: for obj, actors in old.items(): self._remove_actors_widgets(actors) for obj, actors in new.items(): self._add_actors_widgets(actors) finally: scene.disable_render = old_disable_render scene.render() def _separate_actors_widgets(self, actors_widgets): """Given a sequence (or single) of actors or widgets, this returns a list of just the actors and another of just the widgets. """ if not hasattr(actors_widgets, '__getitem__'): actors_widgets = [actors_widgets] actors = [] widgets = [] for actor in actors_widgets: if actor.is_a('vtk3DWidget'): widgets.append(actor) else: actors.append(actor) return actors, widgets def _add_actors_widgets(self, actors_widgets): """Add actors and widgets to scene.""" scene = self._scene actors, widgets = self._separate_actors_widgets(actors_widgets) scene.add_actors(actors) scene.add_widgets(widgets) def _remove_actors_widgets(self, actors_widgets): """Remove actors and widgets from scene.""" scene = self._scene actors, widgets = self._separate_actors_widgets(actors_widgets) scene.remove_actors(actors) scene.remove_widgets(widgets) ##################################################################### # `ActorEditor` class ##################################################################### class ActorEditor(BasicEditorFactory): """ An editor factory for TVTK scenes. """ # The class of the editor object to be constructed. klass = _ActorEditor # The class or factory function for creating the actual scene object. scene_class = Callable(DecoratedScene) # Keyword arguments to pass to the scene factory. scene_kwds = Dict() # The name of the trait used for ITVTKActorModel.disable_render. disable_render_name = Str('disable_render') # The name of the trait used for ITVTKActorModel.do_render. do_render_name = Str('do_render') #### EOF ####################################################################### mayavi-4.1.0/tvtk/pyface/ui/null/0000755000175100001440000000000011674464502017622 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/pyface/ui/null/init.py0000644000175100001440000000137011674464502021140 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2007, Riverbank Computing Limited # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt #------------------------------------------------------------------------------ """ Initialize this backend. """ # There is nothing for us to initialize, but the toolkit switching code depends # on the existence of this module. #### EOF ###################################################################### mayavi-4.1.0/tvtk/pyface/ui/null/__init__.py0000644000175100001440000000120711674464502021733 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2008, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: Enthought, Inc. # #------------------------------------------------------------------------------ """ Enthought pyface package component """ mayavi-4.1.0/tvtk/pyface/ui/__init__.py0000644000175100001440000000120711674464502020761 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # # Copyright (c) 2008, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # # Thanks for using Enthought open source! # # Author: Enthought, Inc. # #------------------------------------------------------------------------------ """ Enthought pyface package component """ mayavi-4.1.0/tvtk/pyface/images/0000755000175100001440000000000011674464502017500 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/pyface/images/16x16/0000755000175100001440000000000011674464502020265 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/pyface/images/16x16/y-axis.png0000644000175100001440000000107311674464502022206 0ustar ischnellusers00000000000000PNG  IHDRabKGD pHYs  tIME 9 zzIDAT8˭kA?oy{D("AAN1N V m vI lF?@Žٱ\R}͗>? 0 xPk!8X0ʗd;p>jN;% 3eWH;愳S{yrJ'Wu8:[_9p~nC~r~'/M2u4sG^?Jy>{F===m[x0Q5f-K̵.ܝ}q P$h ܿ_ 844@sh[Nn\>R21:Rvv,RӬtˤMrk;*b;WfACfXv UI*Yr |O'{: &E @2sgf_/6o%IENDB`mayavi-4.1.0/tvtk/pyface/images/16x16/save.png0000644000175100001440000000114111674464502021726 0ustar ischnellusers00000000000000PNG  IHDRa(IDAT8}kTA?]QeXX )*6vV!ZQc\wogweewu/]Y|p\H)[8D^ZՕ޿[[*LrE;k4Fu+<,1FTx9cRBM֑fwUY H"-D٨Ǒ HDZ kLq#4 ) D,iGBA ( $fʇ9w $5u.wP5f)b'fk*rKȤp뜙'aiO>5$E;D"s|/_m߇rʪ rJhobb;jӔ}$F4g[&w:hD E8vm|gfejji(yZnpnkooopppqqqvvvwww|vr/yyy{{{|||}}}~~~~LPk]y宨|x»ě˷d&tRNS@fbKGDH pHYs  ~tIMEIUIDATxc`UQ}$PQĐ "H|[w]'QKO~ "dj)yi\T98<@@R͗.Аn?PXN. ;Y!(GIENDB`mayavi-4.1.0/tvtk/pyface/images/16x16/fullscreen.png0000644000175100001440000000122611674464502023136 0ustar ischnellusers00000000000000PNG  IHDRabKGDhv|ay pHYs  tIME  78E#IDAT8˥KHTQ;D*&ZDBi WTJ .u0J$+prQ֦Y52H:8ŝ0ТwsW ^Z'I ?08~L0/7@q2ȼ o$tҫvwc+A,ކ0l4Gf6N,#b`x4t34]`o{Iڤ @ <H?A[hҢJ+]6l,SxQg ;A,i7I:bq/E4Т|yby?hKuӭB @ _+[?Kit%4eKA͛(pKjy?P))vXHa- BR%b,4ARӨ`#b@Jb0Q+  5J"AS=o#6Aà b*l&C30bF`<*˽!qaN׉ⅪbC͛-R-U/Tp;Yӹ 66ӐmZGL<ؑO.r<|fEY/9tmza ?1yI3󲦧sy0ʝK$͔L=pE9 >XJs*D I+@I+;oט]| `DJ@3+h*g_J79:Z(, ?&_|dye=:93Ay4vIG`0uF $iܩt Ǘ/۔Bᘹ5/~:&g9IENDB`mayavi-4.1.0/tvtk/pyface/images/16x16/configure.png0000644000175100001440000000202111674464502022747 0ustar ischnellusers00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb?% XJK; f100+rrR8e55i+;x~GbIŮ55dcc>~|]RA##T%a* rrꊞ*< L >ccc)w X~Ġ1p -FF`^GG6>>N70^ׯ&߿4$$$W mãG}:ADAZZAHt!!aǏoܸ| @A @3,[wihY\#'' 6`ݺ_ϟg & ر**2.]:=a߾ˁ1.!!Y ӇoY޽{k/_]AFFMCC_'$$AO߿c^F ,-YLÇ>b>u7o^~5+_JIIs XZZ)(H1/2,Z(@xx?VPPa`aaPY hjwA\\AJJ(W _5W qq `%++.Gϟ89ف)ܹ8q`ӧ˯_?,X0@\moQAQQ 8gP`FP͛gW^jy X@ĭ[WWp缼ڥrr߿bsP>0a2<{f2@0 ܹss %(qrr1̜9=z/F=ã  ,-mqssCX=;&cg+IENDB`mayavi-4.1.0/tvtk/pyface/images/16x16/image_LICENSE.txt0000644000175100001440000000144411674464502023255 0ustar ischnellusers00000000000000The icons are mostly derived work from other icons. As such they are licensed accordingly to the original license: GV: Gael Varoquaux: BSD-like Enthought: BSD-like Nuvola: LGPL Crystal: LGPL Unless stated in this file, icons are work of enthought, and are released under BSD-like license. Files and orginal authors: ---------------------------------------------------------------- configure.png | Nuvola fullscreen.png | GV isometric.png | Nuvola origin_glyph.png | Enthought parallel.png | GV save.png | Crystal x-axis.png | Eclipse y-axis.png | Eclipse z-axis.png | Eclipse mayavi-4.1.0/tvtk/pyface/images/16x16/parallel.png0000644000175100001440000000102611674464502022566 0ustar ischnellusers00000000000000PNG  IHDRa pHYs  tIME Mh IDAT8˝jA]#ċ "$$!xw! xBKbg&鞮*îYAuAT( N[2ȯuK.rXkNͻ(8(* ӆTs$0~ʓ=V28@DaI){K+{_0OA%Ԧi$P֊I`{;w4 *I4N)) .⽶(L20'IhQq'0vd~OrL4mk+)>o㲜6XĴmӤj+ ! VcԸM2`$QBl* f~g71?;g7YA' J9ASI%1֔rTN{}1⫣3{P7%̒)IENDB`mayavi-4.1.0/tvtk/pyface/images/16x16/isometric.png0000644000175100001440000000147611674464502023001 0ustar ischnellusers00000000000000PNG  IHDRabKGDxӖ+ pHYs  tIME 5IDAT8eou?ܙ;M6H`Z M0!F7 M!$,э&t  HB"Z->-t{c1H=|=G}પrË͉ oܵ[w&ſG>wtz>|kצ[ގ =5yו?n߸ ^;ua}o귤+Aj AkHrt` fqiTtiQ=MNdaƀ=J5Sd˯Ɇ%udF Z[ hÃA"'p{&`hfJ.<^nm]b!-iI|)M&XnנcP1` Y`KՋX` .`H[A|6('1Pe'!6؀-2"?x[k 5h] dC1D!(%<, `[P/Cb`~)3.LAɮMjLAB$@΂N*8 JR?>8|,V j,j_C߻2%6[ }c?ضkh J@؅6߂3?H>Q-]rF4>9g)c;"kp/o\W^.Woa[GkͯzssIENDB`mayavi-4.1.0/tvtk/pyface/api.py0000644000175100001440000000010311674464502017350 0ustar ischnellusers00000000000000from decorated_scene import DecoratedScene from scene import Scene mayavi-4.1.0/tvtk/pyface/actors.py0000644000175100001440000001000711674464502020076 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: #------------------------------------------------------------------------------ """Helper functions to make a bunch of simple actors. This is useful when writing demo/example code. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. from tvtk.api import tvtk from vtk.util import colors def axes_actor(origin=(0, 0, 0), scale_factor=1.0, radius=0.02, sides=12): """Creates a simple axes actor and returns a tvtk.Actor object.""" axes = tvtk.Axes(origin=origin, scale_factor=scale_factor, symmetric=1) tube = tvtk.TubeFilter(radius=radius, number_of_sides=sides, vary_radius='vary_radius_off', input=axes.output) mapper = tvtk.PolyDataMapper(input=tube.output) actor = tvtk.Actor(mapper=mapper) return actor def cone_actor(center=(0, 0, 0), height=1.0, radius=0.5, direction=(1, 0, 0), resolution=100, color=colors.red, opacity=1.0): """ Sets up a cone actor and returns the tvtk.Actor object.""" source = tvtk.ConeSource(center=center, height=height, radius=radius, direction=direction, resolution=resolution) mapper = tvtk.PolyDataMapper(input=source.output) p = tvtk.Property(opacity=opacity, color=color) actor = tvtk.Actor(mapper=mapper, property=p) return actor def cube_actor(center=(0, 0, 0), color=colors.blue, opacity=1.0): """ Creates a cube and returns the tvtk.Actor. """ source = tvtk.CubeSource(center=center) mapper = tvtk.PolyDataMapper(input=source.output) p = tvtk.Property(opacity=opacity, color=color) actor = tvtk.Actor(mapper=mapper, property=p) return actor def cylinder_actor(center=(0, 0, 0), radius=0.5, resolution=64, color=colors.green, opacity=1.0): """ Creates a cylinder and returns a tvtk.Actor. """ source = tvtk.CylinderSource(center=center, radius=radius, resolution=resolution) mapper = tvtk.PolyDataMapper(input=source.output) prop = tvtk.Property(opacity=opacity, color=color) actor = tvtk.Actor(mapper=mapper, property=prop) return actor def earth_actor(radius=0.5, opacity=1.0): """ Creates an earth source and returns the actor. """ source = tvtk.EarthSource(radius=radius, on_ratio=16, outline=0) mapper = tvtk.PolyDataMapper(input=source.output) prop = tvtk.Property(opacity=opacity) actor = tvtk.Actor(mapper=mapper, property=prop) return actor def sphere_actor(center=(0, 0, 0), radius=0.5, resolution=32, color=colors.purple, opacity=1.0): """ Creates a sphere and returns the actor. """ source = tvtk.SphereSource(center=center, radius=radius, theta_resolution=resolution, phi_resolution=resolution) mapper = tvtk.PolyDataMapper(input=source.output) prop = tvtk.Property(opacity=opacity, color=color) actor = tvtk.Actor(mapper=mapper, property=prop) return actor def arrow_actor(color=colors.peacock, opacity=1.0, resolution=24): """ Creates a 3D Arrow and returns an actor. """ source = tvtk.ArrowSource(tip_resolution=resolution, shaft_resolution=resolution) mapper = tvtk.PolyDataMapper(input=source.output) prop = tvtk.Property(opacity=opacity, color=color) actor = tvtk.Actor(mapper=mapper, property=prop) return actor mayavi-4.1.0/tvtk/pyface/toolkit.py0000644000175100001440000000615411674464502020300 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2007, Riverbank Computing Limited # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # #------------------------------------------------------------------------------ # Standard library imports. import sys # Enthought library imports. from traits.etsconfig.api import ETSConfig # This is set to the toolkit selection. _toolkit = None def _init_toolkit(): """ Initialise the current toolkit. """ def import_toolkit(tk): try: # Try and import the toolkit's pyface backend init module. __import__('tvtk.pyface.ui.%s.init' % tk) except: raise if ETSConfig.toolkit: # If the toolkit has been explicitly specified, just import it and # allow failure if an exception is thrown. import_toolkit(ETSConfig.toolkit) tk = ETSConfig.toolkit else: # Toolkits to check for if none is explicitly specified. known_toolkits = ('wx', 'qt4', 'null') for tk in known_toolkits: try: import_toolkit(tk) # In case we have just decided on a toolkit, tell everybody else. ETSConfig.toolkit = tk break except ImportError: pass else: # Try to import the null toolkit but don't set the ETSConfig toolkit try: tk = 'null' import_toolkit(tk) import warnings warnings.warn("Unable to import the %s backend for pyface;"\ " using the 'null' toolkit instead." % ", ".join(toolkits)) except: raise ImportError("unable to import a pyface backend for any of the %s toolkits" \ % ", ".join(known_toolkits)) # Save the imported toolkit name. global _toolkit _toolkit = tk # Do this once then disappear. _init_toolkit() del _init_toolkit def toolkit_object(name): """ Return the toolkit specific object with the given name. The name consists of the relative module path and the object name separated by a colon. """ mname, oname = name.split(':') be = 'tvtk.pyface.ui.%s.' % _toolkit be_mname = be + mname class Unimplemented(object): """ This is returned if an object isn't implemented by the selected toolkit. It raises an exception if it is ever instantiated. """ def __init__(self, *args, **kwargs): raise NotImplementedError("the %s pyface backend doesn't implement %s" % (be, oname)) be_obj = Unimplemented try: __import__(be_mname) try: be_obj = getattr(sys.modules[be_mname], oname) except AttributeError: pass except ImportError: pass return be_obj mayavi-4.1.0/tvtk/pyface/scene_model.py0000644000175100001440000002703411674464502021070 0ustar ischnellusers00000000000000""" TODO: * We need a cleaner way to manipulate the camera and other things as a model. The current approach will be far from perfect but should work. Caveats: * You can't have multiple views of the model. This will not be supported. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Dict, Event, \ Instance, List, Property from traitsui.api import View, Group, Item, InstanceEditor from tvtk.pyface.tvtk_scene import TVTKScene ##################################################################### # `SceneModelError` class ##################################################################### class SceneModelError(Exception): pass ##################################################################### # `SceneModel` class ##################################################################### class SceneModel(TVTKScene): ######################################## # TVTKScene traits. light_manager = Property picker = Property ######################################## # SceneModel traits. # A convenient dictionary based interface to add/remove actors and widgets. # This is similar to the interface provided for the ActorEditor. actor_map = Dict() # This is used primarily to implement the add_actor/remove_actor methods. actor_list = List() # The actual scene being edited. scene_editor = Instance(TVTKScene) do_render = Event() # Fired when this is activated. activated = Event() # Fired when this widget is closed. closing = Event() # This exists just to mirror the TVTKWindow api. scene = Property ################################### # View related traits. # Render_window's view. _stereo_view = Group(Item(name='stereo_render'), Item(name='stereo_type'), show_border=True, label='Stereo rendering', ) # The default view of this object. default_view = View(Group( Group(Item(name='background'), Item(name='foreground'), Item(name='parallel_projection'), Item(name='disable_render'), Item(name='off_screen_rendering'), Item(name='jpeg_quality'), Item(name='jpeg_progressive'), Item(name='magnification'), Item(name='anti_aliasing_frames'), ), Group(Item(name='render_window', style='custom', visible_when='object.stereo', editor=InstanceEditor(view=View(_stereo_view)), show_label=False), ), label='Scene'), Group(Item(name='light_manager', style='custom', editor=InstanceEditor(), show_label=False), label='Lights') ) ################################### # Private traits. # Used by the editor to determine if the widget was enabled or not. enabled_info = Dict() def __init__(self, parent=None, **traits): """ Initializes the object. """ # Base class constructor. We call TVTKScene's super here on purpose. # Calling TVTKScene's init will create a new window which we do not # want. super(TVTKScene, self).__init__(**traits) self.control = None ###################################################################### # TVTKScene API. ###################################################################### def render(self): """ Force the scene to be rendered. Nothing is done if the `disable_render` trait is set to True.""" self.do_render = True def add_actors(self, actors): """ Adds a single actor or a tuple or list of actors to the renderer.""" if hasattr(actors, '__iter__'): self.actor_list.extend(actors) else: self.actor_list.append(actors) def remove_actors(self, actors): """ Removes a single actor or a tuple or list of actors from the renderer.""" my_actors = self.actor_list if hasattr(actors, '__iter__'): for actor in actors: my_actors.remove(actor) else: my_actors.remove(actors) # Conevenience methods. add_actor = add_actors remove_actor = remove_actors def add_widgets(self, widgets, enabled=True): """Adds widgets to the renderer. """ if not hasattr(widgets, '__iter__'): widgets = [widgets] for widget in widgets: self.enabled_info[widget] = enabled self.add_actors(widgets) def remove_widgets(self, widgets): """Removes widgets from the renderer.""" if not hasattr(widgets, '__iter__'): widgets = [widgets] self.remove_actors(widgets) for widget in widgets: del self.enabled_info[widget] def reset_zoom(self): """Reset the camera so everything in the scene fits.""" if self.scene_editor is not None: self.scene_editor.reset_zoom() def save(self, file_name, size=None, **kw_args): """Saves rendered scene to one of several image formats depending on the specified extension of the filename. If an additional size (2-tuple) argument is passed the window is resized to the specified size in order to produce a suitably sized output image. Please note that when the window is resized, the window may be obscured by other widgets and the camera zoom is not reset which is likely to produce an image that does not reflect what is seen on screen. Any extra keyword arguments are passed along to the respective image format's save method. """ self._check_scene_editor() self.scene_editor.save(file_name, size, **kw_args) def save_ps(self, file_name): """Saves the rendered scene to a rasterized PostScript image. For vector graphics use the save_gl2ps method.""" self._check_scene_editor() self.scene_editor.save_ps(file_name) def save_bmp(self, file_name): """Save to a BMP image file.""" self._check_scene_editor() self.scene_editor.save_bmp(file_name) def save_tiff(self, file_name): """Save to a TIFF image file.""" self._check_scene_editor() self.scene_editor.save_tiff(file_name) def save_png(self, file_name): """Save to a PNG image file.""" self._check_scene_editor() self.scene_editor.save_png(file_name) def save_jpg(self, file_name, quality=None, progressive=None): """Arguments: file_name if passed will be used, quality is the quality of the JPEG(10-100) are valid, the progressive arguments toggles progressive jpegs.""" self._check_scene_editor() self.scene_editor.save_jpg(file_name, quality, progressive) def save_iv(self, file_name): """Save to an OpenInventor file.""" self._check_scene_editor() self.scene_editor.save_iv(file_name) def save_vrml(self, file_name): """Save to a VRML file.""" self._check_scene_editor() self.scene_editor.save_vrml(file_name) def save_oogl(self, file_name): """Saves the scene to a Geomview OOGL file. Requires VTK 4 to work.""" self._check_scene_editor() self.scene_editor.save_oogl(file_name) def save_rib(self, file_name, bg=0, resolution=None, resfactor=1.0): """Save scene to a RenderMan RIB file. Keyword Arguments: file_name -- File name to save to. bg -- Optional background option. If 0 then no background is saved. If non-None then a background is saved. If left alone (defaults to None) it will result in a pop-up window asking for yes/no. resolution -- Specify the resolution of the generated image in the form of a tuple (nx, ny). resfactor -- The resolution factor which scales the resolution. """ self._check_scene_editor() self.scene_editor.save_rib(file_name, bg, resolution, resfactor) def save_wavefront(self, file_name): """Save scene to a Wavefront OBJ file. Two files are generated. One with a .obj extension and another with a .mtl extension which contains the material proerties. Keyword Arguments: file_name -- File name to save to """ self._check_scene_editor() self.scene_editor.save_wavefront(file_name) def save_gl2ps(self, file_name, exp=None): """Save scene to a vector PostScript/EPS/PDF/TeX file using GL2PS. If you choose to use a TeX file then note that only the text output is saved to the file. You will need to save the graphics separately. Keyword Arguments: file_name -- File name to save to. exp -- Optionally configured vtkGL2PSExporter object. Defaults to None and this will use the default settings with the output file type chosen based on the extention of the file name. """ self._check_scene_editor() self.scene_editor.save_gl2ps(file_name, exp) def get_size(self): """Return size of the render window.""" self._check_scene_editor() return self.scene_editor.get_size() def set_size(self, size): """Set the size of the window.""" self._check_scene_editor() self.scene_editor.set_size(size) def _update_view(self, x, y, z, vx, vy, vz): """Used internally to set the view.""" if self.scene_editor is not None: self.scene_editor._update_view(x, y, z, vx, vy, vz) def _check_scene_editor(self): if self.scene_editor is None: msg = """ This method requires that there be an active scene editor. To do this, you will typically need to invoke:: object.edit_traits() where object is the object that contains the SceneModel. """ raise SceneModelError(msg) def _scene_editor_changed(self, old, new): if new is None: self._renderer = None self._renwin = None self._interactor = None else: self._renderer = new._renderer self._renwin = new._renwin self._interactor = new._interactor def _get_picker(self): """Getter for the picker.""" se = self.scene_editor if se is not None and hasattr(se, 'picker'): return se.picker return None def _get_light_manager(self): """Getter for the light manager.""" se = self.scene_editor if se is not None: return se.light_manager return None ###################################################################### # SceneModel API. ###################################################################### def _get_scene(self): """Getter for the scene property.""" return self mayavi-4.1.0/tvtk/pyface/picker.py0000644000175100001440000004037111674464502020067 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: #------------------------------------------------------------------------------ """This module provides basic picking functionality. Using this, one can interactively select a point and/or a cell in the data. One can also can use a world point picker (i.e. a generic point in space) and probe for the data at that point. """ # Author: Prabhu Ramachandran # Copyright (c) 2004, Enthought, Inc. # License: BSD Style. from traits.api import HasTraits, Trait, Long, Array, Any, Float, \ Instance, Range, true, Str from traitsui.api import View, Group, Item, Handler from tvtk.api import tvtk from tvtk.tvtk_base import TraitRevPrefixMap, false_bool_trait from apptools.persistence import state_pickler ###################################################################### # Utility functions. ###################################################################### def get_last_input(data): """Attempts to get the deepest possible data value in the pipeline. Used when probing a selected point.""" tmp = inp = data while tmp: try: tmp = inp.input if tmp: inp = tmp except AttributeError: tmp = None return inp ###################################################################### # `PickedData` class. ###################################################################### class PickedData(HasTraits): """This class stores the picked data.""" # Was there a valid picked point? valid = Trait(false_bool_trait, desc='specifies the validity of the pick event') # Id of picked point (-1 implies none was picked) point_id = Long(-1, desc='the picked point ID') # Id of picked cell (-1 implies none was picked) cell_id = Long(-1, desc='the picked cell ID') # World pick -- this has no ID. world_pick = Trait(false_bool_trait, desc='specifies if the pick is a world pick.') # Coordinate of picked point. coordinate = Array('d', (3,), labels=['x', 'y', 'z'], cols=3, desc='the coordinate of the picked point') # The picked data -- usually a tvtk.PointData or tvtk.CellData of # the object picked. The user can use this data and extract any # necessary values. data = Any ###################################################################### # `PickerHandler` class. ###################################################################### class PickHandler(HasTraits): """This is the handler for the picked data. Subclass this to do what you need. Each time a pick occurs the handle_pick is called by the `Picker` class.""" def handle_pick(self, data): """Called when a pick event happens. Parameters ---------- - data : `PickedData` instance. """ pass ###################################################################### # `DefaultPickerHandler` class. ###################################################################### class DefaultPickHandler(PickHandler): """The default handler for the picked data.""" # Traits. ID = Trait(None, None, Long, desc='the picked ID') coordinate = Trait(None, None, Array('d', (3,)), desc='the coordinate of the picked point') scalar = Trait(None, None, Float, desc='the scalar at picked point') vector = Trait(None, None, Array('d', (3,)), desc='the vector at picked point') tensor = Trait(None, None, Array('d', (3,3)), desc='the tensor at picked point') # History of picked data. history = Str default_view = View(Item(name='ID', style='readonly'), Item(name='coordinate', style='readonly'), Item(name='scalar', style='readonly'), Item(name='vector', style='readonly'), Item(name='tensor', style='readonly'), Item(name='history', style='custom'), ) def __init__(self, **traits): super(DefaultPickHandler, self).__init__(**traits) # This saves all the data picked earlier. self.data = {'ID':[], 'coordinate':[], 'scalar':[], 'vector':[], 'tensor':[]} ################################################################# # `DefaultPickHandler` interface. ################################################################# def handle_pick(self, data): """Called when a pick event happens. """ if data.valid_: if data.point_id > -1: self.ID = data.point_id elif data.cell_id > -1: self.ID = data.cell_id self.coordinate = data.coordinate if data.data: array_data = {'scalar': data.data.scalars, 'vector': data.data.vectors, 'tensor': data.data.tensors} else: array_data = {'scalar': None, 'vector': None, 'tensor': None} for name in array_data.keys(): if array_data[name]: setattr(self, name, array_data[name][self.ID]) else: setattr(self, name, None) else: for name in ['ID', 'coordinate', 'scalar', 'vector', 'tensor']: setattr(self, name, None) self._update_data() ################################################################# # Non-public interface. ################################################################# def _update_data(self): for name in ['ID', 'coordinate', 'scalar', 'vector', 'tensor']: value = getattr(self, name) self.data.get(name).append(getattr(self, name)) self.history += '%s: %r\n'%(name, value) ###################################################################### # `CloseHandler` class. ###################################################################### class CloseHandler(Handler): """This class cleans up after the UI for the Picker is closed.""" def close(self, info, is_ok): """This method is invoked when the user closes the UI.""" picker = info.object picker.on_ui_close() return True ###################################################################### # `Picker` class. ###################################################################### class Picker(HasTraits): """This module creates a 'Picker' that can interactively select a point and/or a cell in the data. It also can use a world point picker (i.e. a generic point in space) and will probe for the data at that point. The Picker is usually called via a callback from the GUI interactor window. After performing a pick on the VTK scene, a Picker object creates a `PickedData` object and passes it on to the pick_handler trait for further handling. """ # The version of this class. Used for persistence. __version__ = 0 # Speficifies the pick type. The 'point_picker' and 'cell_picker' # options are self-explanatory. The 'world_picker' picks a point # using a WorldPointPicker and additionally uses a ProbeFilter to # probe the data at the picked point. pick_type = Trait('point', TraitRevPrefixMap({'point_picker':1, 'cell_picker':2, 'world_picker':3}), desc='specifies the picker type to use') # The pick_handler. Set this to your own subclass if you want do # do something different from the default. pick_handler = Trait(DefaultPickHandler(), Instance(PickHandler)) # Picking tolerance. tolerance = Range(0.0, 0.25, 0.025) # show the GUI on pick ? show_gui = true(desc = "whether to show the picker GUI on pick") # Raise the GUI on pick ? auto_raise = true(desc = "whether to raise the picker GUI on pick") default_view = View(Group(Group(Item(name='pick_type'), Item(name='tolerance'), show_border=True), Group(Item(name='pick_handler', style='custom'), show_border=True, show_labels=False), Group(Item(name='show_gui'), Item(name='auto_raise'), show_border=True), ), resizable=True, buttons=['OK'], handler=CloseHandler()) ################################################################# # `object` interface. ################################################################# def __init__(self, renwin, **traits): super(Picker, self).__init__(**traits) self.renwin = renwin self.pointpicker = tvtk.PointPicker() self.cellpicker = tvtk.CellPicker() self.worldpicker = tvtk.WorldPointPicker() self.probe_data = tvtk.PolyData() self._tolerance_changed(self.tolerance) # Use a set of axis to show the picked point. self.p_source = tvtk.Axes() self.p_mapper = tvtk.PolyDataMapper() self.p_actor = tvtk.Actor () self.p_source.symmetric = 1 self.p_actor.pickable = 0 self.p_actor.visibility = 0 prop = self.p_actor.property prop.line_width = 2 prop.ambient = 1.0 prop.diffuse = 0.0 self.p_mapper.input = self.p_source.output self.p_actor.mapper = self.p_mapper self.probe_data.points = [[0.0, 0.0, 0.0]] self.ui = None def __get_pure_state__(self): d = self.__dict__.copy() for x in ['renwin', 'ui', 'pick_handler', '__sync_trait__', '__traits_listener__']: d.pop(x, None) return d def __getstate__(self): return state_pickler.dumps(self) def __setstate__(self, str_state): # This method is unnecessary since this object will almost # never be pickled by itself and only via the scene, therefore # __init__ will be called when the scene is constructed. # However, setstate is defined just for completeness. state_pickler.set_state(self, state_pickler.loads_state(str_state)) ################################################################# # `Picker` interface. ################################################################# def pick(self, x, y): """Calls one of the current pickers and then passes the obtained data to the `self.pick_handler` object's `handle_pick` method. Parameters ---------- - x : X position of the mouse in the window. - y : Y position of the mouse in the window. Note that the origin of x, y must be at the left bottom corner of the window. Thus, for most GUI toolkits, y must be flipped appropriately such that y=0 is the bottom of the window. """ data = None if self.pick_type_ == 1: data = self.pick_point(x, y) elif self.pick_type_ == 2: data = self.pick_cell(x, y) elif self.pick_type_ == 3: data = self.pick_world(x, y) self.pick_handler.handle_pick(data) if self.show_gui: self._setup_gui() def pick_point(self, x, y): """ Picks the nearest point. Returns a `PickedData` instance.""" self.pointpicker.pick((float(x), float(y), 0.0), self.renwin.renderer) pp = self.pointpicker id = pp.point_id picked_data = PickedData() coord = pp.pick_position picked_data.coordinate = coord if id > -1: data = pp.mapper.input.point_data bounds = pp.mapper.input.bounds picked_data.valid = 1 picked_data.point_id = id picked_data.data = data self._update_actor(coord, bounds) else: self.p_actor.visibility = 0 self.renwin.render() return picked_data def pick_cell (self, x, y): """ Picks the nearest cell. Returns a `PickedData` instance.""" try: self.cellpicker.pick(float(x), float(y), 0.0, self.renwin.renderer) except TypeError: # On old versions of VTK, the signature used to be different self.cellpicker.pick((float(x), float(y), 0.0), self.renwin.renderer) cp = self.cellpicker id = cp.cell_id picked_data = PickedData() coord = cp.pick_position picked_data.coordinate = coord if id > -1: data = cp.mapper.input.cell_data bounds = cp.mapper.input.bounds picked_data.valid = 1 picked_data.cell_id = id picked_data.data = data self._update_actor(coord, bounds) else: self.p_actor.visibility = 0 self.renwin.render() return picked_data def pick_world(self, x, y): """ Picks a world point and probes for data there. Returns a `PickedData` instance.""" self.worldpicker.pick((float(x), float(y), 0.0), self.renwin.renderer) # Use the cell picker to get the data that needs to be probed. self.cellpicker.pick( (float(x), float(y), 0.0), self.renwin.renderer) wp = self.worldpicker cp = self.cellpicker coord = wp.pick_position self.probe_data.points = [list(coord)] picked_data = PickedData() picked_data.coordinate = coord if cp.mapper: data = get_last_input(cp.mapper.input) # Need to create the probe each time because otherwise it # does not seem to work properly. probe = tvtk.ProbeFilter() probe.source = data probe.input = self.probe_data probe.update() data = probe.output.point_data bounds = cp.mapper.input.bounds picked_data.valid = 1 picked_data.world_pick = 1 picked_data.point_id = 0 picked_data.data = data self._update_actor(coord, bounds) else: self.p_actor.visibility = 0 self.renwin.render() return picked_data def on_ui_close(self): """This method makes the picker actor invisible when the GUI dialog is closed.""" self.p_actor.visibility = 0 self.renwin.renderer.remove_actor(self.p_actor) self.ui = None ################################################################# # Non-public interface. ################################################################# def _tolerance_changed(self, val): """ Trait handler for the tolerance trait.""" self.pointpicker.tolerance = val self.cellpicker.tolerance = val def _update_actor(self, coordinate, bounds): """Updates the actor by setting its position and scale.""" dx = 0.3*(bounds[1]-bounds[0]) dy = 0.3*(bounds[3]-bounds[2]) dz = 0.3*(bounds[5]-bounds[4]) scale = max(dx,dy,dz) self.p_source.origin = coordinate self.p_source.scale_factor = scale self.p_actor.visibility = 1 def _setup_gui(self): """Pops up the GUI control widget.""" # Popup the GUI control. if self.ui is None: self.ui = self.edit_traits() # Note that we add actors to the renderer rather than to # renwin to prevent event notifications on actor # additions. self.renwin.renderer.add_actor(self.p_actor) elif self.auto_raise: try: self.ui.control.Raise() except AttributeError: pass mayavi-4.1.0/tvtk/pyface/scene_editor.py0000644000175100001440000000055511674464502021255 0ustar ischnellusers00000000000000"""A `SceneEditor` for the `SceneModel`. """ # Authors: Prabhu Ramachandran # Robert Kern # # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Import the toolkit specific version. from tvtk.pyface.toolkit import toolkit_object SceneEditor = toolkit_object('scene_editor:SceneEditor') mayavi-4.1.0/tvtk/pyface/light_manager.py0000644000175100001440000004153511674464502021416 0ustar ischnellusers00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2005, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in enthought/LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. # Description: #------------------------------------------------------------------------------ """This module provides a light manager that may be used to change the lighting of a VTK scene. This module is largely ported from MayaVi's Lights.py but the implementation is considerably different. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. from math import sin, cos, atan2, pi, sqrt from traits.api import HasTraits, Range, false, \ Instance, Trait, List from traitsui.api import View, Group, Handler, ListEditor, Item from tvtk.api import tvtk from tvtk.tvtk_base import vtk_color_trait, TraitRevPrefixMap from apptools.persistence import state_pickler ###################################################################### # `LightGlyph` class. ###################################################################### class LightGlyph(HasTraits): """Manages a glyph that represents a Light source in the scene. This gives the user an *idea* of where the light source is placed while configuring the lights. """ # The version of this class. Used for persistence. __version__ = 0 def __init__(self): self.el = 0.0 self.az = 0.0 # Create an arrow. arrow = tvtk.ArrowSource() # Transform it suitably so it is oriented correctly. t = tvtk.Transform() tf = tvtk.TransformFilter() tf.transform = t t.rotate_y(90.0) t.translate((-2, 0, 0)) tf.input = arrow.output mapper = tvtk.PolyDataMapper() mapper.input = tf.output self.actor = actor = tvtk.Follower() actor.mapper = mapper prop = actor.property prop.color = 0, 1, 1 prop.ambient = 0.5 prop.diffuse = 0.5 def __get_pure_state__(self): d = self.__dict__.copy() for name in ['__sync_trait__', '__traits_listener__']: d.pop(name, None) return d def __getstate__(self): return state_pickler.dumps(self) def __setstate__(self, str_state): self.__init__() state_pickler.set_state(self, state_pickler.loads_state(str_state)) ################################################################# # `LightGlyph` interface. ################################################################# def add(self, ren, bounds): """Adds the actors to the given renderer (`ren`). The actors are scaled as per the given bounds.""" scale = max(bounds[1]-bounds[0], bounds[3] - bounds[2], bounds[5]-bounds[4])*0.75 self.actor.scale = scale, scale, scale ren.add_actor(self.actor) cam = ren.active_camera self.actor.camera = cam def remove(self, ren): """Removes the actors of the glyph from the given renderer (`ren`).""" ren.remove_actor(self.actor) def move_to(self, elevation=None, azimuth = None): """Move the glyphs to the specified elevation and azimuth.""" self.actor.rotate_x(-self.el) self.actor.rotate_y(-self.az) if elevation != None: self.el = elevation if azimuth != None: self.az = azimuth self.actor.rotate_y(self.az) self.actor.rotate_x(self.el) def show(self): """Show the glyphs on screen.""" self.actor.visibility = 1 def hide(self): """Hide the glyphs on screen.""" self.actor.visibility = 0 def set_color(self, clr): """Change the glyphs color.""" self.actor.property.color = clr ###################################################################### # `CameraLight` class. ###################################################################### class CameraLight(HasTraits): """This class manages a tvtk.Light object and a LightGlyph object.""" # The version of this class. Used for persistence. __version__ = 0 ################################################################# # Traits. ################################################################# elevation = Range(-90.0, 90.0, 0.0, desc="the elevation of the light") azimuth = Range(-180.0, 180.0, 0.0, desc="the aziumthal angle of the light") activate = Trait(False, false, desc="specifies if the light is enabled or not") source = Instance(tvtk.Light, ()) # FIXME: Traits Delegation does not work correctly and changes to # this object are not reflected in the delegate nicely. #color = Delegate('source', modify=True) #intensity = Delegate('source', modify=True) # For now we mirror these traits from the source. intensity = Range(0.0, 1.0, 1.0, desc="the intensity of the light") color = vtk_color_trait((1.0, 1.0, 1.0)) color.desc = "the color of the light" default_view = View(Item(name='activate'), Item(name='elevation'), Item(name='azimuth'), Item(name='intensity'), Item(name='color')) ################################################################# # `object` interface. ################################################################# def __init__(self, renwin, **traits): self.glyph = LightGlyph() super(CameraLight, self).__init__(**traits) self.source.light_type = 'camera_light' self._intensity_changed(self.intensity) self._activate_changed(self.activate) self._color_changed(self.color) renwin.renderer.add_light(self.source) self.on_trait_change(renwin.render) def __get_pure_state__(self): d = self.__dict__.copy() for name in ['__sync_trait__', '__traits_listener__']: d.pop(name, None) return d def __getstate__(self): return state_pickler.dumps(self) def __setstate__(self, str_state): #self.__init__() state_pickler.set_state(self, state_pickler.loads_state(str_state)) ################################################################# # `LightGlyph` interface. ################################################################# def close(self, renwin): """Remove the light source and the glyph from the renderwindow. This is usually to be called when the Light is removed from the scene.""" ren = renwin.renderer self.on_trait_change(renwin.render, remove=True) ren.remove_light(self.source) self.remove_glyph(ren) def add_glyph(self, ren, bounds): """Add the light glyph to the passed renderer ('ren'). Scale the actors using the bounds (`bounds`) provided.""" self.glyph.add(ren, bounds) def remove_glyph(self, ren): """Remove the light glyph from the passed renderer.""" self.glyph.remove(ren) def move_to(self, elevation, azimuth): """Move the light to the specified elevation and azimuthal angles (in degrees).""" self.elevation = elevation self.azimuth = azimuth ################################################################# # Trait handlers. ################################################################# def _activate_changed(self, val): if val: self.source.switch = 1 self.glyph.show() else: self.source.switch = 0 self.glyph.hide() def _intensity_changed(self, val): self.source.intensity = val def _color_changed(self, val): self.source.color = val self.glyph.set_color(val) def _elevation_changed(self, val): self.glyph.move_to(val, self.azimuth) self.source.position = self._to_pos(val, self.azimuth) def _azimuth_changed(self, val): self.glyph.move_to(self.elevation, val) self.source.position = self._to_pos(self.elevation, val) ################################################################# # Non-public interface. ################################################################# def _to_pos(self, elevation, azimuth): """Convert the given elevation and azimuth angles (degrees) to a position vector.""" theta = azimuth*pi/180.0 phi = (90.0-elevation)*pi/180.0 x = sin(theta)*sin(phi) y = cos(phi) z = cos(theta)*sin(phi) return x, y, z def _from_pos(self, x, y, z): """Given the position vector, return an elevation and azimuth angle (in degrees).""" theta = atan2(x, z) phi = atan2(sqrt(x**2+z**2), y) az = theta*180.0/pi el = 90.0 - phi*180.0/pi return el, az ###################################################################### # `CloseHandler` class. ###################################################################### class CloseHandler(Handler): """This class cleans up after the UI for the Light Manager is closed.""" def close(self, info, is_ok): """This method is invoked when the user closes the UI.""" light_manager = info.object light_manager.on_ui_close() return True ###################################################################### # `LightManager` class. ###################################################################### class LightManager(HasTraits): """This class manages all the lights and presents a GUI for the lights. There are two default lighting modes possible (specified via the `light_mode` trait). The 'vtk' mode uses the default lighting mode which is basically a single headlight. The 'raymond' mode creates three lights to give better overall illumination (thanks to Raymond Maple). """ # The version of this class. Used for persistence. __version__ = 0 ################################################################# # Traits. ################################################################# # Valid modes currently are 'vtk' and 'raymond'. 'vtk' is the # default VTK light setup with only one light on in headlight # mode. 'raymond' is Raymond Maple's default configuration with # three active lights. Please note that this only specifies a # default mode used to initialize the lights to a sane default. # The user can always change the light configuration via the GUI # such that the mode is neither 'vtk' nor 'raymond'. light_mode = Trait('raymond', TraitRevPrefixMap({'raymond':1, 'vtk':2}), desc='specifies a default lighting mode') # Specify the number of lights. If new lights are added they are # by default turned off. Similarly if the number of lights are # reduced the last lights alone are removed. number_of_lights = Range(3, 8, 4, desc='specifies the number of lights') # The list of added lights. lights = List(CameraLight, editor=ListEditor(use_notebook=True, page_name='Light'), record=True) view = View( Group( 'light_mode', 'number_of_lights', ), Item('lights', style='custom', show_label=False), resizable=True, buttons=['OK']) ################################################################# # `object` interface. ################################################################# def __init__(self, renwin, **traits): super(LightManager, self).__init__(**traits) self.ui = None self.renwin = renwin ren = self.renwin.renderer # VTK requires that there always be atleast one light turned # on (with SwitchOn). The renderer already has one headlight # already enabled. We merely set the intensity of this light # to zero and never change that. Note that if this light is # turned off, and no other lights are "on", then VTK will # create a new light! ren.lights[0].intensity = 0.0 # Create the lights. self.lights = [] for i in range(self.number_of_lights): light = CameraLight(self.renwin) self.lights.append(light) # Setup the light mode. self._light_mode_changed(self.light_mode) def __get_pure_state__(self): d = self.__dict__.copy() for name in ['__sync_trait__', 'renwin', 'ui', '__traits_listener__']: d.pop(name, None) return d def __set_pure_state__(self, state): first = ['light_mode', 'number_of_lights'] state_pickler.set_state(self, state, first=first, last=['lights']) def __getstate__(self): return state_pickler.dumps(self) def __setstate__(self, str_state): # This method is unnecessary since this object will almost # never be pickled by itself and only via the scene, therefore # __init__ will be called when the scene is constructed. # However, setstate is defined just for completeness. #self.__init__() state = state_pickler.loads_state(str_state) state_pickler.update_state(state) self.__set_pure_state__(state) ################################################################# # `LightManager` interface. ################################################################# def configure(self): """Pops up the GUI control widget.""" if self.ui is None: self._show_glyphs() view = View(Group(Item(name='light_mode'), Item(name='number_of_lights'), label='LightManager'), Group(Item(name='lights', style='custom'), label='Lights', selected=True, show_labels=False), resizable=True, buttons=['OK'], handler=CloseHandler()) self.ui = view.ui(self) else: try: self.ui.control.Raise() except AttributeError: pass def on_ui_close(self): """This method removes the glyphs used to show the lights on screen when the GUI dialog is closed. This is typically only called from the CloseHandler.""" ren = self.renwin.renderer for l in self.lights: l.remove_glyph(ren) self.ui = None ################################################################# # Non-public interface. ################################################################# def _show_glyphs(self): """Shows the glyphs when the light config UI is shown.""" rw = self.renwin ren = rw.renderer rw.reset_zoom() bounds = ren.compute_visible_prop_bounds() for light in self.lights: light.add_glyph(ren, bounds) rw.render() def _light_mode_changed(self, mode): lights = self.lights if mode == 'raymond': for i in range(len(lights)): if i < 3: lights[i].activate = True lights[i].intensity = 1.0 lights[i].color = 1.0, 1.0, 1.0 else: lights[i].activate = False lights[i].move_to(0.0, 0.0) lights[i].intensity = 1.0 lights[i].color = 1.0, 1.0, 1.0 lights[0].move_to(45.0, 45.0) lights[1].move_to(-30.0,-60.0) lights[1].intensity = 0.6 lights[2].move_to(-30.0, 60.0) lights[2].intensity = 0.5 else: for i in range(len(lights)): lights[i].move_to(0.0, 0.0) lights[i].intensity = 1.0 lights[i].color = 1.0, 1.0, 1.0 if i == 0 : lights[i].activate = True else: lights[i].activate = False def _number_of_lights_changed(self, old, new): ren = self.renwin.renderer changed = False if new == old: return elif new > old: for i in range(new - old): light = CameraLight(self.renwin) self.lights.append(light) changed = True elif new < old: for i in range(old - new): light = self.lights.pop() light.close(self.renwin) changed = True mayavi-4.1.0/tvtk/pyface/decorated_scene.py0000644000175100001440000000072011674464502021713 0ustar ischnellusers00000000000000"""A VTK interactor scene which provides a convenient toolbar that allows the user to set the camera view, turn on the axes indicator etc. """ # Authors: Prabhu Ramachandran , # Dave Peterson # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Import the toolkit specific version. from tvtk.pyface.toolkit import toolkit_object DecoratedScene = toolkit_object('decorated_scene:DecoratedScene') mayavi-4.1.0/tvtk/pyface/__init__.py0000644000175100001440000000064711674464502020353 0ustar ischnellusers00000000000000# Copyright (c) 2005-2011, Enthought, Inc. # All rights reserved. # # This software is provided without warranty under the terms of the BSD # license included in LICENSE.txt and may be redistributed only # under the conditions described in the aforementioned license. The license # is also available online at http://www.enthought.com/licenses/BSD.txt # Thanks for using Enthought open source! # # Author: Enthought, Inc. mayavi-4.1.0/tvtk/pyface/actor_model.py0000644000175100001440000000222011674464502021071 0ustar ischnellusers00000000000000""" A simple model to use for viewing TVTK actors/widgets. """ # Authors: Robert Kern # Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import Dict, Event, HasTraits, Bool ##################################################################### # `ITVTKActorModel` class ##################################################################### class ITVTKActorModel(HasTraits): """ An interface for view models that can control a TVTK scene's contents. """ # This maintains a dictionary mapping objects (by identity) to lists (or # single items) of TVTK Actors or 3D Widgets that represent them in the # scene. Adding and removing objects from this dictionary adds and removes # them from the scene. This is the trait that will be edited by a # ActorEditor. actor_map = Dict() # Turn off rendering such that multiple adds/removes can be refreshed at # once. disable_render = Bool(False) # Send this event in order to force a rendering of the scene. do_render = Event() mayavi-4.1.0/tvtk/pyface/actor_editor.py0000644000175100001440000000061311674464502021263 0ustar ischnellusers00000000000000""" A mostly-general Traits UI editor for viewing things in TVTK scenes. """ # Authors: Robert Kern # Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. # Import the toolkit specific version. from tvtk.pyface.toolkit import toolkit_object ActorEditor = toolkit_object('actor_editor:ActorEditor') mayavi-4.1.0/tvtk/tvtk_base.py0000644000175100001440000004353411674464502017331 0ustar ischnellusers00000000000000"""Commonly used code by tvtk objects. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2008, Enthought, Inc. # License: BSD Style. import types import sys import weakref import os import logging import vtk from traits import api as traits from traitsui.api import BooleanEditor, RGBColorEditor, FileEditor import messenger # Setup a logger for this module. logger = logging.getLogger(__name__) ###################################################################### # The TVTK object cache. ###################################################################### class TVTKObjectCache(weakref.WeakValueDictionary): def __init__(self, *args, **kw): self._observer_data = {} weakref.WeakValueDictionary.__init__(self, *args, **kw) def remove(wr, selfref=weakref.ref(self)): self = selfref() if self is not None: self.teardown_observers(wr.key) del self.data[wr.key] self._remove = remove def setup_observers(self, vtk_obj, event, method): """Setup the observer for the VTK object's event. Parameters ---------- vtk_obj -- The VTK object for which the `event` is observed. event -- The VTK event to watch. method -- The method to be called back when `event` is fired on the VTK object. """ # Setup the observer so the traits are updated even if the # wrapped VTK object changes. if hasattr(vtk_obj, 'AddObserver'): # Some classes like vtkInformation* derive from # tvtk.ObjectBase which don't support Add/RemoveObserver. messenger.connect(vtk_obj, event, method) ob_id = vtk_obj.AddObserver(event, messenger.send) key = vtk_obj.__this__ od = self._observer_data if key in od: od[key].append((vtk_obj, ob_id)) else: od[key] = [(vtk_obj, ob_id)] def teardown_observers(self, key): """Given the key of the VTK object (vtk_obj.__this__), this removes the observer for the ModifiedEvent and also disconnects the messenger. """ od = self._observer_data if key not in od: return for vtk_obj, ob_id in od[key]: try: # The disconnection sometimes fails at exit. vtk_obj.RemoveObserver(ob_id) except AttributeError: pass try: messenger.disconnect(vtk_obj) except AttributeError: pass del od[key] # The TVTK object cache (`_object_cache`). This caches all the TVTK # instances using weakrefs. When a VTK object is wrapped via the # `wrap_vtk` function this cache is checked. The key is the VTK # object's address. The value of the dict is the TVTK wrapper object. # If the VTK object address exists in the cache, it is returned by # `wrap_vtk`. `wrap_vtk` is defined in `tvtk_helper.py` which is # stored in the ZIP file. _dummy = None # This makes the cache work even when the module is reloaded. for name in ['tvtk_base', 'tvtk.tvtk_base']: if sys.modules.has_key(name): mod = sys.modules[name] if hasattr(mod, '_object_cache'): _dummy = mod._object_cache del mod break if _dummy is not None: _object_cache = _dummy else: _object_cache = TVTKObjectCache() del _dummy def get_tvtk_object_from_cache(vtk_obj): """Returns the cached TVTK object given a VTK object.""" return _object_cache.get(vtk_obj.__this__) ###################################################################### # Special traits used by the tvtk objects. ###################################################################### true_bool_trait = traits.Trait('true', {'true': 1, 't': 1, 'yes': 1, 'y': 1, 'on': 1, 1: 1, 'false': 0, 'f': 0, 'no': 0, 'n': 0, 'off': 0, 0: 0}, editor=BooleanEditor) false_bool_trait = traits.Trait('false', true_bool_trait) class TraitRevPrefixMap(traits.TraitPrefixMap): """A reverse mapped TraitPrefixMap. This handler allows for something like the following:: >>> class A(HasTraits): ... a = Trait('ab', TraitRevPrefixMap({'ab':1, 'cd':2})) ... >>> a = A() >>> a.a = 'c' >>> print a.a 'cd' >>> a.a = 1 >>> print a.a 'ab' That is, you can set the trait to the value itself. If multiple keys map to the same value, one of the valid keys will be used. """ def __init__(self, map): traits.TraitPrefixMap.__init__(self, map) self._rmap = {} for key, value in map.items(): self._rmap[value] = key def validate(self, object, name, value): try: if self._rmap.has_key(value): value = self._rmap[value] if not self._map.has_key( value ): match = None n = len( value ) for key in self.map.keys(): if value == key[:n]: if match is not None: match = None break match = key if match is None: self.error( object, name, value ) self._map[ value ] = match return self._map[ value ] except: self.error( object, name, value ) def info(self): keys = [repr(x) for x in self._rmap.keys()] keys.sort() msg = ' or '.join(keys) return traits.TraitPrefixMap.info(self) + ' or ' + msg def vtk_color_trait(default, **metadata): Range = traits.Range if default[0] == -1.0: # Occurs for the vtkTextProperty's color trait. Need to work # around. return traits.Trait(default, traits.Tuple(*default), traits.Tuple(Range(0.0, 1.0), Range(0.0, 1.0), Range(0.0, 1.0), editor=RGBColorEditor), **metadata) else: return traits.Trait(traits.Tuple(Range(0.0, 1.0, default[0]), Range(0.0, 1.0, default[1]), Range(0.0, 1.0, default[2])), editor=RGBColorEditor, **metadata) # Special cases for the FileName and FilePrefix vtk_file_name = traits.Trait(None, None, traits.Str, types.UnicodeType, editor=FileEditor) vtk_file_prefix = traits.Trait(None, None, traits.Str, types.UnicodeType, editor=(FileEditor, {'truncate_ext': True})) # The Property class traits are delegated in the Actors. vtk_property_delegate = traits.Delegate('property', modify=True) ###################################################################### # Utility functions. ###################################################################### def deref_vtk(obj): """Dereferences the VTK object from the object if possible.""" if isinstance(obj, TVTKBase): return obj._vtk_obj else: return obj ###################################################################### # 'TVTKBase' class (base class for all tvtk classes): ###################################################################### class TVTKBase(traits.HasStrictTraits): """The base class for all TVTK objects. This class encapsulates key functionality common to all the TVTK classes. TVTK classes provide a trait wrapped VTK object. They also primitively picklable. Only the basic state of the object itself is pickled. References to other VTK objects are NOT pickled. """ # This is just a dummy integer (MUST be > 1) that indicates that # we are updating the traits and there is no need to change the # underlying VTK object. DOING_UPDATE = 10 ######################################## # Private traits. # This trait is only used internally and should not activate any # notifications when set which is why we use `Python`. _in_set = traits.Python # The wrapped VTK object. _vtk_obj = traits.Trait(None, None, vtk.vtkObjectBase()) # Stores the names of the traits that need to be updated. _updateable_traits_ = traits.Tuple # List of trait names that are to be included in the full traits view of this object. _full_traitnames_list_ = traits.List ################################################################# # `object` interface. ################################################################# def __init__(self, klass, obj=None, update=True, **traits): """Object initialization. Parameters ---------- - klass: `vtkObjectBase` A VTK class to wrap. If `obj` is passed, its class must be the same as `klass` or a subclass of it. - obj: `vtkObjectBase` (default: None) An optional VTK object. If passed the passed object is wrapped. This defaults to `None` where a new VTK instance of class, `klass` is created. - update: `bool` (default: True) If True (default), the traits of the class are automatically updated based on the state of the wrapped VTK object. If False, no updation is performed. This is particularly useful when the object is being unpickled. - traits: `dict` A dictionary having the names of the traits as its keys. This allows a user to set the traits of the object while creating the object. """ # Initialize the Python attribute. self._in_set = 0 if obj: assert obj.IsA(klass.__name__) self._vtk_obj = obj else: self._vtk_obj = klass() # print "INIT", self.__class__.__name__, repr(self._vtk_obj) # Call the Super class to update the traits. # Inhibit any updates at this point since we update in the end # anyway. self._in_set = 1 super(TVTKBase, self).__init__(**traits) self._in_set = 0 # Update the traits based on the values of the VTK object. if update: self.update_traits() # Setup observers for the modified event. self.setup_observers() _object_cache[self._vtk_obj.__this__] = self def __getinitargs__(self): """This is merely a placeholder so that subclasses can override this if needed. This is called by `__setstate__` because `traits.HasTrait` is a newstyle class. """ # You usually don't want to call update when calling __init__ # from __setstate__ return (None, 0) def __getstate__(self): """Support for primitive pickling. Only the basic state is pickled. """ self.update_traits() d = self.__dict__.copy() for i in ['_vtk_obj', '_in_set', 'reference_count', 'global_warning_display', '__sync_trait__']: d.pop(i, None) return d def __setstate__(self, dict): """Support for primitive pickling. Only the basic state is pickled. """ # This is a newstyle class so we need to call init here. if self._vtk_obj is None: self.__init__(*self.__getinitargs__()) self._in_set = 1 for i in dict: # Not enough to update the dict because the vtk object # needs to be updated. try: setattr(self, i, dict[i]) except traits.TraitError, msg: print "WARNING:", print msg self._in_set = 0 def __str__(self): """Return a nice string representation of the object. This merely returns the result of str on the underlying VTK object. """ return str(self._vtk_obj) ################################################################# # `HasTraits` interface. ################################################################# def class_trait_view_elements ( cls ): """ Returns the ViewElements object associated with the class. The returned object can be used to access all the view elements associated with the class. Overridden here to search through a particular directory for substitute views to use for this tvtk object. The view should be declared in a file named _view. We execute this file and replace any currently defined view elements with view elements declared in this file (that have the same name). """ # FIXME: This can be enhanced to search for new views also (in addition # to replacing current views). view_elements = super(TVTKBase, cls).class_trait_view_elements() # Get the names of all the currently defined view elements. names = view_elements.filter_by() baseDir = os.path.dirname(os.path.abspath(__file__)) viewDir = os.path.join(baseDir, 'view') try: module_name = cls.__module__.split('.')[-1] view_filename = os.path.join(viewDir, module_name + '_view.py') result = {} execfile(view_filename, {}, result) for name in names: if name in result: view_elements.content[ name ] = result[name] except Exception, e: pass return view_elements class_trait_view_elements = classmethod( class_trait_view_elements ) ################################################################# # `TVTKBase` interface. ################################################################# def setup_observers(self): """Add an observer for the ModifiedEvent so the traits are kept up-to-date with the wrapped VTK object and do it in a way that avoids reference cycles.""" _object_cache.setup_observers(self._vtk_obj, 'ModifiedEvent', self.update_traits) def teardown_observers(self): """Remove the observer for the Modified event.""" _object_cache.teardown_observers(self._vtk_obj.__this__) def update_traits(self, obj=None, event=None): """Updates all the 'updateable' traits of the object. The method works by getting the current value from the wrapped VTK object. `self._updateable_traits_` stores a tuple of tuples containing the trait name followed by the name of the get method to use on the wrapped VTK object. The `obj` and `event` parameters may be ignored and are not used in the function. They exist only for compatibility with the VTK observer callback functions. """ if self._in_set: return if not hasattr(self, '_updateable_traits_'): return self._in_set = self.DOING_UPDATE vtk_obj = self._vtk_obj # Save the warning state and turn it off! warn = vtk.vtkObject.GetGlobalWarningDisplay() vtk.vtkObject.GlobalWarningDisplayOff() for name, getter in self._updateable_traits_: try: val = getattr(vtk_obj, getter)() except (AttributeError, TypeError): pass else: if name == 'global_warning_display': setattr(self, name, warn) else: setattr(self, name, val) # Reset the warning state. vtk.vtkObject.SetGlobalWarningDisplay(warn) self._in_set = 0 ################################################################# # Non-public interface. ################################################################# def _do_change(self, method, val, force_update=False): """This is called by the various traits when they change in order to update the underlying VTK object. Parameters ---------- - method: `method` The method to invoke on the VTK object when called. - val: `Any` Argument to the method. - force_update: `bool` (default: False) If True, `update_traits` is always called at the end. """ if self._in_set == self.DOING_UPDATE: return vtk_obj = self._vtk_obj self._in_set += 1 mtime = self._wrapped_mtime(vtk_obj) + 1 try: method(val) except TypeError: if hasattr(val, '__len__'): method(*val) else: raise self._in_set -= 1 if force_update or self._wrapped_mtime(vtk_obj) > mtime: self.update_traits() def _wrap_call(self, vtk_method, *args): """This method allows us to safely call a VTK method without calling `update_traits` during the call. This method is therefore used to wrap any 'Set' call on a VTK object. The method returns the output of the vtk_method call. Parameters ---------- - vtk_method: `method` The method to invoke safely. - args: `Any` Argument to be passed to the method. """ vtk_obj = self._vtk_obj self._in_set += 1 mtime = self._wrapped_mtime(vtk_obj) + 1 ret = vtk_method(*args) self._in_set -= 1 if self._wrapped_mtime(vtk_obj) > mtime: self.update_traits() return ret def _wrapped_mtime(self, vtk_obj): """A simple wrapper for the mtime so tvtk can be used for `vtk.vtkObjectBase` subclasses that neither have an `AddObserver` or a `GetMTime` method. """ try: return vtk_obj.GetMTime() except AttributeError: return 0 mayavi-4.1.0/tvtk/misc.py0000644000175100001440000000400111674464502016264 0ustar ischnellusers00000000000000"""Some miscellaneous convenience functionality. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. from os.path import splitext # We import from tvtk.py and not api.py to prevent circular imports. from tvtk.tvtk_access import tvtk ###################################################################### # Utility functions. ###################################################################### def write_data(dataset, fname, **kwargs): """Given a TVTK `dataset` this writes the `dataset` to a VTK XML file having file name `fname`. If the given file name has no extension, one is automatically picked based on the dataset and an XML file is written out. If the filename has a '.vtk' extension an old style VTK file is written. If any other extension is specified, an XML file is written out with the given extension. Any additional keyword arguments are passed to the writer used. """ err_msg = "Can only write tvtk.DataSet instances "\ "'got %s instead"%(dataset.__class__.__name__) assert isinstance(dataset, tvtk.DataSet), err_msg # Mapping to determine appropriate extension and writer. d2r = {'vtkImageData': ('.vti', tvtk.StructuredPointsWriter), 'vtkRectilinearGrid': ('.vtr', tvtk.RectilinearGridWriter), 'vtkStructuredGrid': ('.vts', tvtk.StructuredGridWriter), 'vtkPolyData': ('.vtp', tvtk.PolyDataWriter), 'vtkUnstructuredGrid': ('.vtu', tvtk.UnstructuredGridWriter) } for type in d2r: if dataset.is_a(type): datatype = d2r[type] break ext = splitext(fname)[1] if ext == '.vtk': file_name = fname writer = datatype[1] elif len(ext) == 0: file_name = fname + datatype[0] writer = tvtk.XMLDataSetWriter else: file_name = fname writer = tvtk.XMLDataSetWriter w = writer(file_name=file_name, input=dataset, **kwargs) w.write() mayavi-4.1.0/tvtk/pipeline/0000755000175100001440000000000011674464502016571 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/pipeline/images/0000755000175100001440000000000011674464502020036 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/pipeline/images/filter.png0000644000175100001440000000140411674464502022030 0ustar ischnellusers00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb?% X.# g.$.s޽ͤ{HDDHyݺU@e[>|Ĥw`,,\ _ؿkfb"ƀ/_0h^d-/R : (7 v17o?t췀vt(3pa8,۝mWgcc Ԭr@a5*oC i3TYJ~-ffUbЬ@ rk5w\27SP@Xhdx ïƻLԈ[8Dx{ T~3_1hlfp R Y U]^ǰ=l~0J))-pG% lB, /.}r,6ׯ?LR23#Ynİ3?gť kڰĉ+xWmma1PhHdN?Tef`} ޽;f̘pW^@[  trsktvN/..B5AA S^^@1=dyB MIENDB`mayavi-4.1.0/tvtk/pipeline/images/camera.png0000644000175100001440000000105211674464502021772 0ustar ischnellusers00000000000000PNG  IHDRasBIT|d pHYsu85tEXtSoftwarewww.inkscape.org<IDAT8ՓOkQ̛y3Jtt5);QqWpkˮ5Xڂ`BQqȟ&3n$P ^8pν+ysRRS Xk;b( Ɠʥz +^zL)vx$ɱ֚4MnՄg߻l|ц M՗_Ԫs7iI*.T/>qG+,W[s\}](iֿhQ\.q~vfF̔J,߽σu5ssX]YbU\Gݩae~_ڟÐ(bE)T8Zw;@w($I2[kQCѠYoYl6QꡈL+P"REp\g z؀IENDB`mayavi-4.1.0/tvtk/pipeline/images/property.png0000644000175100001440000000064711674464502022437 0ustar ischnellusers00000000000000PNG  IHDRabKGD pHYs  tIME  2GI4IDAT8˭OKQq/|VKc"hQ&kȆg#[VQiӦoжhEr[uFtz{߹wfJ8YRJN);Z-<@DX%k   CDXg3?.ԙqGz(h!}7 ApE>6tNN.A^t 7Ivu㐪d' oc*j]*raV4{  b*INJkIENDB`mayavi-4.1.0/tvtk/pipeline/images/mapper.png0000644000175100001440000000220211674464502022024 0ustar ischnellusers00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxbd`h``d`a'_m$il&.#)o (:}I@!uk)Z9x9y^~zsO߿3Kq Y4uߟ7GIPH"_w/8Yu8٥؁?1Q/63<}3LeYkn'  |A/3\&"D '+M?2Sߘ&q=+';0Qr20p1???/^:??C##?:j @123['V)`Wp`x;?@Wgd``̔\̐u#ߓ~k9Y82 10s00r10('4?'˗r2k~=O7oI=}wz/` ;OcW H~.7*tfS'A]1    E8#P߿|'ʿc@~!+IENDB`mayavi-4.1.0/tvtk/pipeline/images/rendererwindow.png0000644000175100001440000000043111674464502023600 0ustar ischnellusers00000000000000PNG  IHDRa pHYs B4gAMA|Q cHRMz%u0`:o_FIDATxb?% X@,)@X3i3 ā2,Zb+ C & j Db/^ @{ (@QhLbb FJ3@>DIENDB`mayavi-4.1.0/tvtk/pipeline/images/writer.png0000644000175100001440000000067211674464502022065 0ustar ischnellusers00000000000000PNG  IHDRa pHYs  tIME fYIDAT8͒=KA[ZbئBl%R, Rh# \! ! w#XiF]9.t:02>;a/N@*ObeGi fo zM邽sIENDB`mayavi-4.1.0/tvtk/pipeline/images/source.png0000644000175100001440000000043711674464502022050 0ustar ischnellusers00000000000000PNG  IHDRabKGD pHYs  tIME  9LaIDAT8!0FH/sLM]3z k !KP|əw}vJu%NADByCHSjc ]%{8x4MXk*)'șж-[P}4Ws w *qwu~87՜5>WTIENDB`mayavi-4.1.0/tvtk/pipeline/images/renderer.png0000644000175100001440000000166111674464502022356 0ustar ischnellusers00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<CIDATxb?% (Ȕh͑& _xץ53οg400|9l@nU2b` %_s^?$q0oϭ?2l@1,`/g3eeyíK 8 #f5R0;00b񛁍/ ?xu%3pJ0 300IŒ &NE~Z5{}[99%ge`b׫h@L I@s05p2s1:I[8*24G\G; |T_ ?@qsß@8XxY=Ř842@&L0 vF$|cZt *#2P@H238ӻ |l8IMᥚÇ89tM@13q2r#йL 083#A8XD%Aa8 <:m;[TL ȻӿA`g.KؐSߟ~}|8@1R 1F&vIENDB`mayavi-4.1.0/tvtk/pipeline/images/window.png0000644000175100001440000000072711674464502022061 0ustar ischnellusers00000000000000PNG  IHDR(-SPLTEoznz+[)o)m)n*l*k)j*j)g*g*d*c*a*^*\*Y+Y+X+X+VwzzrlhhvS!tRNS@fbKGDH pHYs  tIME 4$ IDATe0@QTBAb) ąpxlƲjC.rnכnb:hӖ* \iːK qYuC7@hx1* )!DC4l:FIMp1k|*ՏEJؤDIENDB`mayavi-4.1.0/tvtk/pipeline/images/rendererwindowinteractor.png0000644000175100001440000000202111674464502025670 0ustar ischnellusers00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb?% XJK; f100+rrR8e55i+;x~GbIŮ55dcc>~|]RA##T%a* rrꊞ*< L >ccc)w X~Ġ1p -FF`^GG6>>N70^ׯ&߿4$$$W mãG}:ADAZZAHt!!aǏoܸ| @A @3,[wihY\#'' 6`ݺ_ϟg & ر**2.]:=a߾ˁ1.!!Y ӇoY޽{k/_]AFFMCC_'$$AO߿c^F ,-YLÇ>b>u7o^~5+_JIIs XZZ)(H1/2,Z(@xx?VPPa`aaPY hjwA\\AJJ(W _5W qq `%++.Gϟ89ف)ܹ8q`ӧ˯_?,X0@\moQAQQ 8gP`FP͛gW^jy X@ĭ[WWp缼ڥrr߿bsP>0a2<{f2@0 ܹss %(qrr1̜9=z/F=ã  ,-mqssCX=;&cg+IENDB`mayavi-4.1.0/tvtk/pipeline/images/texture.png0000644000175100001440000000071311674464502022245 0ustar ischnellusers00000000000000PNG  IHDRabKGD pHYs  tIME  k|6XIDAT8͓K@ǿ1l :ԂWN YCY2tTqq -Z*r9q|{>:N#.}nxu,ף ;{ɹɸ{] lӇ63ln 0R\_r3ePUzф2M Jx r~V AG7KzVł? BbRb9t6nRFRGÛ{:[ިsώϱA;y`I"  yCpexÙ3g޽(~(g"˯7o8b+&& 'O2:uAEEAPPHT{c[m~& 30v;t?~E01jiiI 䅇1|ADZZVdY!~)Sp1~ }㧓紏 ߿u"""< JJJ @zB`z:0]m+ ۀi'!F&be` TPP#..~(y ~g_B5Ibd@I2di)%9Q`Ö>\@Ⱥafv\arrllJ E ǟ>g(~٣d8 b݄Εji{+z(lR`w<ڣIENDB`mayavi-4.1.0/tvtk/pipeline/images/image_LICENSE.txt0000644000175100001440000000220311674464502023020 0ustar ischnellusers00000000000000The icons are mostly derived work from other icons. As such they are licensed accordingly to the original license: Eclipse: Eclipse Public Licence Nuvola: LGPL Crystal: LGPL OOo: LGPL GV: Gael Varoquaux: BSD-licensed Unless stated in this file, icons are work of enthought, and are released under a 3 clause BSD-like license. Files and orginal authors: ---------------------------------------------------------------- actor.png | Nuvola camera.png | GV coordinate.png | Crystal filter.png | Nuvola lookuptable.png | OOo mapper.png | Crystal polydata.png | Nuvola property.png | Eclipse reader.png | Crystal renderer.png | Crystal rendererwindowinteractor.png | Nuvola rendererwindow.png | Crystal source.png | OOo texture.png | Eclipse window.png | Eclipse writer.png | GV mayavi-4.1.0/tvtk/pipeline/images/reader.png0000644000175100001440000000167211674464502022014 0ustar ischnellusers00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<LIDATxb?%  ~ɠ(3?~?QAVjG:1uf@ X@_@C20gd``'"K9ӫ/>?t  <4S~]M$5oBf9]2yKvivbBА:("L?xl#t42x|X #Ta( l T"gv1_TE3Q6 Oǐa###ݧ)|7?~ gˠ'!*@@E߿?~koh훫'z/_@'- ߯~A Rgcjo,G+H %  Ѡ}Aq"+_c ҦrOKs1 gdt~1>2 ث@ͻA^ w~o|)7Ms'8`1M {NOa81uE bfdg,lAڇeu5Xĸظ1z- gn`8=wow$݃>@DO7?OXߨGv@e^#rR'V? ^ӛ]+r#@!+ 0Js.dbg 1El@ -kIENDB`mayavi-4.1.0/tvtk/pipeline/images/actor.png0000644000175100001440000000153511674464502021660 0ustar ischnellusers00000000000000PNG  IHDRabKGD pHYs  tIME  IDAT8mk\UsϹ?3tZ&֢4ՂТ`TK7+ֿ¥ӥ b;(-!iӦ0if܉s9…]}V(WR 41oF,f[+\uo5xw]&=|,MǞCr_{Kw{Ju@\ wуN4amb?8+&`ݛ7م]ZP|xGv!: obM5•(6Q%˜95%570B 2Ji Bq*l(,Ҳ aYDGD =RA&d](q A{1:ViBԄ OwaeaA .pHbƂ2X BU>* aX렌B X2m KrA%`cJ[d>$a1V_i (v)T*K2"P:2P0` TRVʤ@Gr5#U֖ A 02Yq mkwn .~CN8q'_q<neX%U~2ԕb˃c{䀂c53'_Hȱj<^Vcgm΃ 7@ѱP+`ćPoD_5C5pܿV ,c%*IENDB`mayavi-4.1.0/tvtk/pipeline/images/lookuptable.png0000644000175100001440000000054111674464502023065 0ustar ischnellusers00000000000000PNG  IHDRabKGD pHYs  tIME  8"xIDAT8˵1j0_c<f =D@K7kNƘ?ıCIL"~O QoYexD5š4MrUU;7Ooqm/#.׃ 9u]𠠛/r8(R%#dqp!rI "243mΆ{o3Sh>oxq }Ϯhwв[N;ܿ|π=NAIENDB`mayavi-4.1.0/tvtk/pipeline/images/coordinate.png0000644000175100001440000000177611674464502022706 0ustar ischnellusers00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb?% X@ >>_\\ _d`e`g '#7&&EVnno!aagyII%/3b1H٘V^EU\B@^^^TRPUA;7o Pcf`@`5BT\ZҒ]JJovƇ~?~s>0225y@`d8Κ!,(3qu~] ~ @ĝ/__JI305ן X~3j hj ^f_3Ԥw@y 2r b@ 6Ǐn=XG_ᯘ}BX7`fa`@;8#Sd3g /20'3h< Tdo|@`6~|_f߹%_WNΎ;ÿa𛅙A 59EW.$.|7005;_^?0%e8P@ 0fff`o01* @'uf~>fL }gcgb  3þ^}2&Kǎ0|axvn9ڍk? A)dqϟ o޿gx {x%#:7o<{O {|@`|E_cm?}@%'Cv8##󗁁*X:` ?$ؑ 0shIENDB`mayavi-4.1.0/tvtk/pipeline/browser.py0000644000175100001440000007106111674464502020633 0ustar ischnellusers00000000000000"""A tvtk pipeline browser. An abstract `TreeGenerator` class defines the interface of a tree generator. This class is responsible for generating the list of children. Often tvtk object's children are collections of various objects, some sequences and some simple objects. In order to provide a unified interface to all children, all of these objects are wrapped using the `CompositeIterable` which presents all children as a single iterable. `SimpleTreeGenerator` does not do extensive analysis of the passed object in order to compute the children. `FullTreeGenerator` however uses the algorithm that MayaVi-1.x uses and therefore generates a large number of objects. The `PipelineBrowser` class presents the view of the pipeline as a tree. It allows one to specify the TreeGenerator instance. The `TreeEditor` from the traits package is used to represent the view. A `TVTKLeafNode` defines a node that has no children. A `TVTKBranchNode` is a node that has children. The nodes basically wrap up the tvtk object and present an interface suitable for the TreeEditor. TODO: * When a node is selected, the actor involved could be highlighted. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Standard library imports. import re # Enthought library imports. from traits.api import HasTraits, Property, Any, Instance, \ Trait, List, Str, Dict, Python from traitsui.api import \ TreeEditor, TreeNodeObject, ObjectTreeNode, View, Item, Group from traitsui.menu import Menu, Action from tvtk.api import tvtk from tvtk import messenger from tvtk.tvtk_base import TVTKBase from tvtk.tvtk_base_handler import TVTKBaseHandler from tvtk.common import camel2enthought ###################################################################### # Utility functions. ###################################################################### def is_iterable(x): return hasattr(x, '__iter__') def get_icon(object_name): """Given the name of the object, this function returns an appropriate icon image name. If no icon is appropriate it returns the empty string.""" # The mapping from names to icons. icon_map = {'actor': 'actor.png', 'camera': 'camera.png', 'coordinate': 'coordinate.png', 'filter': 'filter.png', 'lookuptable': 'lookuptable.png', 'mapper': 'mapper.png', 'polydata': 'polydata.png', 'property': 'property.png', 'reader': 'reader.png', 'renderer': 'renderer.png', 'rendererwindowinteractor': 'rendererwindowinteractor.png', 'source': 'source.png', 'texture': 'texture.png', 'window': 'window.png', 'writer': 'writer.png', } # Lower case the name. name = object_name.lower() for key in icon_map: if name.endswith(key): return icon_map[key] # No valid icon for this object. return '' ###################################################################### # `TreeGenerator` class. ###################################################################### class TreeGenerator(HasTraits): """Encapsulates the methods that generate the tree via the `get_children` method.""" def has_children(self, obj): """Returns True if object `obj` has children.""" raise NotImplementedError def get_children(self, obj): """Returns a dictionary containing the children of the object `obj`.""" raise NotImplementedError def get_node(self, obj): """Get a node object representing this object.""" raise NotImplementedError def get_nodes(self, menu): """Returns a list of nodes for the tree editor. The menu entries to use for the nodes are passed as `menu`.""" raise NotImplementedError ###################################################################### # `SimpleTreeGenerator` class. ###################################################################### class SimpleTreeGenerator(TreeGenerator): """This particular class generates a simple pipeline representation. Not every possible object is obtained.""" def has_children(self, obj): """Returns true of the object has children, false if not. This is very specific to tvtk objects.""" if isinstance(obj, (tvtk.RenderWindow, tvtk.Renderer, tvtk.Collection)): return True for attribute in ['source', 'get_input', 'input', 'mapper', 'property', 'texture', 'text_property', 'volume_property', 'lookup_table', 'producer_port', 'producer']: if hasattr(obj, attribute): return True return False def get_children(self, obj): """Returns the child objects of a particular tvtk object in a dictionary, the keys are the trait names. This is used to generate the tree in the browser.""" kids = {} def _add_kid(key, x): if x is None: kids[key] = None else: if type(x) in (type([]), type(())): x1 = [i for i in x if isinstance(i, TVTKBase)] if x1: kids[key] = x1 elif isinstance(x, TVTKBase): kids[key] = x if isinstance(obj, tvtk.RenderWindow): return {'renderers':obj.renderers} elif isinstance(obj, tvtk.Renderer): if hasattr(obj, 'view_props'): return {'view_props':obj.view_props, 'active_camera':obj.active_camera} else: return {'props':obj.props, 'active_camera':obj.active_camera} #if isinstance(obj, tvtk.Collection): # _add_kid(obj) # Misc. properties. for attribute in ['mapper', 'property', 'texture', 'text_property', 'volume_property', 'lookup_table', 'producer']: if hasattr(obj, attribute): _add_kid(attribute, getattr(obj, attribute)) # Check for sources and inputs. if hasattr(obj, 'number_of_sources'): srcs = [obj.get_source(i) \ for i in range(obj.number_of_sources)] _add_kid('source', srcs) elif hasattr(obj, 'source'): _add_kid('source', obj.source) if hasattr(obj, 'get_input'): inputs = [] if hasattr(obj, 'number_of_input_ports'): if obj.number_of_input_ports: inputs = [obj.get_input(i) \ for i in range(obj.number_of_input_ports)] else: inputs = [obj.get_input(i) \ for i in range(obj.number_of_inputs)] _add_kid('input', inputs) elif hasattr(obj, 'input'): _add_kid('input', obj.input) if hasattr(obj, 'producer_port'): _add_kid('producer_port', obj.producer_port) return kids def get_node(self, obj): """Get a node object representing the object passed.""" if self.has_children(obj): return TVTKBranchNode(object=obj, tree_generator=self) else: return TVTKLeafNode(object=obj) def get_nodes(self, menu): """Returns a list of nodes for the tree editor. The menu entries to use are given as `menu`""" nodes = [ObjectTreeNode(node_for=[TVTKBranchNode], view=View(Group(Item('object', style='custom'), show_labels=False)), auto_open=False, children='children', label='name', menu=menu, rename=False, delete=False, copy=False, insert=False), ObjectTreeNode(node_for=[TVTKLeafNode], view=View(Group(Item('object', style='custom'), show_labels=False)), auto_open=False, label='name', menu=menu, rename=False, delete=False, copy=False, insert=False), ObjectTreeNode(node_for=[TVTKCollectionNode], auto_open=True, children='children', label='name', menu=menu, rename=False, delete=False, copy=False, insert=False), ] return nodes ###################################################################### # `FullTreeGenerator` class. ###################################################################### class FullTreeGenerator(SimpleTreeGenerator): """This particular class picks up a lot more children in the pipeline and is similar to the code used in MayaVi-1.x's pipeline browser.""" def __init__(self, **traits): super(FullTreeGenerator, self).__init__(**traits) self.last_transform = 0 def get_children(self, obj): """Returns the child objects of a particular tvtk object in a dictionary, the keys are the trait names. This is used to generate the tree in the browser.""" vtk_obj = tvtk.to_vtk(obj) methods = self._get_methods(vtk_obj) kids = {} def _add_kid(key, x): if x is None: kids[key] = None else: if type(x) in (type([]), type(())): x1 = [i for i in x if isinstance(i, TVTKBase)] if x1: kids[key] = x1 elif isinstance(x, TVTKBase): if hasattr(x, '__iter__'): # Don't add iterable objects that contain non # acceptable nodes if len(list(x)) and isinstance(list(x)[0], TVTKBase): kids[key] = x else: kids[key] = x for method in methods: attr = camel2enthought(method[0]) if hasattr(obj, attr): _add_kid(attr, getattr(obj, attr)) # Check for sources and inputs. if hasattr(obj, 'number_of_sources'): srcs = [obj.get_source(i) \ for i in range(obj.number_of_sources)] _add_kid('source', srcs) elif hasattr(obj, 'source'): _add_kid('source', obj.source) if hasattr(obj, 'get_input'): inputs = [] if hasattr(obj, 'number_of_input_ports'): if obj.number_of_input_ports: # Sometimes not all the inputs can be retrieved using # 'get_input', as they may be sources (for instance # the ProbeFilter). inputs = list() for i in range(obj.number_of_input_ports): try: inputs.append(obj.get_input(i)) except TypeError: pass if not inputs: inputs = [obj.get_input()] else: inputs = [obj.get_input(i) \ for i in range(obj.number_of_inputs)] _add_kid('input', inputs) elif hasattr(obj, 'input'): _add_kid('input', obj.input) if hasattr(obj, 'producer_port'): _add_kid('producer_port', obj.producer_port) return kids def has_children(self, obj): """Returns true of the object has children, false if not. This is very specific to tvtk objects.""" if isinstance(obj, (tvtk.RenderWindow, tvtk.Renderer, tvtk.Collection)): return True for attribute in ['source', 'get_input', 'input', 'mapper', 'property', 'texture', 'text_property', 'volume_property', 'lookup_table', 'producer_port', 'producer']: if hasattr(obj, attribute): return True # FIXME: This is inefficient. We probably should cache the # get_children call. if self.get_children(obj): return True return False ########################################################################### # Non-public interface. ########################################################################### def _get_methods(self, vtk_obj): """Obtain the various methods from the passed object.""" def _remove_method(name, methods, method_names): """Removes methods if they have a particular name.""" try: idx = method_names.index(name) except ValueError: pass else: del methods[idx], method_names[idx] return methods, method_names # The following code basically gets the 'str' representation # of the VTK object and parses it to obtain information about # the object's children. It is a hack but has worked well for # a *very* long time with MayaVi-1.x and before. # Oops, this isn't a VTK object. if not hasattr(vtk_obj, 'GetClassName'): return [] methods = str(vtk_obj) methods = methods.split("\n") del methods[0] # using only the first set of indented values. patn = re.compile(" \S") for method in methods[:]: if patn.match(method): if method.find(":") == -1: methods.remove(method) elif method[1].find("none") > -1: methods.remove(method) else: methods.remove(method) # Props/Prop is deprecated in more recent VTK releases. for method in methods[:]: if method.strip()[:6] == "Props:": if hasattr(vtk_obj, "GetViewProps"): methods.remove(method) methods.append("ViewProps: ") elif method.strip()[:5] == "Prop:": if hasattr(vtk_obj, "GetViewProp"): methods.remove(method) methods.append("ViewProp: ") method_names = [] for i in range(0, len(methods)): strng = methods[i].replace(" ", "") methods[i] = strng.split(":") method_names.append(methods[i][0]) if re.match("vtk\w*Renderer", vtk_obj.GetClassName()): methods.append(["ActiveCamera", ""]) if re.match("vtk\w*Assembly", vtk_obj.GetClassName()): methods.append(["Parts", ""]) methods.append(["Volumes", ""]) methods.append(["Actors", ""]) if vtk_obj.IsA('vtkAbstractTransform'): if self.last_transform > 0: _remove_method('Inverse', methods, method_names) else: self.last_transform += 1 else: self.last_transform = 0 # Some of these object are removed because they arent useful in # the browser. I check for Source and Input anyway so I dont need # them. for name in('Output', 'FieldData', 'CellData', 'PointData', 'Source', 'Input', 'ExtentTranslator', 'Interactor', 'Lights', 'Information', 'Executive'): _remove_method(name, methods, method_names) return methods ###################################################################### # `CompositeIterable` class. ###################################################################### class CompositeIterable(HasTraits): """This class allows one to iterate over a bunch of disparate objects treating them as one single iterable. Each of the iterated objects is wrapped with a suitable Node class so that the object may be used in a Traits Tree Editor. """ tree_generator = Instance(TreeGenerator) def __init__(self, args, **traits): super(CompositeIterable, self).__init__(**traits) self.args = args def __iter__(self): tg = self.tree_generator for arg in self.args: if is_iterable(arg): for x in arg: yield tg.get_node(x) else: yield tg.get_node(arg) def __len__(self): x = 0 for arg in self.args: if is_iterable(arg): x += len(arg) else: x += 1 return x ###################################################################### # `TVTKLeafNode` class. ###################################################################### class TVTKLeafNode(TreeNodeObject): """Represents a leaf in the tree view.""" # The tvtk object being wrapped. object = Instance(TVTKBase) # Name to show on the view. name = Property # Work around problem with HasPrivateTraits. __ = Python def __hash__(self): return hash(tvtk.to_vtk(self.object)) def _get_name(self): return self.object.__class__.__name__ ###################################################################### # `TreeNodeObject` interface ###################################################################### def tno_get_icon(self, node, is_expanded): """ Returns the icon for a specified object. """ icon = get_icon(self.name) if icon: return icon else: return super(TVTKLeafNode, self).tno_get_icon(node, is_expanded) ###################################################################### # `TVTKBranchNode` class. ###################################################################### class TVTKBranchNode(TreeNodeObject): """Represents a branch in the tree view. The `children` trait produces an iterable that represents the children of the branch. """ # The tvtk object being wrapped. object = Instance(TVTKBase) # Children of the object. children = Property # Name to show on the view. name = Property # Tree generator to use. tree_generator = Instance(TreeGenerator) # Cache of children. children_cache = Dict # Work around problem with HasPrivateTraits. __ = Python def __init__(self, **traits): super(TVTKBranchNode, self).__init__(**traits) def __del__(self): try: self._remove_listners() except: pass def __hash__(self): return hash(tvtk.to_vtk(self.object)) def _get_children_from_cache(self): return [x for x in self.children_cache.values() if x is not None] def _create_children(self): kids = self.tree_generator.get_children(self.object) self.children_cache = kids self._setup_listners() def _setup_listners(self): object = self.object kids = self.children_cache for key, val in kids.items(): if isinstance(val, tvtk.Collection): vtk_obj = tvtk.to_vtk(val) messenger.connect(vtk_obj, 'ModifiedEvent', self._notify_children) else: object.on_trait_change(self._notify_children, key) def _remove_listners(self): object = self.object kids = self.children_cache for key, val in kids.items(): if isinstance(val, tvtk.Collection): vtk_obj = tvtk.to_vtk(val) messenger.disconnect(vtk_obj, 'ModifiedEvent', self._notify_children) else: object.on_trait_change(self._notify_children, key, remove=True) def _notify_children(self, obj=None, name=None, old=None, new=None): old_val = self._get_children_from_cache() self._remove_listners() self._create_children() new_val = self._get_children_from_cache() self.trait_property_changed('children', old_val, new_val) def _get_children(self): if not self.children_cache: self._create_children() kids = self._get_children_from_cache() tg = self.tree_generator return CompositeIterable(kids, tree_generator=tg) def _get_name(self): return self.object.__class__.__name__ ###################################################################### # `TreeNodeObject` interface ###################################################################### def tno_get_icon(self, node, is_expanded): """ Returns the icon for a specified object. """ icon = get_icon(self.name) if icon: return icon else: return super(TVTKBranchNode, self).tno_get_icon(node, is_expanded) ###################################################################### # `TVTKCollectionNode` class. ###################################################################### class TVTKCollectionNode(TreeNodeObject): """Represents a collection of typically unconnected roots in the tree view. """ # List of child nodes. object = List(TVTKBase) # Children of the object. children = Property # Name to show on the view. name = Str # Tree generator to use. tree_generator = Instance(TreeGenerator) # Work around problem with HasPrivateTraits. __ = Python def __init__(self, **traits): super(TVTKCollectionNode, self).__init__(**traits) def _get_children(self): tg = self.tree_generator return CompositeIterable(self.object, tree_generator=tg) ###################################################################### # `CloseHandler` class. ###################################################################### class UICloseHandler(TVTKBaseHandler): """This class cleans up after the UI for the object is closed.""" # The browser associated with this UI. browser = Any def close(self, info, is_ok): """This method is invoked when the user closes the UI.""" obj = info.object obj.on_trait_change(self.browser.render, remove=True) return True ###################################################################### # `PipelineBrowser` class. ###################################################################### class PipelineBrowser(HasTraits): # The tree generator to use. tree_generator = Trait(FullTreeGenerator(), Instance(TreeGenerator)) # The TVTK render window(s) associated with this browser. renwins = List # The root object to view in the pipeline. If None (default), the # root object is the render_window of the Scene instance passed at # object instantiation time. root_object = List(TVTKBase) # Private traits. # The root of the tree to display. _root = Any ########################################################################### # `object` interface. ########################################################################### def __init__(self, renwin=None, **traits): """Initializes the object. Parameters ---------- - renwin: `Scene` instance. Defaults to None. This may be passed in addition to the renwins attribute which can be a list of scenes. """ super(PipelineBrowser, self).__init__(**traits) self.ui = None self.view = None if renwin: self.renwins.append(renwin) self._root_object_changed(self.root_object) menu = Menu(Action(name='Refresh', action='editor.update_editor'), Action(name='Expand all', action='editor.expand_all')) self.menu = menu nodes = self.tree_generator.get_nodes(menu) self.tree_editor = TreeEditor(nodes=nodes, editable=False, orientation='vertical', hide_root=True, on_dclick=self._on_dclick) self.view = View(Group(Item(name='_root', editor=self.tree_editor, resizable=True), show_labels=False, show_border=False, orientation='vertical'), title='Pipeline browser', help=False, resizable=True, undo=False, revert=False, width=.3, height=.3) ########################################################################### # `PipelineBrowser` interface. ########################################################################### def show(self, parent=None): """Show the tree view if not already show. If optional `parent` widget is passed, the tree is displayed inside the passed parent widget.""" # If UI already exists, raise it and return. if self.ui and self.ui.control: try: self.ui.control.Raise() except AttributeError: pass else: return else: # No active ui, create one. if parent: self.ui = self.view.ui(self, parent=parent, kind='subpanel') else: self.ui = self.view.ui(self, parent=parent) def update(self): """Update the tree view.""" # This is a hack. if self.ui and self.ui.control: try: ed = self.ui._editors[0] ed.update_editor() self.ui.control.Refresh() except (AttributeError, IndexError): pass # Another name for update. refresh = update def render(self): """Calls render on all render windows associated with this browser.""" for rw in self.renwins: rw.render() ########################################################################### # Non-public interface. ########################################################################### def _make_default_root(self): tree_gen = self.tree_generator objs = [x.render_window for x in self.renwins] node = TVTKCollectionNode(object=objs, name="Root", tree_generator=tree_gen) return node def _tree_generator_changed(self, tree_gen): """Traits event handler.""" if self._root: root_obj = self._root.object else: root_obj = self.root_object if root_obj: ro = root_obj if not hasattr(root_obj, '__len__'): ro = [root_obj] self._root = TVTKCollectionNode(object=ro, name="Root", tree_generator=tree_gen) else: self._root = self._make_default_root() self.tree_editor.nodes = tree_gen.get_nodes(self.menu) self.update() def _root_object_changed(self, root_obj): """Trait handler called when the root object is assigned to.""" tg = self.tree_generator if root_obj: self._root = TVTKCollectionNode(object=root_obj, name="Root", tree_generator=tg) else: self._root = self._make_default_root() self.root_object = self._root.object self.update() def _root_object_items_changed(self, list_event): """Trait handler called when the items of the list change.""" self._root_object_changed(self.root_object) def _on_dclick(self, obj): """Callback that is called when nodes are double-clicked.""" if hasattr(obj, 'object') and hasattr(obj.object, 'edit_traits'): object = obj.object view = object.trait_view() view.handler = UICloseHandler(browser=self) object.on_trait_change(self.render) ui = object.edit_traits(view=view) ###################################################################### # Test cases. ###################################################################### def main(instantiate_gui=True): """Simple test case.""" from tvtk.tools import ivtk v = ivtk.viewer(browser=False, instantiate_gui=instantiate_gui) cs = tvtk.ConeSource() m = tvtk.PolyDataMapper(input=cs.output) a = tvtk.Actor(mapper=m) v.scene.add_actor(a) v.scene.reset_zoom() b = PipelineBrowser(v.scene) b.show() return v, b, a if __name__ == '__main__': from pyface.api import GUI gui = GUI() main(instantiate_gui=False) gui.start_event_loop() mayavi-4.1.0/tvtk/pipeline/__init__.py0000644000175100001440000000013211674464502020676 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/tvtk/indenter.py0000644000175100001440000002057211674464502017154 0ustar ischnellusers00000000000000"""This module defines an indenter class that handles indentation levels for automatic code generation. It also defines other miscellaneous classes useful for the tvtk code generation. """ # Author: Prabhu Ramachandran # Copyright (c) 2004, Enthought, Inc. # License: BSD Style. import re # Local imports (there is a good reason for the relative imports). from common import get_tvtk_name, camel2enthought ###################################################################### # `Indent` class. ###################################################################### class Indent: """This class manages indentation levels for dynamically generated Python code. The class also provides a method that formats a text string suitably at a given indentation level. """ def __init__(self, nspace=4): """Initializes the object. Parameters ---------- - nspace : `int` Specifies the number of spaces to use for each indentation level. Defaults to 4. """ self.tab = '' self.txt = '' self.nspace = 0 self.set_tab(nspace) self.space_re = re.compile(r'^\s*$') self.find_space_re = re.compile(r'\s*(\S)') def __repr__(self): return self.txt def set_tab(self, nspace): """Set the number of spaces a tab represents.""" self.nspace = nspace self.tab = ' '*nspace def reset(self): """Reset the indentation level to 0.""" self.txt = '' def incr(self): """Increase the indentation level.""" self.txt += self.tab def decr(self): """Decrease the indentation level.""" self.txt = self.txt[:-self.nspace] def format(self, txt): """Formats given text as per current indentation levels. Note that the returned string always ends with a newline to avoid problems for subsequent lines in the output that have to deal trailing garbage in the last line. Parameters ---------- - txt : `string` Input text string to be formatted. Can contain newlines. If the input text is a single line of text then leading space is stripped and the current indentation is added to the left along with a newline and the resulting string is returned. If the input text is multi-line input the indentation of the first line is ignored, and the second line is considered. All subsequent lines have the current indentation followed by any extra space from the default indentation. """ space_re = self.space_re find_space_re = self.find_space_re d = txt.split('\n') one_liner = 1 if len(d) > 1: for i in d[1:]: if not space_re.match(i): one_liner = 0 break elif len(d) == 0: return '\n' if one_liner: return '%s%s\n'%(repr(self), d[0].strip()) else: strip_idx = 0 m = find_space_re.match(d[1]) try: strip_idx = m.start(1) except AttributeError: strip_idx = 0 ret = [] if not space_re.match(d[0]): ret.append('%s%s'%(repr(self), d[0])) for i in d[1:]: if i: ret.append('%s%s'%(repr(self), i[strip_idx:])) else: ret.append(repr(self)) if space_re.match(ret[-1]): ret[-1] = '' else: ret.append('') return '\n'.join(ret) ###################################################################### # `VTKDocMassager` class. ###################################################################### class VTKDocMassager: """This class massages the documentation strings suitably for inclusion in the TVTK code. The names of all the VTK classes are changed suitably and when possible the method names are also changed. This class is *not* generic and is *very* specific to VTK documentation strings. """ def __init__(self): self.renamer = re.compile(r'(vtk[A-Z0-9]\S+)') self.ren_func = lambda m: get_tvtk_name(m.group(1)) self.func_re = re.compile(r'([a-z0-9]+[A-Z])') self.cpp_method_re = re.compile(r'C\+\+: .*?;\n*') ################################################################# # `VTKDocMassager` interface. ################################################################# def write_class_doc(self, doc, out, indent): """Write processed class documentation string into `out`. Parameters ---------- - doc : `string` The documentation string. - out : file line object. - indent : `Indent` """ ret = self.massage(doc) indent.incr() out.write(indent.format('"""')) out.write(indent.format('\n' + ret)) out.write(indent.format('"""')) indent.decr() def write_trait_doc(self, doc, out, indent): """Write processed trait documentation string into `out`. This method removes the call signature information from the method. Parameters ---------- - doc : `string` The documentation string. - out : file line object. - indent : `Indent` """ ret = self._remove_sig(doc) indent.incr() out.write(indent.format('"""')) out.write(indent.format('\n'+self.massage(ret))) out.write(indent.format('"""')) indent.decr() def write_method_doc(self, doc, out, indent): """Write processed method documentation string into `out`. The method signature is appopriately massaged. Parameters ---------- - doc : `string` The documentation string. - out : file line object. - indent : `Indent` """ orig_name = doc[2:doc.find('(')] name = camel2enthought(orig_name) my_sig = self._rename_class(doc[:doc.find('\n\n')]) my_sig = self.cpp_method_re.sub('', my_sig) my_sig = my_sig.replace('V.'+orig_name, 'V.'+name) indent.incr() out.write(indent.format('"""')) out.write(indent.format(my_sig)) ret = self._remove_sig(doc) if ret: out.write('\n') out.write(indent.format('\n'+self.massage(ret))) out.write(indent.format('"""')) indent.decr() def get_method_doc(self, doc): """Return processed method documentation string from `doc`. The method signature is appopriately massaged. Parameters ---------- - doc : `string` The documentation string. """ orig_name = doc[2:doc.find('(')] name = camel2enthought(orig_name) my_sig = self._rename_class(doc[:doc.find('\n\n')]) my_sig = self.cpp_method_re.sub('', my_sig) my_sig = my_sig.replace('V.'+orig_name, 'V.'+name) ret = self.massage(self._remove_sig(doc)) if ret: return my_sig + '\n' + ret else: return my_sig def massage(self, doc): """Returns massaged documentation string from passed docstring, `doc`. This method basically renames the methods and classes in the docstring. """ ret = self._rename_methods(doc) ret = self._rename_class(ret) return ret ################################################################# # Non-public interface. ################################################################# def _rename_class(self, doc): return self.renamer.sub(self.ren_func, doc) def _remove_sig(self, doc): idx = doc.find('\n\n') + 2 if len(doc) > idx: return doc[idx:] else: return '' def _rename_methods(self, doc): lines = doc.split('\n') nl = [] for line in lines: words = line.split(' ') nw = [] for word in words: if word[:3] == 'vtk': nw.append(word) else: if self.func_re.search(word): nw.append(camel2enthought(word)) else: nw.append(word) nl.append(' '.join(nw)) return '\n'.join(nl) mayavi-4.1.0/tvtk/setup.py0000755000175100001440000000477211674464502016513 0ustar ischnellusers00000000000000#!/usr/bin/env python # Setup script for TVTK, numpy.distutils based. # # import os, sys def configuration(parent_package=None, top_path=None): from os.path import join from numpy.distutils.misc_util import Configuration config = Configuration('tvtk',parent_package,top_path) config.set_options(ignore_setup_xxx_py=True, assume_default_configuration=True, delegate_options_to_subpackages=True, quiet=True) config.add_subpackage('custom') config.add_subpackage('pipeline') config.add_data_dir('pipeline/images') config.add_data_dir('pyface/images') config.add_data_dir('tools/images') config.add_subpackage('plugins') config.add_subpackage('plugins.*') config.add_subpackage('tools') config.add_subpackage('util') config.add_subpackage('tests') # Numpy support. config.add_extension('array_ext', sources = [join('src','array_ext.c')], depends = [join('src','array_ext.pyx')], ) tvtk_classes_zip_depends = config.paths( 'code_gen.py','wrapper_gen.py', 'special_gen.py', 'tvtk_base.py', 'indenter.py', 'vtk_parser.py') return config def gen_tvtk_classes_zip(): from code_gen import TVTKGenerator target = os.path.join(os.path.dirname(__file__), 'tvtk_classes.zip') output_dir = os.path.dirname(target) try: os.mkdir(output_dir) except: pass print '-'*70 if os.path.exists(target): print 'Deleting possibly old TVTK classes' os.unlink(target) print "Building TVTK classes...", sys.stdout.flush() cwd = os.getcwd() os.chdir(output_dir) gen = TVTKGenerator('') gen.generate_code() gen.build_zip(True) os.chdir(cwd) print "Done." print '-'*70 def vtk_version_changed(zipfile): """Checks the ZIP file's VTK build version versus the current installed version of VTK and returns `True` if the versions are different. """ result = True if os.path.exists(zipfile): import vtk vtk_version = vtk.vtkVersion().GetVTKVersion()[:3] sys.path.append(zipfile) try: from tvtk_classes.vtk_version import vtk_build_version except ImportError: result = True else: if vtk_version != vtk_build_version: result = True else: result = False sys.path.pop() return result mayavi-4.1.0/tvtk/version.py0000644000175100001440000000047211674464502017026 0ustar ischnellusers00000000000000# Wrapped in a try/except in those situations where someone hasn't installed # as an egg. What do we do then? For now, we just punt since we don't want # to define the version number in two places. try: import pkg_resources version = pkg_resources.require('Mayavi')[0].version except: version = '' mayavi-4.1.0/tvtk/array_handler.py0000644000175100001440000006454111674464502020163 0ustar ischnellusers00000000000000""" This module contains all the array handling code for TVTK. The most important functions provided by this module involve the conversion of numpy arrays/Python lists to different VTK data arrays and vice-versa. Warning: Numpy Character arrays will not work properly since there seems no unique one-to-one VTK data array type to map it to. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2008, Enthought, Inc. # License: BSD Style. import types import sys import vtk from vtk.util import vtkConstants try: from vtk.util import numpy_support except ImportError: numpy_support = None import numpy # Enthought library imports. from tvtk.array_ext import set_id_type_array # Useful constants for VTK arrays. VTK_ID_TYPE_SIZE = vtk.vtkIdTypeArray().GetDataTypeSize() if VTK_ID_TYPE_SIZE == 4: ID_TYPE_CODE = numpy.int32 elif VTK_ID_TYPE_SIZE == 8: ID_TYPE_CODE = numpy.int64 VTK_LONG_TYPE_SIZE = vtk.vtkLongArray().GetDataTypeSize() if VTK_LONG_TYPE_SIZE == 4: LONG_TYPE_CODE = numpy.int32 ULONG_TYPE_CODE = numpy.uint32 elif VTK_LONG_TYPE_SIZE == 8: LONG_TYPE_CODE = numpy.int64 ULONG_TYPE_CODE = numpy.uint64 BASE_REFERENCE_COUNT = vtk.vtkObject().GetReferenceCount() ###################################################################### # The array cache. ###################################################################### class ArrayCache(object): """Caches references to numpy arrays that are not copied but views of which are converted to VTK arrays. The caching prevents the user from deleting or resizing the numpy array after it has been sent down to VTK. The cached arrays are automatically removed when the VTK array destructs.""" ###################################################################### # `object` interface. ###################################################################### def __init__(self): # The cache. self._cache = {} def __len__(self): return len(self._cache) def __contains__(self, vtk_arr): key = vtk_arr.__this__ return self._cache.has_key(key) ###################################################################### # `ArrayCache` interface. ###################################################################### def add(self, vtk_arr, np_arr): """Add numpy array corresponding to the vtk array to the cache.""" key = vtk_arr.__this__ cache = self._cache # Setup a callback so this cached array reference is removed # when the VTK array is destroyed. Passing the key to the # `lambda` function is necessary because the callback will not # receive the object (it will receive `None`) and thus there # is no way to know which array reference one has to remove. vtk_arr.AddObserver('DeleteEvent', lambda o, e, key=key: \ self._remove_array(key)) # Cache the array cache[key] = np_arr def get(self, vtk_arr): """Return the cached numpy array given a VTK array.""" key = vtk_arr.__this__ return self._cache[key] ###################################################################### # Non-public interface. ###################################################################### def _remove_array(self, key): """Private function that removes the cached array. Do not call this unless you know what you are doing.""" try: del self._cache[key] except KeyError: pass ###################################################################### # Setup a global `_array_cache`. The array object cache caches all the # converted numpy arrays that are not copied. This prevents the user # from deleting or resizing the numpy array after it has been sent down # to VTK. ###################################################################### _dummy = None # This makes the cache work even when the module is reloaded. for name in ['array_handler', 'tvtk.array_handler']: if sys.modules.has_key(name): mod = sys.modules[name] if hasattr(mod, '_array_cache'): _dummy = mod._array_cache del mod break if _dummy: _array_cache = _dummy else: _array_cache = ArrayCache() del _dummy ###################################################################### # Array conversion functions. ###################################################################### def get_vtk_array_type(numeric_array_type): """Returns a VTK typecode given a numpy array.""" # This is a Mapping from numpy array types to VTK array types. _arr_vtk = {numpy.dtype(numpy.character):vtkConstants.VTK_UNSIGNED_CHAR, numpy.dtype(numpy.uint8):vtkConstants.VTK_UNSIGNED_CHAR, numpy.dtype(numpy.uint16):vtkConstants.VTK_UNSIGNED_SHORT, numpy.dtype(numpy.int8):vtkConstants.VTK_CHAR, numpy.dtype(numpy.int16):vtkConstants.VTK_SHORT, numpy.dtype(numpy.int32):vtkConstants.VTK_INT, numpy.dtype(numpy.uint32):vtkConstants.VTK_UNSIGNED_INT, numpy.dtype(numpy.float32):vtkConstants.VTK_FLOAT, numpy.dtype(numpy.float64):vtkConstants.VTK_DOUBLE, numpy.dtype(numpy.complex64):vtkConstants.VTK_FLOAT, numpy.dtype(numpy.complex128):vtkConstants.VTK_DOUBLE, } _extra = {numpy.dtype(ID_TYPE_CODE):vtkConstants.VTK_ID_TYPE, numpy.dtype(ULONG_TYPE_CODE):vtkConstants.VTK_UNSIGNED_LONG, numpy.dtype(LONG_TYPE_CODE):vtkConstants.VTK_LONG, } for t in _extra: if t not in _arr_vtk: _arr_vtk[t] = _extra[t] try: return _arr_vtk[numeric_array_type] except KeyError: for key in _arr_vtk: if numpy.issubdtype(numeric_array_type, key): return _arr_vtk[key] raise TypeError, "Couldn't translate array's type to VTK" def get_vtk_to_numeric_typemap(): """Returns the VTK array type to numpy array type mapping.""" _vtk_arr = {vtkConstants.VTK_BIT:numpy.bool, vtkConstants.VTK_CHAR:numpy.int8, vtkConstants.VTK_UNSIGNED_CHAR:numpy.uint8, vtkConstants.VTK_SHORT:numpy.int16, vtkConstants.VTK_UNSIGNED_SHORT:numpy.uint16, vtkConstants.VTK_INT:numpy.int32, vtkConstants.VTK_UNSIGNED_INT:numpy.uint32, vtkConstants.VTK_LONG:LONG_TYPE_CODE, vtkConstants.VTK_UNSIGNED_LONG:ULONG_TYPE_CODE, vtkConstants.VTK_ID_TYPE:ID_TYPE_CODE, vtkConstants.VTK_FLOAT:numpy.float32, vtkConstants.VTK_DOUBLE:numpy.float64} return _vtk_arr def get_numeric_array_type(vtk_array_type): """Returns a numpy array typecode given a VTK array type.""" return get_vtk_to_numeric_typemap()[vtk_array_type] def get_sizeof_vtk_array(vtk_array_type): """Returns the size of a VTK array type.""" _size_dict = {vtkConstants.VTK_BIT : 1, vtkConstants.VTK_CHAR : 1, vtkConstants.VTK_UNSIGNED_CHAR : 1, vtkConstants.VTK_SHORT : 2, vtkConstants.VTK_UNSIGNED_SHORT : 2, vtkConstants.VTK_INT : 4, vtkConstants.VTK_UNSIGNED_INT : 4, vtkConstants.VTK_LONG : VTK_LONG_TYPE_SIZE, vtkConstants.VTK_UNSIGNED_LONG : VTK_LONG_TYPE_SIZE, vtkConstants.VTK_ID_TYPE : VTK_ID_TYPE_SIZE, vtkConstants.VTK_FLOAT : 4, vtkConstants.VTK_DOUBLE : 8 } return _size_dict[vtk_array_type] def create_vtk_array(vtk_arr_type): """Internal function used to create a VTK data array from another VTK array given the VTK array type. """ tmp = vtk.vtkDataArray.CreateDataArray(vtk_arr_type) # CreateDataArray sets the refcount to 3 and this causes a severe # memory leak. tmp.SetReferenceCount(BASE_REFERENCE_COUNT) return tmp def array2vtk(num_array, vtk_array=None): """Converts a real numpy Array (or a Python list) to a VTK array object. This function only works for real arrays. Complex arrays are NOT handled. It also works for multi-component arrays. However, only 1, and 2 dimensional arrays are supported. This function is very efficient, so large arrays should not be a problem. Even in cases when no copy of the numpy array data is performed, a reference to the array is cached. The passed array can therefore be deleted safely in all circumstances. Parameters ---------- - num_array : numpy array or Python list/tuple The input array must be 1 or 2D. A copy of the numeric array data passed is made in the following circumstances: 1. A Python list/tuple was passed. 2. A non-contiguous numpy array was passed. 3. A `vtkBitArray` instance was passed as the second argument. 4. The types of the `vtk_array` and the `num_array` are not equivalent to each other. For example if one is an integer array and the other a float. - vtk_array : `vtkDataArray` (default: `None`) If an optional `vtkDataArray` instance, is passed as an argument then a new array is not created and returned. The passed array is itself returned. """ z = numpy.asarray(num_array) shape = z.shape assert len(shape) < 3, \ "Only arrays of dimensionality 2 or lower are allowed!" assert not numpy.issubdtype(z.dtype, complex), \ "Complex numpy arrays cannot be converted to vtk arrays."\ "Use real() or imag() to get a component of the array before"\ " passing it to vtk." # First create an array of the right type by using the typecode. # Bit arrays need special casing. bit_array = False if vtk_array is None: vtk_typecode = get_vtk_array_type(z.dtype) result_array = create_vtk_array(vtk_typecode) elif vtk_array.GetDataType() == vtkConstants.VTK_BIT: vtk_typecode = vtkConstants.VTK_CHAR result_array = create_vtk_array(vtkConstants.VTK_CHAR) bit_array = True else: vtk_typecode = vtk_array.GetDataType() result_array = vtk_array # Find the shape and set number of components. if len(shape) == 1: result_array.SetNumberOfComponents(1) else: result_array.SetNumberOfComponents(shape[1]) result_array.SetNumberOfTuples(shape[0]) # Ravel the array appropriately. arr_dtype = get_numeric_array_type(vtk_typecode) if numpy.issubdtype(z.dtype, arr_dtype): z_flat = numpy.ravel(z) else: z_flat = numpy.ravel(z).astype(arr_dtype) # Point the VTK array to the numpy data. The last argument (1) # tells the array not to deallocate. result_array.SetVoidArray(numpy.getbuffer(z_flat), len(z_flat), 1) if bit_array: # Handle bit arrays -- they have to be copied. Note that bit # arrays are used ONLY when the user has passed one as an # argument to this function. vtk_array.SetNumberOfTuples(result_array.GetNumberOfTuples()) vtk_array.SetNumberOfComponents(result_array.GetNumberOfComponents()) for i in range(result_array.GetNumberOfComponents()): vtk_array.CopyComponent(i, result_array, i) result_array = vtk_array else: # Save a reference to the flatted array in the array cache. # This prevents the user from deleting or resizing the array # and getting into serious trouble. This is only done for # non-bit array cases where the data is not copied. global _array_cache _array_cache.add(result_array, z_flat) return result_array def vtk2array(vtk_array): """Converts a VTK data array to a numpy array. Given a subclass of vtkDataArray, this function returns an appropriate numpy array containing the same data. The function is very efficient since it uses the VTK imaging pipeline to convert the data. If a sufficiently new version of VTK (5.2) is installed then it actually uses the buffer interface to return a view of the VTK array in the returned numpy array. Parameters ---------- - vtk_array : `vtkDataArray` The VTK data array to be converted. """ typ = vtk_array.GetDataType() assert typ in get_vtk_to_numeric_typemap().keys(), \ "Unsupported array type %s"%typ shape = vtk_array.GetNumberOfTuples(), \ vtk_array.GetNumberOfComponents() if shape[0] == 0: dtype = get_numeric_array_type(typ) return numpy.array([], dtype) # First check if this array already has a numpy array cached, if # it does, reshape that and return it. if vtk_array in _array_cache: arr = _array_cache.get(vtk_array) if shape[1] == 1: shape = (shape[0], ) arr = numpy.reshape(arr, shape) return arr # If VTK's new numpy support is available, use the buffer interface. if numpy_support is not None and typ != vtkConstants.VTK_BIT: dtype = get_numeric_array_type(typ) result = numpy.frombuffer(vtk_array, dtype=dtype) if shape[1] == 1: shape = (shape[0], ) result.shape = shape return result # Setup an imaging pipeline to export the array. img_data = vtk.vtkImageData() img_data.SetDimensions(shape[0], 1, 1) if typ == vtkConstants.VTK_BIT: iarr = vtk.vtkCharArray() iarr.DeepCopy(vtk_array) img_data.GetPointData().SetScalars(iarr) elif typ == vtkConstants.VTK_ID_TYPE: # Needed since VTK_ID_TYPE does not work with VTK 4.5. iarr = vtk.vtkLongArray() iarr.SetNumberOfTuples(vtk_array.GetNumberOfTuples()) nc = vtk_array.GetNumberOfComponents() iarr.SetNumberOfComponents(nc) for i in range(nc): iarr.CopyComponent(i, vtk_array, i) img_data.GetPointData().SetScalars(iarr) else: img_data.GetPointData().SetScalars(vtk_array) img_data.SetNumberOfScalarComponents(shape[1]) if typ == vtkConstants.VTK_ID_TYPE: # Hack necessary because vtkImageData can't handle VTK_ID_TYPE. img_data.SetScalarType(vtkConstants.VTK_LONG) r_dtype = get_numeric_array_type(vtkConstants.VTK_LONG) elif typ == vtkConstants.VTK_BIT: img_data.SetScalarType(vtkConstants.VTK_CHAR) r_dtype = get_numeric_array_type(vtkConstants.VTK_CHAR) else: img_data.SetScalarType(typ) r_dtype = get_numeric_array_type(typ) img_data.Update() exp = vtk.vtkImageExport() exp.SetInput(img_data) # Create an array of the right size and export the image into it. im_arr = numpy.empty((shape[0]*shape[1],), r_dtype) exp.Export(im_arr) # Now reshape it. if shape[1] == 1: shape = (shape[0], ) im_arr = numpy.reshape(im_arr, shape) return im_arr def array2vtkCellArray(num_array, vtk_array=None): """Given a nested Python list or a numpy array, this method creates a vtkCellArray instance and returns it. A variety of input arguments are supported as described in the Parameter documentation. If numpy arrays are given, this method is highly efficient. This function is most efficient if the passed numpy arrays have a typecode `ID_TYPE_CODE`. Otherwise a typecast is necessary and this involves an extra copy. This method *always copies* the input data. An alternative and more efficient way to build the connectivity list is to create a vtkIdTypeArray having data of the form (npts,p0,p1,...p(npts-1), repeated for each cell) and then call .SetCells(n_cell, id_list). Parameters ---------- - num_array : numpy array or Python list/tuple Valid values are: 1. A Python list of 1D lists. Each 1D list can contain one cell connectivity list. This is very slow and is to be used only when efficiency is of no consequence. 2. A 2D numpy array with the cell connectivity list. 3. A Python list of 2D numpy arrays. Each numeric array can have a different shape. This makes it easy to generate a cell array having cells of different kinds. - vtk_array : `vtkCellArray` (default: `None`) If an optional `vtkCellArray` instance, is passed as an argument then a new array is not created and returned. The passed array is itself modified and returned. Example ------- >>> a = [[0], [1, 2], [3, 4, 5], [6, 7, 8, 9]] >>> cells = array_handler.array2vtkCellArray(a) >>> a = numpy.array([[0,1,2], [3,4,5], [6,7,8]], 'l') >>> cells = array_handler.array2vtkCellArray(a) >>> l_a = [a[:,:1], a[:2,:2], a] >>> cells = array_handler.array2vtkCellArray(l_a) """ if vtk_array: cells = vtk_array else: cells = vtk.vtkCellArray() assert cells.GetClassName() == 'vtkCellArray', \ 'Second argument must be a `vtkCellArray` instance.' if len(num_array) == 0: return cells ######################################## # Internal functions. def _slow_array2cells(z, cells): cells.Reset() vtk_ids = vtk.vtkIdList() for i in z: vtk_ids.Reset() for j in i: vtk_ids.InsertNextId(j) cells.InsertNextCell(vtk_ids) def _get_tmp_array(arr): try: tmp_arr = numpy.asarray(arr, ID_TYPE_CODE) except TypeError: tmp_arr = arr.astype(ID_TYPE_CODE) return tmp_arr def _set_cells(cells, n_cells, id_typ_arr): vtk_arr = vtk.vtkIdTypeArray() array2vtk(id_typ_arr, vtk_arr) cells.SetCells(n_cells, vtk_arr) ######################################## msg = "Invalid argument. Valid types are a Python list of lists,"\ " a Python list of numpy arrays, or a numpy array." if issubclass(type(num_array), (types.ListType, types.TupleType)): assert len(num_array[0]) > 0, "Input array must be 2D." tp = type(num_array[0]) if issubclass(tp, types.ListType): # Pure Python list. _slow_array2cells(num_array, cells) return cells elif issubclass(tp, numpy.ndarray): # List of arrays. # Check shape of array and find total size. tot_size = 0 n_cells = 0 for arr in num_array: assert len(arr.shape) == 2, "Each array must be 2D" shp = arr.shape tot_size += shp[0]*(shp[1] + 1) n_cells += shp[0] # Create an empty array. id_typ_arr = numpy.empty((tot_size,), ID_TYPE_CODE) # Now populate it with the ids. count = 0 for arr in num_array: tmp_arr = _get_tmp_array(arr) shp = arr.shape sz = shp[0]*(shp[1] + 1) set_id_type_array(tmp_arr, id_typ_arr[count:count+sz]) count += sz # Now set them cells. _set_cells(cells, n_cells, id_typ_arr) return cells else: raise TypeError, msg elif issubclass(type(num_array), numpy.ndarray): assert len(num_array.shape) == 2, "Input array must be 2D." tmp_arr = _get_tmp_array(num_array) shp = tmp_arr.shape id_typ_arr = numpy.empty((shp[0]*(shp[1] + 1),), ID_TYPE_CODE) set_id_type_array(tmp_arr, id_typ_arr) _set_cells(cells, shp[0], id_typ_arr) return cells else: raise TypeError, msg def array2vtkPoints(num_array, vtk_points=None): """Converts a numpy array/Python list to a vtkPoints object. Unless a Python list/tuple or a non-contiguous array is given, no copy of the data is made. Thus the function is very efficient. Parameters ---------- - num_array : numpy array or Python list/tuple The input array must be 2D with `shape[1] == 3`. - vtk_points : `vtkPoints` (default: `None`) If an optional `vtkPoints` instance, is passed as an argument then a new array is not created and returned. The passed array is itself modified and returned. """ if vtk_points: points = vtk_points else: points = vtk.vtkPoints() arr = numpy.asarray(num_array) assert len(arr.shape) == 2, "Points array must be 2 dimensional." assert arr.shape[1] == 3, "Incorrect shape: shape[1] must be 3." vtk_array = array2vtk(arr) points.SetData(vtk_array) return points def array2vtkIdList(num_array, vtk_idlist=None): """Converts a numpy array/Python list to a vtkIdList object. Parameters ---------- - num_array : numpy array or Python list/tuple The input array must be 2D with `shape[1] == 3`. - vtk_idlist : `vtkIdList` (default: `None`) If an optional `vtkIdList` instance, is passed as an argument then a new array is not created and returned. The passed array is itself modified and returned. """ if vtk_idlist: ids = vtk_idlist else: ids = vtk.vtkIdList() arr = numpy.asarray(num_array) assert len(arr.shape) == 1, "Array for vtkIdList must be 1D" ids.SetNumberOfIds(len(arr)) for i, j in enumerate(arr): ids.SetId(i, j) return ids ###################################################################### # Array argument handling functions. ###################################################################### def is_array(arr): """Returns True if the passed `arr` is a numpy array or a List.""" if issubclass(type(arr), (numpy.ndarray, types.ListType)): return True return False def convert_array(arr, vtk_typ=None): """Convert the given array to the optional type specified by `vtk_typ`. Parameters ---------- - arr : numpy array/list. - vtk_typ : `string` or `None` represents the type the array is to be converted to. """ if vtk_typ: conv = {'vtkCellArray': array2vtkCellArray, 'vtkPoints': array2vtkPoints, 'vtkIdList': array2vtkIdList} if vtk_typ in conv.keys(): vtk_arr = getattr(vtk, vtk_typ)() return conv[vtk_typ](arr, vtk_arr) elif vtk_typ.find('Array') > -1: try: vtk_arr = getattr(vtk, vtk_typ)() except TypeError: # vtk_typ == 'vtkDataArray' return array2vtk(arr) else: return array2vtk(arr, vtk_arr) else: return arr else: return array2vtk(arr) def is_array_sig(s): """Given a signature, return if the signature has an array.""" if not isinstance(s, basestring): return False arr_types = ['Array', 'vtkPoints', 'vtkIdList'] for i in arr_types: if s.find(i) > -1: return True return False def is_array_or_vtkarray(arg): """Returns True if the argument is an array/Python list or if it is a vtk array.""" if is_array(arg): return True else: if hasattr(arg, '_vtk_obj'): if is_array_sig(arg._vtk_obj.__class__.__name__): return True return False def get_correct_sig(args, sigs): """Given a list of args and a collection of possible signatures, this function returns the most appropriate signature. This function is only called by deref_array. This implies that one of the signatures has an array type. """ # First do the trivial cases. if sigs is None: return None if len(sigs) == 1: return sigs[0] else: # Non-trivial cases. la = len(args) candidate_sigs = [s for s in sigs if len(s) == la] count = len(candidate_sigs) if count == 0: # No sig has the right number of args. msg = "Insufficient number of arguments to method."\ "Valid arguments are:\n%s"%sigs raise TypeError, msg elif count == 1: # If only one of the sigs has the right number of args, # return it. return candidate_sigs[0] else: # More than one sig has the same number of args. # Check if args need conversion at all. array_idx = [i for i, a in enumerate(args) \ if is_array_or_vtkarray(a)] n_arr = len(array_idx) if n_arr == 0: # No conversion necessary so signature info is # useless. return None else: # Need to find the right sig. This is done by finding # the first signature that matches all the arrays in # the argument. for sig in candidate_sigs: array_in_sig = [is_array_sig(s) for s in sig] if array_in_sig.count(True) != len(array_idx): continue bad = False for i in array_idx: if not array_in_sig[i]: bad = True if not bad: return sig # Could not find any valid signature, so give up. return None def deref_vtk(obj): """Dereferences the VTK object from the object if possible. This is duplicated from `tvtk_base.py` because I'd like to keep this module independent of `tvtk_base.py`. """ if hasattr(obj, '_vtk_obj'): return obj._vtk_obj else: return obj def deref_array(args, sigs=None): """Given a bunch of arguments and optional signature information, this converts the arguments suitably. If the argument is either a Python list or a numpy array it is converted to a suitable type based on the signature information. If it is not an array, but a TVTK object the VTK object is dereferenced. Otherwise nothing is done. If no signature information is provided the arrays are automatically converted (this can sometimes go wrong). The signature information is provided in the form of a list of lists. """ ret = [] sig = get_correct_sig(args, sigs) if sig: for a, s in zip(args, sig): if is_array(a) and is_array_sig(s): ret.append(convert_array(a, s)) else: ret.append(deref_vtk(a)) else: for a in args: if is_array(a): ret.append(convert_array(a)) else: ret.append(deref_vtk(a)) return ret mayavi-4.1.0/tvtk/custom/0000755000175100001440000000000011674464502016276 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/custom/__init__.py0000644000175100001440000000013711674464502020410 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2004-2007, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/tvtk/messenger.py0000644000175100001440000002363111674464502017333 0ustar ischnellusers00000000000000""" Implements a simple, robust, safe, Messenger class that allows one to register callbacks for a signal/slot (or event/handler) kind of messaging system. One can basically register a callback function/method to be called when an object sends a particular event. The Messenger class is Borg. So it is easy to instantiate and use. This module is also reload-safe, so if the module is reloaded the callback information is not lost. Method callbacks do not have a reference counting problem since weak references are used. The main functionality of this module is provided by three functions, `connect`, `disconnect` and `send`. Here is example usage with VTK:: >>> import messenger, vtk >>> def cb(obj, evt): ... print obj.__class__.__name__, evt ... >>> o = vtk.vtkProperty() >>> o.AddObserver('ModifiedEvent', messenger.send) 1 >>> messenger.connect(o, 'ModifiedEvent', cb) >>> >>> o.SetRepresentation(1) vtkOpenGLProperty ModifiedEvent >>> messenger.connect(o, 'AnyEvent', cb) >>> o.SetRepresentation(2) vtkOpenGLProperty ModifiedEvent vtkOpenGLProperty ModifiedEvent >>> >>> messenger.send(o, 'foo') vtkOpenGLProperty foo >>> messenger.disconnect(o, 'AnyEvent') >>> messenger.send(o, 'foo') >>> This approach is necessary if you don't want to be bitten by reference cycles. If you have a Python object holding a reference to a VTK object and pass a method of the object to the AddObserver call, you will get a reference cycle that cannot be collected by the garbage collector. Using this messenger module gets around the problem. Also note that adding a connection for 'AnyEvent' will trigger a callback no matter what event was generated. The code above also shows how disconnection works. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2007, Enthought, Inc. # License: BSD Style. __all__ = ['Messenger', 'MessengerError', 'connect', 'disconnect', 'send'] import types import sys import weakref ################################################################# # This code makes the module reload-safe. ################################################################# _saved = {} for name in ['messenger', 'tvtk.messenger']: if sys.modules.has_key(name): mod = sys.modules[name] if hasattr(mod, 'Messenger'): _saved = mod.Messenger._shared_data del mod break ################################################################# # `MessengerError` class for exceptions raised by Messenger. ################################################################# class MessengerError(Exception): pass ################################################################# # `Messenger` class. ################################################################# class Messenger: """Implements a messenger class which deals with something like signals and slots. Basically, an object can register a signal that it plans to emit. Any other object can decide to handle that signal (of that particular object) by registering itself with the messenger. When a signal is emitted the messenger calls all handlers. This makes it totally easy to deal with communication between objects. The class is Borg. Rather than use this class, please use the 'connect' and 'disconnect' functions. """ _shared_data = _saved def __init__(self): """Create the messenger. This class is Borg. So all instances are the same. """ self.__dict__ = self._shared_data if not hasattr(self, '_signals'): # First instantiation. self._signals = {} self._catch_all = ['AnyEvent', 'all'] ################################################################# # 'Messenger' interface. ################################################################# def connect(self, obj, event, callback): """ Registers a slot given an object and its signal to slot into and also given a bound method in `callback` that should have two arguments. `send` will call the callback with the object that emitted the signal and the actual event/signal as arguments. Parameters ---------- - obj : Python object Any Python object that will generate the particular event. - event : An event (can be anything, usually strings) The event `obj` will generate. If this is in the list `self._catch_all`, then any event will call this callback. - callback : `function` or `method` This callback will be called when the object generates the particular event. The object, event and any other arguments and keyword arguments given by the `obj` are passed along to the callback. """ typ = type(callback) key = hash(obj) if not self._signals.has_key(key): self._signals[key] = {} signals = self._signals[key] if not signals.has_key(event): signals[event] = {} slots = signals[event] callback_key = hash(callback) if typ is types.FunctionType: slots[callback_key] = (None, callback) elif typ is types.MethodType: obj = weakref.ref(callback.im_self) name = callback.__name__ slots[callback_key] = (obj, name) else: raise MessengerError, \ "Callback must be a function or method. "\ "You passed a %s."%(str(callback)) def disconnect(self, obj, event=None, callback=None, obj_is_hash=False): """Disconnects the object and its event handlers. Parameters ---------- - obj : Object The object that generates events. - event : The event. (defaults to None) - callback : `function` or `method` The event handler. If `event` and `callback` are None (the default) all the events and handlers for the object are removed. If only `callback` is None, only this handler is removed. If `obj` and 'event' alone are specified, all handlers for the event are removed. - obj_is_hash : `bool` Specifies if the object passed is a hash instead of the object itself. This is needed if the object is gc'd but only the hash exists and one wants to disconnect the object. """ signals = self._signals if obj_is_hash: key = obj else: key = hash(obj) if not signals.has_key(key): return if callback is None: if event is None: del signals[key] else: del signals[key][event] else: del signals[key][event][hash(callback)] def send(self, source, event, *args, **kw_args): """To be called by the object `source` that desires to generate a particular event. This function in turn invokes all the handlers for the event passing the `source` object, event and any additional arguments and keyword arguments. If any connected callback is garbage collected without being disconnected, it is silently removed from the existing slots. Parameters ---------- - source : Python object This is the object that generated the event. - event : The event. If there are handlers connected to events called 'AnyEvent' or 'all', then any event will invoke these. """ try: sigs = self._get_signals(source) except (MessengerError, KeyError): return events = self._catch_all[:] if event not in events: events.append(event) for evt in events: if sigs.has_key(evt): slots = sigs[evt] for key in slots.keys(): obj, meth = slots[key] if obj: # instance method inst = obj() if inst: getattr(inst, meth)(source, event, *args, **kw_args) else: # Oops, dead reference. del slots[key] else: # normal function meth(source, event, *args, **kw_args) def is_registered(self, obj): """Returns if the given object has registered itself with the messenger. """ try: sigs = self._get_signals(obj) except MessengerError: return 0 else: return 1 def get_signal_names(self, obj): """Returns a list of signal names the object passed has registered. """ return self._get_signals(obj).keys() ################################################################# # Non-public interface. ################################################################# def _get_signals(self, obj): """Given an object `obj` it returns the signals of that object. """ ret = self._signals.get(hash(obj)) if ret is None: raise MessengerError, \ "No such object: %s, has registered itself "\ "with the messenger."%obj else: return ret ################################################################# # Convenience functions. ################################################################# _messenger = Messenger() def connect(obj, event, callback): _messenger.connect(obj, event, callback) connect.__doc__ = _messenger.connect.__doc__ def disconnect(obj, event=None, callback=None, obj_is_hash=False): _messenger.disconnect(obj, event, callback) disconnect.__doc__ = _messenger.disconnect.__doc__ def send(obj, event, *args, **kw_args): _messenger.send(obj, event, *args, **kw_args) send.__doc__ = _messenger.send.__doc__ del _saved mayavi-4.1.0/tvtk/util/0000755000175100001440000000000011674464502015741 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/util/tk_gradient_editor.py0000644000175100001440000006526111674464502022166 0ustar ischnellusers00000000000000""" Defines the UI for a gradient editor for vtkLookupTables and color transfer functions. This code is distributed under the conditions of the BSD license. This code was originally written by Gerald Knizia and later modified by Prabhu Ramachandran Copyright (c) 2005-2006, Gerald Knizia and Prabhu Ramachandran """ import Tkinter as tk import tkFileDialog from gradient_editor import GradientTable, ColorControlPoint ########################################################################## # `GradientControl` class. ########################################################################## class GradientControl(tk.Frame): """Widget which displays the gradient represented by an GradientTable object (and does nothing beyond that)""" def __init__(self, master, gradient_table, width, height ): """master: frame in which to place the control. GradientTable is the Table to which to attach.""" tk.Frame.__init__(self, master, borderwidth=2, relief='groove') self.width = width self.height = height self.gradient_table = gradient_table assert( gradient_table.size == width ) # ^- currently only able to use gradient tables in the same # size as the canvas width self.canvas = tk.Canvas(self, background="white", width=width, height=height) self.canvas.pack() self.update() def update(self): """Repaint the control.""" self.canvas.delete(tk.ALL) # clears all lines contained. # a look around the web (http://wiki.tcl.tk/11868) told me that # using the PhotoImage tk-control would not be a good idea and # that line objects work faster. While I doubt this is an optimal # solution it currently works fast enought. xform = self.gradient_table.scaling_function start_y = 0 end_y = self.height if xform: # if a scaling transformation is provided, paint the original # gradient under the scaled gradient. start_y = self.height/2 # paint the original gradient as it stands in the table. width = self.width for x in range(width): (r,g,b,a) = self.gradient_table.get_pos_rgba_color_lerped(float(x)/(width-1)) self.canvas.create_line(x,start_y,x,end_y, \ fill="#%02x%02x%02x" % (int(255*r),int(255*g),int(255*b))) if xform: # paint the scaled gradient below end_y = start_y start_y = 0 for x in range(self.width): f = float(x)/(self.width-1) (r,g,b,a) = self.gradient_table.get_pos_rgba_color_lerped(xform(f)) self.canvas.create_line(x,start_y,x,end_y, \ fill="#%02x%02x%02x" % (int(255*r),int(255*g),int(255*b))) ########################################################################## # `FunctionControl` class. ########################################################################## class FunctionControl(tk.Frame): """Widget which displays a rectangular regions on which hue, sat, val or rgb values can be modified. An function control can have one or more attached color channels.""" # Radius around a control point center in which we'd still count a # click as "clicked the control point" control_pt_click_tolerance = 4 class Channel: def __init__(self, function_control, name, color_string, channel_index, channel_mode): """arguments documented in function body""" self.control = function_control #owning function control self.name = name #'r','g','b','h','s','v' or 'a' self.color_string = color_string # ^-- string containing a tk color value with which to # paint this channel self.index = channel_index #0: r or h, 1: g or s, 2: b or v, 3: a self.mode = channel_mode #'hsv' or 'rgb' def get_value(self, color): """Return height value of the current channel for the given color. Range: 0..1""" if ( self.mode == 'hsv' ): return color.get_hsva()[self.index] else: return color.get_rgba()[self.index] def get_value_index(self, color): """Return height index of channel value of Color. Range: [1..ControlHeight]""" return int( 1+(self.control.height-1)*(1.0 - self.get_value(color)) ) def get_index_value(self, y): """Get value in [0..1] of height index y""" return min(1.0, max(0.0, 1.0 - float(y-1)/(self.control.height-1))) def set_value( self, color, new_value_on_this_channel ): """Color will be modified: NewValue.. will be set to the color channel that ``*self`` represents.""" if ( self.mode == 'hsv' ): hsva = [color.get_hsva()[0], color.get_hsva()[1], color.get_hsva()[2], color.get_hsva()[3] ] hsva[self.index] = new_value_on_this_channel if ( hsva[0] >= 1.0 - 1e-5 ): # hack to make sure hue does not jump back to 0.0 # when it should be at 1.0 (rgb <-> hsv xform not # invertible there) hsva[0] = 1.0 - 1e-5 color.set_hsva(hsva[0],hsva[1],hsva[2],hsva[3]) else: rgba = [color.get_rgba()[0], color.get_rgba()[1], color.get_rgba()[2], color.get_rgba()[3] ] rgba[self.index] = new_value_on_this_channel color.set_rgba(rgba[0],rgba[1],rgba[2],rgba[3]) def set_value_index( self, color, y ): """Color will be modified: the value assigned to the height index y will be set to the color channel of Color that ``*self`` represents.""" self.set_value( color, self.get_index_value(y) ) def get_pos_index(self,f): """Return x-index for gradient position f in [0..1]""" return int(f*(self.control.width-1)) def get_index_pos(self,idx): """Return gradient position f in [0..1] for x-index Idx in [0..ControlWidth-1]""" return (1.0*idx)/(self.control.width-1) def paint(self, canvas): """Paint current channel into Canvas (a canvas of a function control object). Contents of the canvas are not deleted prior to painting, so more than one channel can be painted into the same canvas.""" table = self.control.table # only control points which are active for the current channel # are to be painted. filter them out. relevant_control_points = filter( \ lambda x: self.name in x.active_channels, table.control_points ) # lines between control points for k in range( len(relevant_control_points) - 1 ): cur_point = relevant_control_points[k] next_point = relevant_control_points[1+k] canvas.create_line( self.get_pos_index(cur_point.pos), self.get_value_index(cur_point.color), self.get_pos_index(next_point.pos), self.get_value_index(next_point.color), fill = self.color_string ) # control points themself. for control_point in relevant_control_points: x = self.get_pos_index( control_point.pos ) y = self.get_value_index( control_point.color ) radius = 3 canvas.create_rectangle( x - radius, y - radius, x + radius, y + radius, outline = '#000000' ) def __init__(self, master, gradient_table, color_space, width, height, on_table_changed = None ): tk.Frame.__init__(self, master, borderwidth=2, relief='groove') """Initialize a function control widget on tkframe master. input: OnTableChanged: Callback function taking a bool argument of meaning 'FinalUpdate'. FinalUpdate is true if a control point is dropped, created or removed and false if the update is due to a control point currently beeing dragged (but not yet dropped) ColorSpace: String which specifies the channels painted on this control. May be any combination of h,s,v,r,g,b,a in which each channel occurs only once.""" self.on_table_changed = on_table_changed self.table = gradient_table self.width = width self.height = height self.gradient_table = gradient_table self.canvas = tk.Canvas(self, background="white", \ width=self.width, height=self.height) self.canvas.pack() self.channels = [] # add the channels Channel = FunctionControl.Channel for c in color_space: if c == 'r': self.channels += [Channel(self, "r", "red", 0, 'rgb' )] elif c == 'g': self.channels += [Channel(self, "g", "green", 1, 'rgb' )] elif c == 'b': self.channels += [Channel(self, "b", "blue", 2, 'rgb' )] elif c == 'v': self.channels += [Channel(self, "v", "#7f7f7f", 2, 'hsv' )] elif c == 'h': self.channels += [Channel(self, "h", "#ff0000", 0, 'hsv' )] elif c == 's': self.channels += [Channel(self, "s", "#ffafaf", 1, 'hsv' )] elif c == 'a': self.channels += [Channel(self, "a", "#000000", 3, 'hsv' )] # generate a list of channels on which markers should # be bound if moved on the current channel. since we interpolate # the colors in hsv space, changing the r, g or b coordinates # explicitely means that h, s and v all have to be fixed. self.active_channels_string = "" for channel in self.channels: self.active_channels_string += channel.name if ( ( 'r' in color_space ) or ( 'g' in color_space ) or ( 'b' in color_space ) ): for c in "hsv": if ( not ( c in self.active_channels_string ) ): self.active_channels_string += c if ( color_space == 'a' ): # alpha channels actually independent of all other channels. self.active_channels_string = 'a' self.update() self.canvas.bind( "", self.on_left_button_up ) self.canvas.bind( "", self.on_left_button_down ) self.canvas.bind( "", self.on_right_button_up ) self.canvas.bind( "", self.on_right_button_down ) self.canvas.bind( "", self.on_mouse_move ) self.cur_drag = None #<- [channel,control_point] while something is dragged. def update(self): """Repaint the control.""" canvas = self.canvas # shortcut... canvas.delete(tk.ALL) for channel in self.channels: channel.paint(self.canvas) def find_control_point(self, x, y): """Check if a control point lies near (x,y) or near x if y == None. returns [channel, control point] if found, None otherwise""" for channel in self.channels: for control_point in self.table.control_points: # take into account only control points which are # actually active for the current channel if ( not ( channel.name in control_point.active_channels ) ): continue point_x = channel.get_pos_index( control_point.pos ) point_y = channel.get_value_index( control_point.color ) y_ = y if ( None == y_ ): y_ = point_y if ( (point_x-x)**2 + (point_y-y_)**2 <= self.control_pt_click_tolerance**2 ): return [channel, control_point] return None def on_left_button_down(self, event): self.cur_drag = self.find_control_point( event.x, event.y ) def on_left_button_up(self, event): if self.cur_drag: self.table_config_changed( final_update = True ) self.cur_drag = None def on_right_button_down(self, event): pass def table_config_changed(self, final_update): """Called internally in the control if the configuration of the attached gradient table has changed due to actions of this control. Forwards the update/change notice.""" self.table.update() if self.on_table_changed: self.on_table_changed(final_update) else: self.update() def on_right_button_up(self, event): # toggle control point. check if there is a control point # under the mouse. If yes, delete it, if not, create one # at that point. cur_control_point = self.find_control_point(event.x, None) if cur_control_point: # found a marker at the click position. delete it and return, # unless it is a fixed marker (at pos 0 or 1).. if ( cur_control_point[1].fixed ): # in this case do nothing. Fixed markers cannot be deleted. return self.table.control_points.remove(cur_control_point[1]) self.table_config_changed(final_update=True) else: # since there was no marker to remove at the point, we assume # that we should place one there new_control_point = ColorControlPoint(active_channels = self.active_channels_string) new_control_point.set_pos(self.channels[0].get_index_pos(event.x)) # set new control point color to the color currently present # at its designated position new_control_point.color = self.table.get_pos_color(new_control_point.pos) self.table.insert_control_point( new_control_point ) self.table_config_changed( final_update = True ) def on_mouse_move(self, event): # currently dragging a control point? if self.cur_drag: channel = self.cur_drag[0] point = self.cur_drag[1] if ( not point.fixed ): point.set_pos( channel.get_index_pos(event.x) ) point.activate_channels( self.active_channels_string ) self.table.sort_control_points() channel.set_value_index( point.color, event.y ) self.table_config_changed( final_update = False ) ########################################################################## # `GradientEditor` class. ########################################################################## class GradientEditor(tk.Toplevel): """The gradient editor window, i.e. the thing that contains the gradient display, the function controls and the buttons.""" def __init__(self, master, vtk_table, on_change_color_table = None): """Initialize the gradient editor window. Parameters ---------- master Owning widget, for example a tk root object. VtkTable Instance of vtkLookupTable, designating the table which is to be edited. OnChangeColorTable Callback function taking no arguments. Called when the color table was changed and rendering is requested.""" # Inner dimensions of the color control gui-elements in pixels. gradient_preview_width = 300 gradient_preview_height = 50 channel_function_width = gradient_preview_width channel_function_height = 80 tk.Toplevel.__init__(self, master) self.title("Color Gradient Editor") self.minsize( gradient_preview_width+4, gradient_preview_height + 5 * \ channel_function_height + 50 ) self.gradient_table = GradientTable(gradient_preview_width) self.vtk_color_table = vtk_table # create controls. self.gradient_control = GradientControl(self, self.gradient_table, gradient_preview_width, gradient_preview_height ) self.gradient_control.grid(row=0,column=1,sticky="we") def on_gradient_table_changed( final_update ): # update all function controls. self.function_control_rgb.update() for control in self.function_controls_hsv: control.update() # repaint the gradient display or the external windows only # when the instant*** options are set or when the update was final. if final_update or ( 1 == self.show_instant_gradients.get() ): self.gradient_control.update() if final_update or ( 1 == self.show_instant_feedback.get() ): self.gradient_table.store_to_vtk_lookup_table( self.vtk_color_table ) on_change_color_table() self.on_gradient_table_changed = on_gradient_table_changed self.function_control_rgb = FunctionControl(self, self.gradient_table, "rgb", channel_function_width, channel_function_height, on_gradient_table_changed) label = tk.Label( self, text = "rgb" ) label.grid(row=1, column=0) self.function_control_rgb.grid(row=1,column=1,sticky="we") self.function_controls_hsv = [] for it in [("hue",2), ("sat",3), ("val",4), ("alp", 5) ]: control = FunctionControl(self, self.gradient_table, it[0][0], channel_function_width, channel_function_height, on_gradient_table_changed ) control.grid(row=it[1],column=1,sticky="we") self.function_controls_hsv.append(control) label = tk.Label( self, text = it[0] ) label.grid(row=it[1], column=0) # buttons and the instruction label get into an own subframe for # easier control. button_frame = tk.Frame(self) button_frame.grid(row=6,column=0,columnspan=2) ok_button = tk.Button(button_frame, text="ok", command=self.ok) ok_button.grid(row=0,column=1) #CancelButton = tk.Button(ButtonFrame, text="cancel", command=self.Cancel) #CancelButton.grid(row=0,column=2) spacer = tk.Frame(button_frame, width=10 ) spacer.grid(row=0,column=3) save_button = tk.Button(button_frame, text="save", command=self.save_gradient) save_button.grid(row=0,column=4) load_button = tk.Button(button_frame, text="load", command=self.load_gradient) load_button.grid(row=0,column=5) spacer = tk.Frame(button_frame, width=10 ) spacer.grid(row=0,column=6) label = tk.Label(button_frame,text="instant:") label.grid(row=0,column=7) # these two buttons control whether gradient and render target # updates are executed during movement of control points or # only at the end of such changes. self.show_instant_gradients = tk.IntVar() self.show_instant_gradients.set(1) # enable instant gradients by default self.show_instant_feedback = tk.IntVar() self.show_instant_feedback.set(0) # disable instant feedback by default instant_gradient_button = tk.Checkbutton(button_frame, text="grad") instant_gradient_button.grid(row=0,column=8) instant_gradient_button.configure(variable=self.show_instant_gradients) instant_feedback_button = tk.Checkbutton(button_frame, text="feed") instant_feedback_button.grid(row=0,column=9) instant_feedback_button.configure(variable=self.show_instant_feedback) instruction_label = tk.Label(button_frame, text="left button: move point; right click: toggle point") instruction_label.grid(column=0,columnspan=9,row=1) # insert a ratio button which decides whether the controls for nonlinear # scaling of the gradient are shown and activated. self.nonlinear_scaling_enabled = tk.IntVar() self.nonlinear_scaling_enabled.set(0) nonlinear_enabled_button = tk.Checkbutton(button_frame, text="nonlin") nonlinear_enabled_button.grid(column=9,row=1) nonlinear_enabled_button.configure(variable=self.nonlinear_scaling_enabled, command=self.nonlinear_scaling_option_changed) # the controls for the nonlinear scaling also get into an own frame. # this one can be shown or hidden when the "nonlin"-button is pressed nonlin_frame = tk.Frame(self) self.nonlin_frame = nonlin_frame label = tk.Label(nonlin_frame, text="f(x) =") label.grid(row=0, column=0) self.nonlinear_function_string = tk.StringVar() self.nonlinear_function_string.set( "x**(4*a)" ) function_edit = tk.Entry(nonlin_frame, width=35, textvariable=self.nonlinear_function_string) function_edit.bind("", self.nonlinear_function_string_changed ) function_edit.grid(row=0, column=1) label = tk.Label(nonlin_frame, text="param a:") label.grid(row=1, column=0) self.parameter_scale = tk.Scale(nonlin_frame, from_=0.0, to=1.0, resolution=0.001, length=250, orient="horizontal") self.parameter_scale.bind("", lambda event: self.nonlinear_parameter_scale_changed(final_update=True)) self.parameter_scale.bind("", lambda event:self.nonlinear_parameter_scale_changed(final_update=False)) self.parameter_scale.set(0.5) self.parameter_scale.grid(row=1, column=1) label = tk.Label(nonlin_frame, text= \ "f(x) should map [0..1] to [0..1]. It rescales the gradient.") label.grid(column=0,columnspan=2,row=2) # finally, write the current gradient out into main program on_gradient_table_changed(final_update = True) def nonlinear_scaling_option_changed(self): """called when the 'nonlin'-button is pressed to toggle if nonlinear- scaling is activated and the corresponding controls are shown""" if ( 1 == self.nonlinear_scaling_enabled.get() ): # activate the nonlinear scaling controls self.nonlin_frame.grid(row=7,column=0,columnspan=2) self.nonlinear_parameter_scale_changed(final_update=False) self.nonlinear_function_string_changed(None) else: # disable the nonlinear scaling controls (and the scaling) self.nonlin_frame.pack(side=tk.LEFT, anchor=tk.NW) self.nonlin_frame.pack_forget() self.gradient_table.set_scaling_function("") self.on_gradient_table_changed(final_update=True) def nonlinear_parameter_scale_changed(self,final_update): """Event Handler for the nonlinear-parameter scaling bar. FinalUpdate is true on ButtonRelease and False on Motion""" self.gradient_table.set_scaling_function_parameter(self.parameter_scale.get()) self.on_gradient_table_changed(final_update = final_update) def nonlinear_function_string_changed(self,event): """Invoked when Return is pressed in the nonlinear-function edit""" self.gradient_table.set_scaling_function(self.nonlinear_function_string.get()) self.on_gradient_table_changed(final_update = True) def ok(self): self.destroy() def save_gradient(self): filetypes = [("Gradient Files","*.grad"),("All Files","*")] file_name = tkFileDialog.asksaveasfilename(defaultextension=".grad", filetypes=filetypes) if file_name: # there is probably a way to find out which file type the user # actually selected. But since I don't know it and also don't really # know how to find it out, i rely on this error prone method... if ( ".lut" == file_name[len(file_name)-4:] ): self.gradient_table.save(file_name) self.gradient_table.save(file_name) def load_gradient(self): filetypes = [("Gradient Files","*.grad"), ("All Files","*")] file_name = tkFileDialog.askopenfilename(defaultextension=".grad", filetypes=filetypes) if file_name: self.gradient_table.load(file_name) self.on_gradient_table_changed(final_update = True) if self.gradient_table.scaling_function: self.parameter_scale.set(self.gradient_table.scaling_function_parameter) self.nonlinear_function_string.set(self.gradient_table.scaling_function_string) self.nonlinear_scaling_enabled.set(1) self.nonlinear_scaling_option_changed() else: self.nonlinear_scaling_enabled.set(0) self.nonlinear_scaling_option_changed() if __name__ == "__main__": # prepare a vtk window with an actor for visible feedback. Don't be # be scared, the actual gradient editor code is only 3 lines long, # the rest is setup of the scene. from tvtk.api import tvtk import vtk from vtk.tk import vtkTkRenderWidget from math import cos root = tk.Tk() root.minsize(520,520) render_frame = tk.Frame(root) render_frame.pack() render_widget = vtkTkRenderWidget.vtkTkRenderWidget(render_frame, width=512, height=512 ) render_widget.pack(side=tk.BOTTOM,expand='true',fill='both') render_window = render_widget.GetRenderWindow() renderer = vtk.vtkRenderer() renderer.SetBackground(0.2,0.2,0.4) render_window.AddRenderer(renderer) image_data = vtk.vtkImageData() N = 72 image_data.SetDimensions(N,N,1) try: method = image_data.SetScalarComponentFromFloat except AttributeError: method = image_data.SetScalarComponentFromDouble for i in range(N): for j in range(N): a = float(i)/N b = float(j)/N v = 0.5 + 0.5*cos(13*a)*cos(8*b+3*a*a) v = v**2 method(i,j,0,0,v) geometry_filter = vtk.vtkImageDataGeometryFilter() geometry_filter.SetInput(image_data) warp = vtk.vtkWarpScalar() warp.SetInput(geometry_filter.GetOutput()) warp.SetScaleFactor(8.1) normal_filter = vtk.vtkPolyDataNormals() normal_filter.SetInput(warp.GetOutput()) data_mapper = vtk.vtkDataSetMapper() data_mapper.SetInput(normal_filter.GetOutput()) data_actor = vtk.vtkActor() data_actor.SetMapper(data_mapper) renderer.AddActor(data_actor) table = vtk.vtkLookupTable() data_mapper.SetLookupTable(table) # the actual gradient editor code. def on_color_table_changed(): render_window.Render() # Gradient editor only works with tvtk objects, so convert the lut # to a tvtk version. tvtk_table = tvtk.to_tvtk(table) editor = GradientEditor(root, tvtk_table, on_color_table_changed) root.mainloop() mayavi-4.1.0/tvtk/util/ctf.py0000644000175100001440000002321711674464502017074 0ustar ischnellusers00000000000000"""Color transfer function related code. """ # Author: Prabhu Ramachandran # Copyright (c) 2006-2009, Enthought, Inc. # License: BSD Style. # Enthought library imports. from traits.api import List from tvtk.api import tvtk ########################################################################## # Color transfer function related utility code from MayaVi1. ########################################################################## def _err_msg(obj, cls_name): return '%s %s does not have either a "nodes" attribute or a '\ '"get_node_value" method'%(cls_name, str(obj)) def save_ctfs(volume_property): """Given a `tvtk.VolumeProperty` it saves the state of the RGB and opacity CTF to a dictionary and returns that. The 'rgb' key stores a list of (x, r, g, b) and the 'alpha' a list of (x, a) values. """ vp = volume_property ctf = vp.rgb_transfer_function otf = vp.get_scalar_opacity() s1, s2 = ctf.range # The RGB values. nc = ctf.size rgb = [] if hasattr(ctf, 'nodes'): for i in range(nc): x = ctf.nodes[i] r, g, b = ctf.get_color(x) rgb.append([x, r, g, b]) elif hasattr(ctf, 'get_node_value'): val = [0]*6 for i in range(nc): ctf.get_node_value(i, val) rgb.append(val[:4]) else: raise TypeError, _err_msg(ctf, 'ColorTransferFunction') # The Alpha values. na = otf.size a = [] if hasattr(otf, 'nodes'): for i in range(na): x = otf.nodes[i] val = otf.get_value(x) a.append([x, val]) elif hasattr(otf, 'get_node_value'): val = [0]*4 for i in range(na): otf.get_node_value(i, val) a.append(val[:2]) else: raise TypeError, _err_msg(otf, 'PiecewiseFunction') return {'range': (s1, s2), 'rgb':rgb, 'alpha':a} def load_ctfs(saved_data, volume_property): """ Given the saved data produced via `save_ctfs`, this sets the state of the passed volume_property appropriately. It returns the new color transfer function and piecewise function. """ rgb = saved_data['rgb'] a = saved_data['alpha'] # The new_ctf/otf shenanigans are necessary because if the ctf/otf # go out of scope we loose the node information. This is because # the tvtk object is really a dynamically generated wrapper. # First do the RGB values ... new_ctf = True ctf = volume_property.rgb_transfer_function if isinstance(ctf, ColorTransferFunction): new_ctf = False ctf.remove_all_points() else: ctf = ColorTransferFunction() nc = len(rgb) for i in range(nc): ctf.add_rgb_point(rgb[i][0], *(rgb[i][1:])) if new_ctf: volume_property.set_color(ctf) try: ctf.range = saved_data['range'] except Exception: # VTK versions < 5.2 don't seem to need this. pass # and then the alpha values. na = len(a) new_otf = True otf = volume_property.get_scalar_opacity() if isinstance(otf, PiecewiseFunction): new_otf = False otf.remove_all_points() else: otf = PiecewiseFunction() for i in range(na): otf.add_point(a[i][0], a[i][1]) if new_otf: volume_property.set_scalar_opacity(otf) return ctf, otf def rescale_ctfs(volume_property, new_range): """ Given the volume_property with a new_range for the data while using the same transfer functions, this function rescales the CTF's so that everything works OK. It returns the CTF and OTF. """ ctf = volume_property.rgb_transfer_function otf = volume_property.get_scalar_opacity() old_range = ctf.range def _rescale_value(x, old, new): nx = (x - old[0])/(old[1] - old[0]) return new[0] + nx*(new[1] - new[0]) if new_range[0] != old_range[0] and new_range[1] != old_range[1]: s_d = save_ctfs(volume_property) # Set the new range making sure that they are in the right order. s1, s2 = new_range if s1 > s2: s_d['range'] = (s2, s1) else: s_d['range'] = (s1, s2) # Rescale the RGB values. rgb = s_d['rgb'] for v in rgb: v[0] = _rescale_value(v[0], old_range, new_range) # Rescale the alpha values. alpha = s_d['alpha'] for v in alpha: v[0] = _rescale_value(v[0], old_range, new_range) # Now load the rescaled values. ctf, otf = load_ctfs(s_d, volume_property) return ctf, otf def set_lut(lut, volume_property): """Given a `tvtk.LookupTable` and a `tvtk.VolumeProperty` it saves the state of the RGB and opacity CTF from the volume property to the LUT. The number of colors to use is obtained from the LUT and not the CTF. """ vp = volume_property ctf = vp.rgb_transfer_function otf = vp.get_scalar_opacity() s1, s2 = ctf.range nc = lut.number_of_colors ds = float(s2-s1)/(nc - 1) for i in range(nc): r, g, b = ctf.get_color(s1 + i*ds) a = otf.get_value(s1 + i*ds) lut.set_table_value(i, r, g, b, a) def set_ctf_from_lut(lut, volume_property): """Given a `tvtk.LookupTable` and a `tvtk.VolumeProperty` it loads the state of the RGB and opacity CTF from the lookup table to the CTF. The CTF range is obtained from the volume property and the number of colors to use is obtained from the LUT. """ vp = volume_property ctf = vp.rgb_transfer_function s1, s2 = ctf.range nc = lut.number_of_colors ds = float(s2-s1)/(nc - 1) ctf = ColorTransferFunction() otf = PiecewiseFunction() for i in range(nc): v = s1 + i*ds r, g, b, a = lut.get_table_value(i) ctf.add_rgb_point(v, r, g, b) otf.add_point(v, a) volume_property.set_color(ctf) volume_property.set_scalar_opacity(otf) ########################################################################## # `ColorTransferFunction` class. ########################################################################## class ColorTransferFunction(tvtk.ColorTransferFunction): """Overrides a few important methods that allow us to glean node information. This is useful in cases where the super class does not have methods to get the nodes. """ # Stores the nodes used by the CTF. Note that this is not a # proper trait and modifying this will not change the underlying # VTK object. nodes = List def add_rgb_point(self, *args): """V.add_rgb_point(float, float, float, float) -> int V.add_rgb_point(float, float, float, float, float, float) -> int Add/Remove a point to/from the function defined in RGB or HSV Return the index of the point (0 based), or -1 on error. Wrapper around parent class functionality to store node information. """ ret = super(ColorTransferFunction, self).add_rgb_point(*args) self.nodes.append(args[0]) self.nodes.sort() return ret def add_hsv_point(self, *args): """V.add_hsv_point(float, float, float, float) -> int V.add_hsv_point(float, float, float, float, float, float) -> int Add/Remove a point to/from the function defined in RGB or HSV Return the index of the point (0 based), or -1 on error. Wrapper around parent class functionality to store node information. """ ret = super(ColorTransferFunction, self).add_hsv_point(*args) self.nodes.append(args[0]) self.nodes.sort() return ret def remove_all_points(self): """Remove all the points. """ super(ColorTransferFunction, self).remove_all_points() self.nodes = [] ########################################################################## # `PiecewiseFunction` class. ########################################################################## class PiecewiseFunction(tvtk.PiecewiseFunction): """Overrides a few important methods that allow us to glean node information. This is useful in cases where the super class does not have methods to get the nodes. """ # Stores the nodes used by the function. Note that this is not a # proper trait and modifying this will not change the underlying # VTK object. nodes = List def initialize(self): """V.initialize() Clears out the current function. A newly created PiecewiseFunction is alreay initialized, so there is no need to call this method which in turn simply calls remove_all_points() """ super(PiecewiseFunction, self).initialize() self.nodes = [] def add_point(self, x, val): """V.add_point(float, float) -> int V.add_point(float, float, float, float) -> int Add/Remove points to/from the function. If a duplicate point is added then the function value is changed at that location. Return the index of the point (0 based), or -1 on error. """ ret = super(PiecewiseFunction, self).add_point(x, val) self.nodes.append(x) self.nodes.sort() return ret def remove_point(self, x): """V.remove_point(float) -> int Add/Remove points to/from the function. If a duplicate point is added then the function value is changed at that location. Return the index of the point (0 based), or -1 on error. """ ret = super(PiecewiseFunction, self).remove_point(x) self.nodes.remove(x) self.nodes.sort() return ret def remove_all_points(self): """Remove all the points. """ super(PiecewiseFunction, self).remove_all_points() self.nodes = [] mayavi-4.1.0/tvtk/util/wx_gradient_editor.py0000644000175100001440000010014011674464502022170 0ustar ischnellusers00000000000000""" A wxPython based color gradient editor for vtkLookupTables and color transfer functions. This code is distributed under the conditions of the BSD license. Based on a Tk version of this widget by Gerald Knizia Ported to wxPython by Pete Schmitt Cleaned up and enhanced for use with MayaVi2 by Prabhu Ramachandran Copyright (c) 2005-2006, Gerald Knizia, Pete Schmitt and Prabhu Ramachandran """ ########################################################################## # TODO: # # * Support for resizing the widget. # ########################################################################## # Third-party imports import wx # Enthought library imports from tvtk.api import tvtk # Local imports from gradient_editor import GradientTable, ColorControlPoint ########################################################################## # `wxGradientControl` class. ########################################################################## class wxGradientControl(wx.Panel): """Widget which displays the gradient represented by an GradientTable object (and does nothing beyond that)""" def __init__(self, masterPanel, gradient_table, width, height ): """master: panel in which to place the control. GradientTable is the Table to which to attach.""" wx.Panel.__init__(self, masterPanel, size=wx.Size(width, height), style=wx.RAISED_BORDER, name="Colormap Panel") self.SetBackgroundColour(wx.Colour(255,255,255)) self.width = width self.height = height self.gradient_table = gradient_table assert( gradient_table.size == width ) # ^- currently only able to use gradient tables in the same size as the canvas width # bind paint event to redraw when resizing/creating window... wx.EVT_PAINT(self, self.OnPaint) def OnPaint(self, event): """ Paint event handler for when the window is resized and whatnot.""" dc = wx.PaintDC(self) self.update() def update(self): """Repaint the control.""" #self.canvas.delete(tk.ALL) # clears all lines contained. dc = wx.ClientDC(self) dc.SetBackground(wx.Brush(wx.Colour(0,0,0), wx.SOLID)) dc.Clear() width, height = self.GetSize() # From the old tk GradientEditor: # a look around the web (http://wiki.tcl.tk/11868) told me that # using the PhotoImage tk-control would not be a good idea and # that line objects work faster. While I doubt this is an optimal # solution it currently works fast enought. # So... let's do the same thing for the new and improved (?) wxPython GradientEditor. xform = self.gradient_table.scaling_function start_y = 0 end_y = height if xform: # if a scaling transformation is provided, paint the original # gradient under the scaled gradient. start_y = height/2 # paint the original gradient as it stands in the table. dc.BeginDrawing() for x in range(width): (r,g,b,a) = self.gradient_table.get_pos_rgba_color_lerped(float(x)/(width-1)) dc.SetPen(wx.Pen(wx.Colour(int(255*r),int(255*g),int(255*b)))) dc.SetBrush(wx.Brush((int(255*r),int(255*g),int(255*b)), wx.SOLID)) dc.DrawLine(x, start_y, x, end_y) if xform: # paint the scaled gradient below end_y = start_y start_y = 0 for x in range(width): f = float(x)/(width-1) (r,g,b,a) = self.gradient_table.get_pos_rgba_color_lerped(xform(f)) dc.SetBrush(wx.Brush((int(255*r),int(255*g),int(255*b)), wx.SOLID)) dc.DrawLine(x, start_y, x, end_y) dc.EndDrawing() ########################################################################## # `Channel` class. ########################################################################## class Channel: def __init__(self, function_control, name, rgb_color, channel_index, channel_mode): """arguments documented in function body""" self.control = function_control #owning function control self.name = name #'r','g','b','h','s','v' or 'a' self.rgb_color = rgb_color # ^-- string containing a tk color value with which to # paint this channel self.index = channel_index #0: r or h, 1: g or s, 2: b or v, 3: a self.mode = channel_mode #'hsv' or 'rgb' def get_value(self, color): """Return height value of the current channel for the given color. Range: 0..1""" if ( self.mode == 'hsv' ): return color.get_hsva()[self.index] else: return color.get_rgba()[self.index] def get_value_index(self, color): """Return height index of channel value of Color. Range: [1..ControlHeight]""" return int( 1+(self.control.height-1)*(1.0 - self.get_value(color)) ) def get_index_value(self, y): """Get value in [0..1] of height index y""" return min(1.0, max(0.0, 1.0 - float(y)/(self.control.height-1))) def set_value( self, color, new_value_on_this_channel ): """Color will be modified: NewValue.. will be set to the color channel that ``*self`` represents.""" if ( self.mode == 'hsv' ): hsva = [color.get_hsva()[0], color.get_hsva()[1], color.get_hsva()[2], color.get_hsva()[3] ] hsva[self.index] = new_value_on_this_channel if ( hsva[0] >= 1.0 - 1e-5 ): # hack to make sure hue does not jump back to 0.0 # when it should be at 1.0 (rgb <-> hsv xform not # invertible there) hsva[0] = 1.0 - 1e-5 color.set_hsva(hsva[0],hsva[1],hsva[2],hsva[3]) else: rgba = [color.get_rgba()[0], color.get_rgba()[1], color.get_rgba()[2], color.get_rgba()[3] ] rgba[self.index] = new_value_on_this_channel color.set_rgba(rgba[0],rgba[1],rgba[2],rgba[3]) def set_value_index( self, color, y ): """Color will be modified: the value assigned to the height index y will be set to the color channel of Color ``*self`` represents.""" self.set_value( color, self.get_index_value(y) ) def get_pos_index(self,f): """Return x-index for gradient position f in [0..1]""" return int(f*(self.control.width-1)) def get_index_pos(self,idx): """Return gradient position f in [0..1] for x-index Idx in [0..ControlWidth-1]""" return (1.0*idx)/(self.control.width-1) def paint(self, deviceContext): """Paint current channel into Canvas (a canvas of a function control object). Contents of the canvas are not deleted prior to painting, so more than one channel can be painted into the same canvas.""" dc = deviceContext table = self.control.table # only control points which are active for the current channel # are to be painted. filter them out. relevant_control_points = filter( \ lambda x: self.name in x.active_channels, table.control_points ) dc.BeginDrawing() # lines between control points dc.SetPen(wx.Pen(self.rgb_color,1)) #dc.SetBrush(wx.Brush((255,255,255), wx.SOLID)) dc.SetBrush(wx.Brush((255,255,255), wx.SOLID)) for k in range( len(relevant_control_points) - 1 ): cur_point = relevant_control_points[k] next_point = relevant_control_points[1+k] dc.DrawLine( self.get_pos_index(cur_point.pos), self.get_value_index(cur_point.color), self.get_pos_index(next_point.pos), self.get_value_index(next_point.color)) # control points themself. dc.SetPen(wx.Pen("BLACK",1)) dc.SetBrush(wx.Brush((255,255,255), wx.SOLID)) for control_point in relevant_control_points: x = self.get_pos_index( control_point.pos ) y = self.get_value_index( control_point.color ) radius=6 #print(x,y) dc.DrawRectangle(x-(radius/2.0), y-(radius/2.0),radius,radius) dc.DrawRectangle(100,80,6,6) dc.EndDrawing() ########################################################################## # `wxFunctionControl` class. ########################################################################## class wxFunctionControl(wx.Panel): """Widget which displays a rectangular regions on which hue, sat, val or rgb values can be modified. An function control can have one or more attached color channels.""" # Radius around a control point center in which we'd still count a # click as "clicked the control point" control_pt_click_tolerance = 4 def __init__(self, master, gradient_table, color_space, width, height): """Initialize a function control widget on tkframe master. Parameters: ----------- master: The master widget. Note that this widget *must* have the methods specified in the `AbstractGradEditorWidget` interface. on_table_changed: Callback function taking a bool argument of meaning 'FinalUpdate'. FinalUpdate is true if a control point is dropped, created or removed and false if the update is due to a control point currently beeing dragged (but not yet dropped) color_space: String which specifies the channels painted on this control. May be any combination of h,s,v,r,g,b,a in which each channel occurs only once. set_status_text: a callback used to set the status text when using the editor. """ self.text_map = {'r': 'RED', 'g': 'GREEN', 'b': 'BLUE', 'h': 'HUE', 's': 'SATURATION', 'v': 'VALUE', 'a': 'ALPHA'} self.master = master self.table = gradient_table self.gradient_table = gradient_table self.width = width self.height = height wx.Panel.__init__(self, master, size=wx.Size(width, height), name="RGBHSVA Editor") # You can pass style=wx.RAISED_BORDER into wx.Panel() to make a border, but.... # then a few pixels on each side are chopped off... #self.SetBackgroundColour(wx.Colour(255,0,0)) self.channels = [] # add the channels for c in color_space: if c == 'r': self.channels += [Channel(self, "r", (255,0,0), 0, 'rgb' )] elif c == 'g': self.channels += [Channel(self, "g", (0,255,0), 1, 'rgb' )] elif c == 'b': self.channels += [Channel(self, "b", (0,0,255), 2, 'rgb' )] elif c == 'h': self.channels += [Channel(self, "h", (255,0,0), 0, 'hsv' )] elif c == 's': self.channels += [Channel(self, "s", (0,255,0), 1, 'hsv' )] elif c == 'v': self.channels += [Channel(self, "v", (0,0,255), 2, 'hsv' )] elif c == 'a': self.channels += [Channel(self, "a", (0,0,0), 3, 'hsv' )] # generate a list of channels on which markers should # be bound if moved on the current channel. since we interpolate # the colors in hsv space, changing the r, g or b coordinates # explicitely means that h, s and v all have to be fixed. self.active_channels_string = "" for channel in self.channels: self.active_channels_string += channel.name if ( ( 'r' in color_space ) or ( 'g' in color_space ) or ( 'b' in color_space ) ): for c in "hsv": if ( not ( c in self.active_channels_string ) ): self.active_channels_string += c if ( color_space == 'a' ): # alpha channels actually independent of all other channels. self.active_channels_string = 'a' self.update() wx.EVT_LEFT_DOWN(self, self.on_left_button_down) wx.EVT_LEFT_UP(self, self.on_left_button_up) wx.EVT_RIGHT_DOWN(self, self.on_right_button_down) wx.EVT_RIGHT_UP(self, self.on_right_button_up) wx.EVT_MOTION(self, self.on_mouse_move) wx.EVT_PAINT(self, self.on_paint) wx.EVT_LEAVE_WINDOW(self, self.on_leave_window) # need to set to "None" initially or event handlers get confused. self.cur_drag = None #<- [channel,control_point] while something is dragged. def find_control_point(self, x, y): """Check if a control point lies near (x,y) or near x if y == None. returns [channel, control point] if found, None otherwise""" for channel in self.channels: for control_point in self.table.control_points: # take into account only control points which are # actually active for the current channel if ( not ( channel.name in control_point.active_channels ) ): continue point_x = channel.get_pos_index( control_point.pos ) point_y = channel.get_value_index( control_point.color ) y_ = y if ( None == y_ ): y_ = point_y if ( (point_x-x)**2 + (point_y-y_)**2 <= self.control_pt_click_tolerance**2 ): return [channel, control_point] return None def table_config_changed(self, final_update): """Called internally in the control if the configuration of the attached gradient table has changed due to actions of this control. Forwards the update/change notice.""" self.table.update() self.master.on_gradient_table_changed(final_update) def update(self, event = None): """Repaint the control.""" dc = wx.ClientDC(self) #if we have a custom background, we *must* set the background brush *BEFORE* clearing... dc.SetBackground(wx.Brush(wx.Colour(255,255,255), wx.SOLID)) dc.Clear() for channel in self.channels: channel.paint(dc) def on_paint(self, event=None): dc = wx.PaintDC(self) self.update() def on_left_button_down(self, event): self.cur_drag = self.find_control_point( event.GetX(), event.GetY() ) def on_left_button_up(self, event): if self.cur_drag: self.table_config_changed( final_update = True ) self.cur_drag = None def on_leave_window(self, event): self.on_left_button_up(event) def on_right_button_down(self, event): pass def on_right_button_up(self, event): # toggle control point. check if there is a control point # under the mouse. If yes, delete it, if not, create one # at that point. cur_control_point = self.find_control_point(event.GetX(), None) if cur_control_point: # found a marker at the click position. delete it and return, # unless it is a fixed marker (at pos 0 or 1).. if ( cur_control_point[1].fixed ): # in this case do nothing. Fixed markers cannot be deleted. return self.table.control_points.remove(cur_control_point[1]) self.table_config_changed(final_update=True) else: # since there was no marker to remove at the point, we assume # that we should place one there new_control_point = ColorControlPoint(active_channels = self.active_channels_string) new_control_point.set_pos(self.channels[0].get_index_pos(event.GetX())) # set new control point color to the color currently present # at its designated position new_control_point.color = self.table.get_pos_color(new_control_point.pos) self.table.insert_control_point( new_control_point ) self.table_config_changed( final_update = True ) def on_mouse_move(self, event): # currently dragging a control point? channel = None point = None if self.cur_drag: channel = self.cur_drag[0] point = self.cur_drag[1] if ( not point.fixed ): point.set_pos( channel.get_index_pos(event.GetX()) ) point.activate_channels( self.active_channels_string ) self.table.sort_control_points() channel.set_value_index( point.color, event.GetY() ) self.table_config_changed( final_update = False ) screenX = event.GetX() screenY = event.GetY() width, height = self.GetSize() master = self.master s1, s2 = master.get_table_range() if channel is not None: name = self.text_map[channel.name] pos = s1 + (s2 - s1)*point.pos val = channel.get_value(point.color) txt = '%s: (%.3f, %.3f)'%(name, pos, val) else: x = s1 + (s2 - s1)*float(screenX)/(width-1) y = 1.0 - float(screenY)/(height-1) txt = "position: (%.3f, %.3f)"%(x, y) self.master.set_status_text(txt) ########################################################################## # `AbstractGradEditor` interface. ########################################################################## class AbstractGradEditor(object): def on_gradient_table_changed(self, final_update): """ Update the gradient table and vtk lookuptable.""" raise NotImplementedError def set_status_text(self, msg): """Set the status on the status widget if you have one.""" raise NotImplementedError def get_table_range(self): """Return the CTF or LUT's scalar range.""" raise NotImplementedError ########################################################################## # `wxGradientEditorWidget` class. ########################################################################## class wxGradientEditorWidget(wx.Panel, AbstractGradEditor): """A Gradient Editor widget that can be used anywhere. """ def __init__(self, master, vtk_table, on_change_color_table=None, colors=None): """ Parameters: ----------- vtk_table : the `tvtk.LookupTable` or `tvtk.VolumeProperty` object to set. on_change_color_table : A callback called when the color table changes. colors : list of 'rgb', 'hsv', 'h', 's', 'v', 'a' (Default : ['rgb', 'hsv', 'a']) 'rgb' creates one panel to edit Red, Green and Blue colors. 'hsv' creates one panel to edit Hue, Saturation and Value. 'h', 's', 'v', 'r', 'g', 'b', 'a' separately specified creates different panels for each. """ wx.Panel.__init__(self, master) if colors is None: colors = ['rgb', 'hsv', 'a'] gradient_preview_width = 300 gradient_preview_height = 50 channel_function_width = gradient_preview_width channel_function_height = 80 self.gradient_table = GradientTable(gradient_preview_width) self.vtk_color_table = vtk_table if isinstance(vtk_table, tvtk.LookupTable): self.vtk_table_is_lut = True else: # This is a tvtk.VolumeProperty self.vtk_table_is_lut = False # Initialize the editor with the volume property. self.gradient_table.load_from_vtk_volume_prop(vtk_table) self.on_change_color_table = on_change_color_table # set up all the panels in a gridbagsizer (i.e. a big grid) # 6x2 size: 6 rows, 2 columns... sizer = wx.GridBagSizer(2, 2) # "Gradient Viewer" panel, in position (0,1) for sizer self.gradient_control = wxGradientControl(self, self.gradient_table, gradient_preview_width, gradient_preview_height) tt = wx.ToolTip('Right click for menu') self.gradient_control.Bind(wx.EVT_CONTEXT_MENU, self.on_gradient_menu) self.gradient_control.SetToolTip(tt) sizer.Add(self.gradient_control, pos=(0,1)) # Add the function controls: function_controls = [] self.function_controls = function_controls tooltip_text = 'Left click: move control points\n'\ 'Right click: add/remove control points' editor_data = {'rgb': ('', 'RGB'), 'hsv': ('Hue: Red; Saturation: Green; '\ 'Value: Blue\n', 'HSV' ), 'h': ('', 'HUE'), 's': ('', 'SAT'), 'v': ('', 'VAL'), 'r': ('', 'RED'), 'g': ('', 'GREEN'), 'b': ('', 'BLUE'), 'a': ('', 'ALPHA'), } row = 1 for color in colors: data = editor_data[color] control = wxFunctionControl(self, self.gradient_table, color, channel_function_width, channel_function_height) txt = data[0] + tooltip_text control.SetToolTip(wx.ToolTip(txt)) # Add name of editor (to left side of editor) sizer.Add(wx.StaticText(self, -1, data[1]), pos=(row, 0), flag=wx.ALIGN_CENTER|wx.ALL) # Add the "RGB" control point editor sizer.Add(control, pos=(row, 1)) function_controls.append(control) row += 1 # The status text. self.text = wx.StaticText(self, -1, 'status') sizer.Add(self.text, (row,0), (row,2)) row += 1 # set the appropriate sizer. sizer.SetSizeHints(self) self.SetSizerAndFit(sizer) ###################################################################### # `wxGradientEditorWidget` interface. ###################################################################### def set_status_text(self, msg): t = self.text t.SetLabel(msg) t.Refresh() t.Update() def on_gradient_table_changed(self, final_update ): """ Update the gradient table and vtk lookuptable...""" # update all function controls. for control in self.function_controls: control.update() # repaint the gradient display or the external windows only # when the instant*** options are set or when the update was final. #if final_update or ( 1 == self.show_instant_gradients.get() ): if True: self.gradient_control.update() #if final_update or ( 1 == self.show_instant_feedback.get() ): if final_update: vtk_table = self.vtk_color_table if self.vtk_table_is_lut: self.gradient_table.store_to_vtk_lookup_table(vtk_table) else: rng = self.get_table_range() self.gradient_table.store_to_vtk_volume_prop(vtk_table, rng) cb = self.on_change_color_table if cb is not None: cb() def get_table_range(self): vtk_table = self.vtk_color_table if self.vtk_table_is_lut: return vtk_table.table_range else: return vtk_table.get_scalar_opacity().range def load(self, file_name): """Set the state of the color table using the given file. """ if len(file_name) == 0: return self.gradient_table.load(file_name) self.on_gradient_table_changed(final_update = True) def save(self, file_name): """Store the color table to the given file. This actually generates 3 files, a '.grad', a '.lut' file and a '.jpg' file. The .lut file can be used to setup a lookup table. The .grad file is used to set the state of the gradient table and the JPG file is an image of the how the lut will look. """ if len(file_name) == 0: return self.gradient_table.save(file_name) ###################################################################### # wxPython event methods. ###################################################################### def on_gradient_menu(self, event): if not hasattr(self, 'save_menuid'): # Do this only the first time. self.save_menuid = wx.NewId() self.load_menuid = wx.NewId() self.Bind(wx.EVT_MENU, self.on_save, id=self.save_menuid) self.Bind(wx.EVT_MENU, self.on_load, id=self.load_menuid) menu = wx.Menu() menu.Append(self.save_menuid, "Save as") menu.Append(self.load_menuid, "Load") self.PopupMenu(menu) menu.Destroy() def on_save(self, event): """ Open "Save" dialog, write lookuptable to 3 files: ``*.lut`` (lookuptable) ``*.grad`` (gradient table for use with this program), and ``*.jpg`` (image of the gradient) """ dlg = wx.FileDialog(self, "Save LUT to...", style=wx.SAVE) wildcard = "Gradient Files (.grad)|*.grad|" \ "All files (*.*)|*.*" dlg.SetWildcard(wildcard) if (dlg.ShowModal() == wx.ID_OK): file_name = dlg.GetPath() if file_name: self.save(file_name) def on_load(self, event): """ Load a ``*.grad`` lookuptable file using wxpython dialog """ style = wx.OPEN | wx.HIDE_READONLY dlg = wx.FileDialog(self, "Open a file", style=style) wildcard = "Gradient Files (.grad)|*.grad|" \ "All files (*.*)|*.*" dlg.SetWildcard(wildcard) if (dlg.ShowModal() == wx.ID_OK): file_name = dlg.GetPath() if file_name: self.load(file_name) ########################################################################## # `wxGradientEditor` class. ########################################################################## class wxGradientEditor(wx.Frame): """ wxPython frame that displays the gradient editor window, i.e. the thing that contains the gradient display, the function controls and the buttons. """ def __init__(self, vtk_table, on_change_color_table = None, colors=None): """Initialize the gradient editor window. Parameters ---------- vtk_table: Instance of vtkLookupTable, designating the table which is to be edited. on_change_color_table: Callback function taking no arguments. Called when the color table was changed and rendering is requested. """ wx.Frame.__init__(self, None, -1, "Color Gradient Editor", wx.DefaultPosition, [350, 400]) self.widget = wxGradientEditorWidget(self, vtk_table, on_change_color_table, colors) # draw the rest of the GUI (i.e. statusbar, menubar, etc. self.SetupMenuBar() self.CreateStatusBar() def SetupMenuBar(self): """ Create menus (i.e. Create Filemenu and submenus, help menu, ...) """ ## Set up the MenuBar MenuBar = wx.MenuBar() #FILE Menu.... file_menu = wx.Menu() item = file_menu.Append(-1, "&Save","Save CTF") self.Bind(wx.EVT_MENU, self.widget.on_save, item) item = file_menu.Append(-1, "&Load","Load CTF") self.Bind(wx.EVT_MENU, self.widget.on_load, item) item = file_menu.Append(-1, "&Close","Close this frame") self.Bind(wx.EVT_MENU, self.OnQuit, item) MenuBar.Append(file_menu, "&File") help_menu = wx.Menu() item = help_menu.Append(-1, "&Help", "Help") self.Bind(wx.EVT_MENU, self.OnHelp, item) item = help_menu.Append(-1, "&About", "About") self.Bind(wx.EVT_MENU, self.OnAbout, item) MenuBar.Append(help_menu, "&Help") self.SetMenuBar(MenuBar) def OnQuit(self, event): self.Close() def OnHelp(self, event): """ Help defining the mouse interactions """ message = "Right click to add control points. Left click to move control points" dlg = wx.MessageDialog(self, message, 'About wxGradientEditor', wx.OK | wx.ICON_INFORMATION ) dlg.ShowModal() dlg.Destroy() def OnAbout(self, event): """ Who wrote the program?""" message = 'tk Gradient Editor for MayaVi1: Gerald Knizia (cgk.d@gmx.net)\n'\ 'wxPython port: Pete Schmitt (schmitt@colorado.edu)\n'\ 'Enhanced for MayaVi2: Prabhu Ramachandran' dlg = wx.MessageDialog(self, message, 'About wxGradientEditor', wx.OK | wx.ICON_INFORMATION ) dlg.ShowModal() dlg.Destroy() ########################################################################## # Traits UI factory functions. ########################################################################## def gradient_editor_factory(wx_parent, trait_editor): """This is a factory function for `traitsui.CustomEditor` and allows us to use the `wxGradientEditorWidget` as a traits UI editor. This currently assumes that the user is using TVTK. The `wxGradientEditorWidget` works with VTK directly. """ tvtk_obj = getattr(trait_editor.object, trait_editor.name) widget = wxGradientEditorWidget(wx_parent, tvtk_obj) return widget ########################################################################## # Test case related code. ########################################################################## def make_test_table(lut=False): from ctf import ColorTransferFunction, PiecewiseFunction if lut: table = tvtk.LookupTable() table.table_range = (255, 355) return table, None, None else: table = tvtk.VolumeProperty() ctf = ColorTransferFunction() mins, maxs = 255, 355 ds = (maxs-mins)/4.0 try: ctf.range = (mins, maxs) except Exception: # VTK versions < 5.2 don't seem to need this. pass ctf.add_rgb_point(mins, 0.00, 0.0, 1.00) ctf.add_rgb_point(mins+ds, 0.25, 0.5, 0.75) ctf.add_rgb_point(mins+2*ds, 0.50, 1.0, 0.50) ctf.add_rgb_point(mins+3*ds, 0.75, 0.5, 0.25) ctf.add_rgb_point(maxs, 1.00, 0.0, 0.00) otf = PiecewiseFunction() otf.add_point(255, 0.0) otf.add_point(355, 0.2) table.set_color(ctf) table.set_scalar_opacity(otf) return table, ctf, otf def test_trait_ui(): from traits.api import HasTraits, Instance, Button from traitsui.api import View, Item, CustomEditor class Test(HasTraits): p = Instance(tvtk.VolumeProperty, ()) b = Button('Click me') view = View(Item(name='p', style='custom', resizable=True, editor=CustomEditor(gradient_editor_factory)), Item('b') ) table, otf, ctf = make_test_table(False) t = Test(p=table) # We need to hang on to these so these don't go out of scope. t.otf = otf t.ctf = ctf return t def main(): table, ctf, otf = make_test_table(lut=False) # the actual gradient editor code. def on_color_table_changed(): """If we had a vtk window running, update it here""" print("Update Render Window") app = wx.PySimpleApp() editor = wxGradientEditor(table, on_color_table_changed, #colors=['rgb', 'a', 'h', 's', 'v'], ) editor.Show() app.MainLoop() ########################################################################## # Test application. ########################################################################## if __name__ == "__main__": #t = test_trait_ui() #t.configure_traits() main() mayavi-4.1.0/tvtk/util/__init__.py0000644000175100001440000000013211674464502020046 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/tvtk/util/gradient_editor.py0000644000175100001440000011330111674464502021455 0ustar ischnellusers00000000000000""" The common code for a gradient editor for `tvtk.LookupTables` and `tvtk.VolumeProperty` color transfer functions. Most of the code is independent of tvtk however. The toolkit specific code is in toolkit specific files. This code is distributed under the conditions of the BSD license. This code was originally written by Gerald Knizia and later modified by Prabhu Ramachandran for tvtk and MayaVi2. Copyright (c) 2005-2006, Gerald Knizia and Prabhu Ramachandran """ from os.path import splitext from tvtk.api import tvtk ########################################################################## # Utility functions. ########################################################################## def lerp(arg0,arg1,f): """linearly interpolate between arguments arg0 and arg1. The weight f is from [0..1], with f=0 giving arg0 and f=1 giving arg1""" return (1-f)*arg0 + f*arg1 def rgba_to_hsva(r,g,b,a): """Convert color from RGBA to HSVA. input: r,g,b,a are from [0..1] output: h,s,v,a are from [0..1] (h will never be 1.0) See http://en.wikipedia.org/wiki/HSV_color_space Only difference: hue range is [0..1) here, not [0..360).""" max_comp = max((r,g,b)) min_comp = min((r,g,b)) h = 1.0/6.0 #60.0 if ( max_comp != min_comp ): if ( r >= g) and ( r >= b ): h *= 0 + (g-b)/(max_comp-min_comp) elif ( g >= b ): h *= 2 + (b-r)/(max_comp-min_comp) else: h *= 4 + (r-g)/(max_comp-min_comp) if h < 0: h += 1.0 if h > 1.0: h -= 1.0 if ( max_comp != 0 ): s = ( max_comp - min_comp )/max_comp else: s = 0 v = max_comp return (h,s,v,a) def hsva_to_rgba(h_,s,v,a): """Convert color from HSVA to RGBA. input: h,s,v,a are from [0..1] output: r,g,b,a are from [0..1] See http://en.wikipedia.org/wiki/HSV_color_space Only difference: hue range is [0..1) here, not [0..360).""" (r,g,b,a) = (v,v,v,a) h = h_ * 360.0 if ( s < 1e-4 ): return (r,g,b,a)#zero saturation -> color acromatic hue_slice_index = int(h/60.0) hue_partial = h/60.0 - hue_slice_index p = v * ( 1 - s ) q = v * ( 1 - hue_partial * s ) t = v * ( 1 - (1-hue_partial) * s ) if ( 0 == hue_slice_index ): r, g, b = v, t, p elif ( 1 == hue_slice_index ): r, g, b = q, v, p elif ( 2 == hue_slice_index ): r, g, b = p, v, t elif ( 3 == hue_slice_index ): r, g, b = p, q, v elif ( 4 == hue_slice_index ): r, g, b = t, p, v elif ( 5 == hue_slice_index ): r, g, b = v, p, q return (r,g,b,a) ########################################################################## # `Color` class. ########################################################################## class Color: """Represents a color and provides means of automatic conversion between HSV(A) and RGB(A) color spaces. The color is stored in HSVA space.""" def __init__(self): self.hsva = (0.0, 0.0, 0.5, 1.0) def set_rgb(self,r,g,b): self.set_rgba(r,g,b,1.0) def set_rgba(self,r,g,b,a): self.hsva = rgba_to_hsva(r,g,b,a) def get_rgb255(self): """returns a tuple (r,g,b) of 3 integers in range [0..255] representing the color.""" rgba = self.get_rgba() return (int(rgba[0]*255), int(rgba[1]*255), int(rgba[2]*255) ) def get_rgba(self): h,s,v,a = self.hsva return hsva_to_rgba(h,s,v,a) def get_hsva(self): return self.hsva def set_hsva(self,h,s,v,a): self.hsva = (h,s,v,a) def set_lerp(self, f,A,B): """Set self to result of linear interpolation between colors A and B in HSVA space. The weight f is from [0..1], with f=0 giving A and f=1 giving color B.""" h = lerp(A.hsva[0], B.hsva[0], f) s = lerp(A.hsva[1], B.hsva[1], f) v = lerp(A.hsva[2], B.hsva[2], f) a = lerp(A.hsva[3], B.hsva[3], f) self.hsva = (h,s,v,a) ########################################################################## # `ColorControlPoint` class. ########################################################################## class ColorControlPoint: """A control point represents a fixed position in the gradient and its assigned color. A control point can have indifferent color channels in hsv space, i.e. channels, on which its presence does not impose any effect.""" def __init__(self, active_channels, fixed=False): self.color = Color() # position in the gradient table. range: [0..1]. self.pos = 0.0 # fixed control points can not be moved to other positions. The # control points for the begin and the end of the gradient are usually # the only fixed control points. self.fixed = fixed if ( 'a' != active_channels ): self.active_channels = "rgb" self.activate_channels(active_channels) else: self.active_channels = "a" def activate_channels(self,new_channels): """NewChannels: string consisting of the new color channel names""" for c in new_channels: if ( not ( c in self.active_channels ) ): self.active_channels += c def set_pos(self,f): self.pos = max(min(f,1.0), 0.0) ########################################################################## # `GradientTableOld` class. ########################################################################## class GradientTableOld: """this class represents a logical gradient table, i.e. an array of colors and the means to control it via control points""" def __init__( self, num_entries ): self.size = num_entries self.table = [[0.0]*self.size, [0.0]*self.size, [0.0]*self.size, [0.0]*self.size] self.table_hsva = [[0.0]*self.size, [0.0]*self.size, [0.0]*self.size, [0.0]*self.size] # ^- table[channel][index]: rgba values of the colors of the table. # range: [0..1]^4. # insert the control points for the left and the right end of the # gradient. These are fixed (i.e. cannot be moved or deleted) and # allow one to set begin and end colors. left_control_point = ColorControlPoint(fixed=True, active_channels="hsva") left_control_point.set_pos(0.0) left_control_point.color.set_rgb(0.0, 0.0, 0.0) right_control_point = ColorControlPoint(fixed=True, active_channels="hsva") right_control_point.set_pos(1.0) right_control_point.color.set_rgb(1.0, 1.0, 1.0) self.control_points = [left_control_point, right_control_point] # note: The array of control points always has to be sorted by gradient # position of the control points. # insert another control point. This one has no real function, it # is just there to make the gradient editor more colorful initially # and suggest to the (first time user) that it is actually possible to # place more control points. mid_control_point = ColorControlPoint(active_channels="hsv") mid_control_point.set_pos(0.4) mid_control_point.color.set_rgb(1.0,0.4,0.0) self.insert_control_point( mid_control_point ) # it is possible to scale the output gradient using a nonlinear function # which maps [0..1] to [0..1], aviable using the "nonlin" option in the # gui. Per default, this option is disabled however. self.scaling_function_string = "" # will receive the function string if # set, e.g. "x**(4*a)" self.scaling_function_parameter = 0.5 # the parameter a, slider controlled self.scaling_function = None # the actual function object. takes one # position parameter. None if disabled. self.update() def get_color_hsva(self,idx): """return (h,s,v,a) tuple in self.table_hsva for index idx""" return (self.table_hsva[0][idx],self.table_hsva[1][idx], self.table_hsva[2][idx],self.table_hsva[3][idx]) def get_color(self,idx): """return (r,g,b,a) tuple in self.table for index idx""" return (self.table[0][idx],self.table[1][idx], self.table[2][idx],self.table[3][idx]) def set_color_hsva(self,idx,hsva_color): """set hsva table entry for index idx to hsva_color, which must be (h,s,v,a)""" self.table_hsva[0][idx] = hsva_color[0] self.table_hsva[1][idx] = hsva_color[1] self.table_hsva[2][idx] = hsva_color[2] self.table_hsva[3][idx] = hsva_color[3] def set_color(self,idx,rgba_color): """set rgba table entry for index idx to rgba_color, which must be (r,g,b,a)""" self.table[0][idx] = rgba_color[0] self.table[1][idx] = rgba_color[1] self.table[2][idx] = rgba_color[2] self.table[3][idx] = rgba_color[3] def get_pos_index(self,f): """return index in .table of gradient position f \in [0..1]""" return int(f*(self.size-1)) def get_index_pos(self,idx): """return position f \in [0..1] of gradient table index idx""" return (1.0*idx)/(self.size-1) def get_pos_color(self,f): """return a Color object representing the color which is lies at position f \in [0..1] in the current gradient""" result = Color() #e = self.table_hsva[:,self.get_pos_index(f)] e = self.get_color_hsva(self.get_pos_index(f)) result.set_hsva(e[0], e[1], e[2], e[3]) return result def get_pos_rgba_color_lerped(self,f): """return a (r,g,b,a) color representing the color which is lies at position f \in [0..1] in the current gradient. if f is outside the [0..1] interval, the result will be clamped to this interval""" scaled_pos = max(min(f,1.0), 0.0)*(self.size-1) idx0 = int(scaled_pos) fraction = scaled_pos - idx0 idx1 = min( self.size - 1, 1 + idx0 ) r = lerp( self.table[0][idx0], self.table[0][idx1], fraction ) g = lerp( self.table[1][idx0], self.table[1][idx1], fraction ) b = lerp( self.table[2][idx0], self.table[2][idx1], fraction ) a = lerp( self.table[3][idx0], self.table[3][idx1], fraction ) return (r,g,b,a) def insert_control_point(self,new_point): """Insert a new control point into the table. Does sort the control points, but does NOT update the table.""" self.control_points += [new_point] self.sort_control_points() def sort_control_points(self): """Sort control points by position. Call this if the position of any control point was changed externally. The control point array always has to be sorted.""" def pred(x, y): if x < y: return -1 elif y < x: return +1 else: return 0 self.control_points.sort( lambda x, y: pred(x.pos, y.pos) ) def update(self): """Recalculate the gradient table from the control points. The colors are interpolated linearly between each two control points in hsva space. """ #self.Sortcontrol_points() control_point_indices_total = [] for point in self.control_points: control_point_indices_total.append((self.get_pos_index(point.pos),point)) # first, recalculate the Hsva table channel-wise from the control points for it in [("h",0),("s",1),("v",2),("a",3)]: # take into account only control points which are active # for the current channel control_point_indices = filter( \ lambda x: it[0] in x[1].active_channels, control_point_indices_total ) assert( len( control_point_indices ) >= 2 ) # we always interpolate between two adjacent control points on the # current channel. NextIntervalBeginIdx marks the first table index # on which the next set of control points is to be choosen. start_point_id = -1 end_point_id = 0 start_pos = 0 #dummy value end_pos = 0 #dummy value next_interval_begin_idx = 0 end_point = control_point_indices[0][1] assert( next_interval_begin_idx == 0 ) for k in range(self.size): while( k == next_interval_begin_idx ): # ^-- this loop makes sure that we won't attempt to # interpolate between two control points that lie on # each other. read "if" instead of "while". start_point_id += 1 end_point_id += 1 start_point = end_point start_pos = end_pos end_point = control_point_indices[end_point_id][1] end_pos = end_point.pos next_interval_begin_idx = 1+control_point_indices[end_point_id][0] # calculate float position of this entry in the gradient table # and (linear) position in the current gradient between the # two current control points cur_pos = self.get_index_pos(k) f = ( cur_pos - start_pos ) / ( end_pos - start_pos ) assert( ( 0 <= f ) and ( f <= 1 ) ) # ^-- this might happen when two control points lie on each # other. Since this case only occurs as an intermediate case # when dragging it is not really problematic. #f = min( 1.0, max( 0.0, f ) ) self.table_hsva[it[1]][k] = lerp(start_point.color.hsva[it[1]], end_point.color.hsva[it[1]], f) assert( next_interval_begin_idx == self.size ) # convert hsva colors to rgba for k in range(self.size): h,s,v,a = self.get_color_hsva(k) self.set_color(k, hsva_to_rgba(h, s, v, a)) def store_to_vtk_lookup_table(self, vtk_table, num_entries=256): """Store current color table in `vtk_table`, an instance of `tvtk.LookupTable`. """ vtk_table.number_of_table_values = num_entries scale_xform = lambda x:x if self.scaling_function: scale_xform = self.scaling_function for idx in range(num_entries): f = scale_xform(float(idx)/(num_entries-1)) rgba = self.get_pos_rgba_color_lerped(f) vtk_table.set_table_value( idx, rgba ) def store_to_vtk_volume_prop(self, volume_prop, scalar_range): """Given a `tvtk.VolumeProperty` and a scalar range to map values into, this sets the CTF based on the current control points. """ # FIXME: This method does not support scaling! ctf = volume_prop.rgb_transfer_function ctf.remove_all_points() otf = volume_prop.get_scalar_opacity() otf.remove_all_points() s1, s2 = scalar_range size = s2 - s1 for point in self.control_points: x = s1 + point.pos*size h, s, v, a = point.color.get_hsva() if point.active_channels != 'a': ctf.add_hsv_point(x, h, s, v) if 'a' in point.active_channels: otf.add_point(x, a) def load_from_vtk_volume_prop(self, volume_prop): """Given a vtkVolumeProperty, this initializes the control points of the gradient table. This works best when a ctf.ColorTransferFunction and PiecewiseFunction are used. Note that it is not as easy to setup the control points from a LUT because the LUT may end up having more than the size of the table editor here. It also usually does not make sense to do this with a LUT. """ # FIXME: This method does not support scaling! ctf = volume_prop.rgb_transfer_function otf = volume_prop.get_scalar_opacity() # We need a CTF with at least 2 points. size = ctf.size assert (size > 1) assert (otf.size > 1) s1, s2 = ctf.range scale = float(s2 - s1) ds = scale/(size -1) new_ctl_pts = [] has_nodes = False if hasattr(ctf, 'nodes'): has_nodes = True for i in range(size): if has_nodes: x = ctf.nodes[i] else: x = s1 + i*ds r, g, b = ctf.get_color(x) a = otf.get_value(x) if (i == 0) or (i == (size-1)): # First and last points are fixed. pt = ColorControlPoint(active_channels="hsva", fixed=True) else: pt = ColorControlPoint(active_channels="hsv", fixed=False) pt.color.set_rgba(r, g, b, a) pos = (x - s1)/scale pt.set_pos(pos) new_ctl_pts.append(pt) # The alpha values are indipendent of the hsv ones. size = otf.size ds = scale/(size -1) has_nodes = False if hasattr(ctf, 'nodes'): has_nodes = True for i in range(1, size-1): if has_nodes: x = otf.nodes[i] else: x = s1 + i*ds a = otf.get_value(x) r, g, b = ctf.get_color(x) pt = ColorControlPoint(active_channels="a", fixed=False) pt.color.set_rgba(r, g, b, a) pos = (x - s1)/scale pt.set_pos(pos) new_ctl_pts.append(pt) self.control_points = new_ctl_pts self.sort_control_points() self.update() def scaling_parameters_changed(self): """Recompile the scaling function.""" from math import tan, atan, cos, acos, sin, asin, pow, log, exp, e, pi self.scaling_function = None # let python generate a new function via the exec statement. to make # the security risk calculable, we execute that function in a local # scope. The downside is that we have to provide math functions # one at a time. def_string = "def ParamFn(x): return %s " % (self.scaling_function_string) dict = {"a":self.scaling_function_parameter, "ParamFn":None, "atan":atan, "tan":tan, "cos":cos, "acos":acos, "sin":sin, "asin":asin, "pow":pow, "log":log, "exp":exp, "e":e, "pi":pi } if ( "" == self.scaling_function_string ): return try: exec def_string in dict self.scaling_function = dict["ParamFn"] except: raise ValueError("failed to compile function: ", def_string ) def set_scaling_function_parameter(self,new_parameter): """Set the 'a' parameter of the scaling function""" self.scaling_function_parameter = new_parameter self.scaling_parameters_changed() def set_scaling_function(self,new_function_string): """Set scaling function. new_function_string is a string describing the function, e.g. 'x**(4*a)' """ self.scaling_function_string = new_function_string self.scaling_parameters_changed() def save(self, file_name): """Save control point set into a new file FileName. It is not checked whether the file already exists. Further writes out a VTK .lut file and a .jpg file showing the gradients.""" # Ensure that if the input file name had one of the extensions # we'll be writing out ourselves, it gets stripped out first. path_base,ext = splitext(file_name) #print(file_name) if ext.lower() in ['.lut','.jpg','.jpeg','.grad']: ext = '' file_name = path_base + ext # Create the three names for the files we'll be actually # writing out. file_name_grad = file_name + '.grad' file_name_lut = file_name + '.lut' file_name_jpg = file_name + '.jpg' # write control points set. file = open( file_name_grad, "w" ) file.write( "V 2.0 Color Gradient File\n" ) file.write( "ScalingFunction: %s\n" % (self.scaling_function_string) ) file.write( "ScalingParameter: %s\n" % (self.scaling_function_parameter) ) file.write( "ControlPoints: (pos fixed bindings h s v a)\n" ) for control_point in self.control_points: file.write( " %s %s %s %s %s %s %s\n" % ( \ control_point.pos, control_point.fixed, control_point.active_channels, control_point.color.get_hsva()[0], control_point.color.get_hsva()[1], control_point.color.get_hsva()[2], control_point.color.get_hsva()[3] ) ) file.close() # write vtk lookup table. Unfortunatelly these objects don't seem to # have any built in and exposed means of loading or saving them, so # we build the vtk file directly vtk_table = tvtk.LookupTable() self.store_to_vtk_lookup_table(vtk_table) file = open( file_name_lut, "w" ) num_colors = vtk_table.number_of_table_values file.write( "LOOKUP_TABLE UnnamedTable %s\n" % ( num_colors ) ) for idx in range(num_colors): entry = vtk_table.get_table_value(idx) file.write("%.4f %.4f %.4f %.4f\n" % (entry[0],entry[1],entry[2],entry[3])) file.close() # if the python image library is aviable, also generate a small .jpg # file showing how the gradient looks. Based on code from Arnd Baecker. try: import Image except ImportError: pass # we're ready otherwise. no jpg output tho. else: Ny=64 # vertical size of the jpeg im = Image.new("RGBA",(num_colors,Ny)) for nx in range(num_colors): (r,g,b,a) = vtk_table.get_table_value(nx) for ny in range(Ny): im.putpixel((nx,ny),(int(255*r),int(255*g),int(255*b), int(255*a))) im.save(file_name_jpg,"JPEG") # it might be better to store the gradient as .png file, as these # are actually able to store alpha components (unlike jpg files) # and might also lead to a better compression. def load(self, file_name): """Load control point set from file FileName and recalculate gradient table.""" file = open( file_name, "r" ) version_tag = file.readline() version = float(version_tag.split()[1])+1e-5 if ( version >= 1.1 ): # read in the scaling function and the scaling function parameter function_line_split = file.readline().split() parameter_line = file.readline() if ( len(function_line_split)==2 ): self.scaling_function_string = function_line_split[1] else: self.scaling_function_string = "" self.scaling_function_parameter = float(parameter_line.split()[1]) else: self.scaling_function_string = "" self.scaling_function_parameter = 0.5 file.readline() new_control_points = [] while True: cur_line = file.readline() if len(cur_line) == 0: # readline is supposed to return an empty string at EOF break args = cur_line.split() if ( len(args) < 7 ): msg = "gradient file format broken at line:\n" msg += cur_line raise ValueError(msg) new_point = ColorControlPoint(active_channels="") new_point.set_pos( float( args[0] ) ) new_point.fixed = "True" == args[1] #bool( args[1] ) new_point.active_channels = args[2] (h,s,v,a) = ( float(args[3]), float(args[4]), float(args[5]), float(args[6]) ) new_point.color.set_hsva(h,s,v,a) new_control_points.append(new_point) file.close() self.control_points = new_control_points self.sort_control_points() self.scaling_parameters_changed() self.update() ########################################################################## # `GradientTable` class. ########################################################################## class GradientTable: """this class represents a logical gradient table, i.e. an array of colors and the means to control it via control points This class (unlike the GradientTableOld) does not support scaling and uses VTK's ColorTransferFunction and PiecewiseFunction to perform the actual interpolation. """ def __init__( self, num_entries ): self.size = num_entries self.table = tvtk.ColorTransferFunction() try: self.table.range = (0.0, 1.0) except Exception: # VTK versions < 5.2 don't seem to need this. pass self.alpha = tvtk.PiecewiseFunction() # These VTK classes perform the interpolation for us. # insert the control points for the left and the right end of the # gradient. These are fixed (i.e. cannot be moved or deleted) and # allow one to set begin and end colors. left_control_point = ColorControlPoint(fixed=True, active_channels="hsva") left_control_point.set_pos(0.0) left_control_point.color.set_rgb(0.0, 0.0, 0.0) right_control_point = ColorControlPoint(fixed=True, active_channels="hsva") right_control_point.set_pos(1.0) right_control_point.color.set_rgb(1.0, 1.0, 1.0) self.control_points = [left_control_point, right_control_point] # note: The array of control points always has to be sorted by gradient # position of the control points. # insert another control point. This one has no real function, it # is just there to make the gradient editor more colorful initially # and suggest to the (first time user) that it is actually possible to # place more control points. mid_control_point = ColorControlPoint(active_channels="hsv") mid_control_point.set_pos(0.4) mid_control_point.color.set_rgb(1.0,0.4,0.0) self.insert_control_point( mid_control_point ) # These variables are only for compatibility with GradientTableOld. self.scaling_function_string = "" # will receive the function string if # set, e.g. "x**(4*a)" self.scaling_function_parameter = 0.5 # the parameter a, slider controlled self.scaling_function = None # the actual function object. takes one # position parameter. None if disabled. self.update() def get_color_hsva(self, f): """return (h,s,v,a) tuple in self.table_hsva for fraction f in [0,1].""" r, g, b = self.table.get_color(f) a = self.alpha.get_value(f) return rgba_to_hsva(r, g, b, a) def get_color(self, f): """return (r,g,b,a) tuple in self.table for fraction f in [0,1].""" r, g, b = self.table.get_color(f) a = self.alpha.get_value(f) return r, g, b, a def get_pos_color(self,f): """return a Color object representing the color which is lies at position f \in [0..1] in the current gradient""" result = Color() e = self.get_color_hsva(f) result.set_hsva(*e) return result def get_pos_rgba_color_lerped(self,f): """return a (r,g,b,a) color representing the color which is lies at position f \in [0..1] in the current gradient. if f is outside the [0..1] interval, the result will be clamped to this interval.""" return self.get_color(f) def insert_control_point(self,new_point): """Insert a new control point into the table. Does sort the control points, but does NOT update the table.""" self.control_points += [new_point] self.sort_control_points() def sort_control_points(self): """Sort control points by position. Call this if the position of any control point was changed externally. The control point array always has to be sorted.""" def pred(x, y): if x < y: return -1 elif y < x: return +1 else: return 0 self.control_points.sort( lambda x, y: pred(x.pos, y.pos) ) def update(self): """Recalculate the gradient table from the control points. The colors are interpolated linearly between each two control points in hsva space. """ #self.sort_control_points() table = self.table alpha = self.alpha table.remove_all_points() alpha.remove_all_points() for point in self.control_points: x = point.pos h, s, v, a = point.color.get_hsva() if point.active_channels != 'a': table.add_hsv_point(x, h, s, v) if 'a' in point.active_channels: alpha.add_point(x, a) def store_to_vtk_lookup_table(self, vtk_table, num_entries=256): """Store current color table in `vtk_table`, an instance of `tvtk.LookupTable`. """ vtk_table.number_of_table_values = num_entries for idx in range(num_entries): f = float(idx)/(num_entries-1) rgba = self.get_color(f) vtk_table.set_table_value( idx, rgba ) def store_to_vtk_volume_prop(self, volume_prop, scalar_range): """Given a `tvtk.VolumeProperty` and a scalar range to map values into, this sets the CTF based on the current control points. """ # FIXME: This method does not support scaling! ctf = volume_prop.rgb_transfer_function ctf.remove_all_points() otf = volume_prop.get_scalar_opacity() otf.remove_all_points() s1, s2 = scalar_range try: ctf.range = s1, s2 except Exception: # VTK versions < 5.2 don't seem to need this. pass size = s2 - s1 for point in self.control_points: x = s1 + point.pos*size h, s, v, a = point.color.get_hsva() if point.active_channels != 'a': ctf.add_hsv_point(x, h, s, v) if 'a' in point.active_channels: otf.add_point(x, a) def load_from_vtk_volume_prop(self, volume_prop): """Given a vtkVolumeProperty, this initializes the control points of the gradient table. This works best when a ctf.ColorTransferFunction and PiecewiseFunction are used. Note that it is not as easy to setup the control points from a LUT because the LUT may end up having more than the size of the table editor here. It also usually does not make sense to do this with a LUT. """ # FIXME: This method does not support scaling! ctf = volume_prop.rgb_transfer_function otf = volume_prop.get_scalar_opacity() # We need a CTF with at least 2 points. size = ctf.size assert (size > 1) assert (otf.size > 1) s1, s2 = ctf.range scale = float(s2 - s1) ds = scale/(size -1) new_ctl_pts = [] has_nodes = False if hasattr(ctf, 'nodes'): has_nodes = True for i in range(size): if has_nodes: x = ctf.nodes[i] else: x = s1 + i*ds r, g, b = ctf.get_color(x) a = otf.get_value(x) if (i == 0) or (i == (size-1)): # First and last points are fixed. pt = ColorControlPoint(active_channels="hsva", fixed=True) else: pt = ColorControlPoint(active_channels="hsv", fixed=False) pt.color.set_rgba(r, g, b, a) pos = (x - s1)/scale pt.set_pos(pos) new_ctl_pts.append(pt) # The alpha values are indipendent of the hsv ones. size = otf.size ds = scale/(size -1) has_nodes = False if hasattr(ctf, 'nodes'): has_nodes = True for i in range(1, size-1): if has_nodes: x = otf.nodes[i] else: x = s1 + i*ds a = otf.get_value(x) r, g, b = ctf.get_color(x) pt = ColorControlPoint(active_channels="a", fixed=False) pt.color.set_rgba(r, g, b, a) pos = (x - s1)/scale pt.set_pos(pos) new_ctl_pts.append(pt) self.control_points = new_ctl_pts self.sort_control_points() self.update() def scaling_parameters_changed(self): """Recompile the scaling function.""" raise NotImplementedError def set_scaling_function_parameter(self,new_parameter): """Set the 'a' parameter of the scaling function""" raise NotImplementedError def set_scaling_function(self,new_function_string): """Set scaling function. new_function_string is a string describing the function, e.g. 'x**(4*a)' """ raise NotImplementedError def save(self, file_name): """Save control point set into a new file FileName. It is not checked whether the file already exists. Further writes out a VTK .lut file and a .jpg file showing the gradients.""" # Ensure that if the input file name had one of the extensions # we'll be writing out ourselves, it gets stripped out first. path_base,ext = splitext(file_name) #print(file_name) if ext.lower() in ['.lut','.jpg','.jpeg','.grad']: ext = '' file_name = path_base + ext # Create the three names for the files we'll be actually # writing out. file_name_grad = file_name + '.grad' file_name_lut = file_name + '.lut' file_name_jpg = file_name + '.jpg' # write control points set. file = open( file_name_grad, "w" ) file.write( "V 2.0 Color Gradient File\n" ) file.write( "ScalingFunction: %s\n" % (self.scaling_function_string) ) file.write( "ScalingParameter: %s\n" % (self.scaling_function_parameter) ) file.write( "ControlPoints: (pos fixed bindings h s v a)\n" ) for control_point in self.control_points: file.write( " %s %s %s %s %s %s %s\n" % ( \ control_point.pos, control_point.fixed, control_point.active_channels, control_point.color.get_hsva()[0], control_point.color.get_hsva()[1], control_point.color.get_hsva()[2], control_point.color.get_hsva()[3] ) ) file.close() # write vtk lookup table. Unfortunatelly these objects don't seem to # have any built in and exposed means of loading or saving them, so # we build the vtk file directly vtk_table = tvtk.LookupTable() self.store_to_vtk_lookup_table(vtk_table) file = open( file_name_lut, "w" ) num_colors = vtk_table.number_of_table_values file.write( "LOOKUP_TABLE UnnamedTable %s\n" % ( num_colors ) ) for idx in range(num_colors): entry = vtk_table.get_table_value(idx) file.write("%.4f %.4f %.4f %.4f\n" % (entry[0],entry[1],entry[2],entry[3])) file.close() # if the python image library is aviable, also generate a small .jpg # file showing how the gradient looks. Based on code from Arnd Baecker. try: import Image except ImportError: pass # we're ready otherwise. no jpg output tho. else: Ny=64 # vertical size of the jpeg im = Image.new("RGBA",(num_colors,Ny)) for nx in range(num_colors): (r,g,b,a) = vtk_table.get_table_value(nx) for ny in range(Ny): im.putpixel((nx,ny),(int(255*r),int(255*g),int(255*b), int(255*a))) im.save(file_name_jpg,"JPEG") # it might be better to store the gradient as .png file, as these # are actually able to store alpha components (unlike jpg files) # and might also lead to a better compression. def load(self, file_name): """Load control point set from file FileName and recalculate gradient table.""" file = open( file_name, "r" ) version_tag = file.readline() version = float(version_tag.split()[1])+1e-5 if ( version >= 1.1 ): # read in the scaling function and the scaling function parameter function_line_split = file.readline().split() parameter_line = file.readline() if ( len(function_line_split)==2 ): self.scaling_function_string = function_line_split[1] else: self.scaling_function_string = "" self.scaling_function_parameter = float(parameter_line.split()[1]) else: self.scaling_function_string = "" self.scaling_function_parameter = 0.5 file.readline() new_control_points = [] while True: cur_line = file.readline() if len(cur_line) == 0: # readline is supposed to return an empty string at EOF break args = cur_line.split() if ( len(args) < 7 ): msg = "gradient file format broken at line:\n" msg += cur_line raise ValueError(msg) new_point = ColorControlPoint(active_channels="") new_point.set_pos( float( args[0] ) ) new_point.fixed = "True" == args[1] #bool( args[1] ) new_point.active_channels = args[2] (h,s,v,a) = ( float(args[3]), float(args[4]), float(args[5]), float(args[6]) ) new_point.color.set_hsva(h,s,v,a) new_control_points.append(new_point) file.close() self.control_points = new_control_points self.sort_control_points() #self.scaling_parameters_changed() self.update() mayavi-4.1.0/tvtk/plugins/0000755000175100001440000000000011674464502016445 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/plugins/scene/0000755000175100001440000000000011674464502017542 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/plugins/scene/ui/0000755000175100001440000000000011674464502020157 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/plugins/scene/ui/actions.py0000644000175100001440000001612511674464502022176 0ustar ischnellusers00000000000000""" The various actions for the Scene plugin. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. # Enthought library imports. from pyface.api import FileDialog, OK from pyface.action.api import Action from traits.api import Instance, Property, Str class SceneAction(Action): """ Base class for actions that require a scene manager. """ #### 'SceneAction' interface ############################################## # The scene manager. scene_manager = Property(Instance( 'tvtk.plugins.scene.i_scene_manager import ISceneManager' )) def _get_scene_manager(self): """ Trait property getter. """ from tvtk.plugins.scene.i_scene_manager import ( ISceneManager ) return self.window.get_service(ISceneManager) class NewScene(Action): """ An action that creates a new TVTK scene. """ #### 'Action' interface ################################################### name = 'Scene' def perform(self, event): """ Performs the action. """ from tvtk.plugins.scene.scene_editor import SceneEditor editor = self.window.edit(object(), kind=SceneEditor) return editor class SaveScene(SceneAction): """ An action that saves a scene to an image. """ #### 'Action' interface ################################################### name = 'Save Scene' def perform(self, event): """ Performs the action. """ extensions = [ '*.png', '*.jpg', '*.jpeg', '*.tiff', '*.bmp', '*.ps', '*.eps', '*.tex', '*.rib', '*.wrl', '*.oogl', '*.pdf', '*.vrml', '*.obj', '*.iv' ] wildcard = '|'.join(extensions) dialog = FileDialog( parent = self.window.control, title = 'Save scene to image', action = 'save as', wildcard = wildcard ) if dialog.open() == OK: scene = self.scene_manager.current_scene if scene is not None: scene.save(dialog.path) return class SaveSceneToImage(SceneAction): """ An action that saves a scene to an image. """ #### 'Action' interface ################################################### # Name of the action. name = 'Image' #### 'SaveSceneToImage' interface ######################################### # The save method name. save_method = Str('save') # The wildcard for the file dialog. wildcard = Str("All files (*.*)|*.*") ########################################################################### # 'Action' interface. ########################################################################### def perform(self, event): """ Perform the action. """ dialog = FileDialog( parent = self.window.control, title = 'Save scene to %s' % self.name, action = 'save as', wildcard = self.wildcard ) if dialog.open() == OK: scene = self.scene_manager.current_scene if scene is not None: method = getattr(scene, self.save_method) method(dialog.path) return # These are all specific subclasses that save particular images. class SaveSceneToPNG(SaveSceneToImage): name = 'PNG Image' save_method = 'save_png' wildcard = 'PNG images (*.png)|*.png|' \ 'All files (*.*)|*.*' class SaveSceneToJPEG(SaveSceneToImage): name = 'JPEG Image' save_method = 'save_jpg' wildcard = 'JPEG images (*.jpg)|*.jpg|' \ 'JPEG images (*.jpeg)|*.jpeg|' \ 'All files (*.*)|*.*' class SaveSceneToBMP(SaveSceneToImage): name = 'BMP Image' save_method = 'save_bmp' wildcard = 'BMP images (*.bmp)|*.bmp|' \ 'All files (*.*)|*.*' class SaveSceneToTIFF(SaveSceneToImage): name = 'TIFF Image' save_method = 'save_tiff' wildcard = 'TIFF images (*.tif)|*.tif|' \ 'TIFF images (*.tiff)|*.tiff|' \ 'All files (*.*)|*.*' class SaveSceneToPS(SaveSceneToImage): name = 'PostScript bitmap Image' save_method = 'save_ps' wildcard = 'PostScript bitmap images (*.ps)|*.ps|' \ 'All files (*.*)|*.*' class SaveSceneToGL2PS(SaveSceneToImage): name = 'Vector PS/EPS/PDF/TeX' save_method = 'save_gl2ps' wildcard = 'All files (*.*)|*.*|' \ 'EPS files (*.eps)|*.eps|' \ 'PS files (*.ps)|*.ps|' \ 'PDF files (*.pdf)|*.pdf|' \ 'TeX files (*.tex)|*.tex' class SaveSceneToRIB(SaveSceneToImage): name = 'RenderMan RIB file' save_method = 'save_rib' wildcard = 'RIB files (*.rib)|*.rib|' \ 'All files (*.*)|*.*' class SaveSceneToOOGL(SaveSceneToImage): name = 'GeomView OOGL file' save_method = 'save_oogl' wildcard = 'OOGL files (*.oogl)|*.oogl|' \ 'All files (*.*)|*.*' class SaveSceneToIV(SaveSceneToImage): name = 'OpenInventor file' save_method = 'save_iv' wildcard = 'OpenInventor files (*.iv)|*.iv|' \ 'All files (*.*)|*.*' class SaveSceneToVRML(SaveSceneToImage): name = 'VRML file' save_method = 'save_vrml' wildcard = 'VRML files (*.wrl)|*.wrl|' \ 'All files (*.*)|*.*' class SaveSceneToOBJ(SaveSceneToImage): name = 'Wavefront OBJ file' save_method = 'save_wavefront' wildcard = 'OBJ files (*.obj)|*.obj|' \ 'All files (*.*)|*.*' class SetView(SceneAction): """ An action that sets the current scene to a particular view.""" #### 'SetView' interface ################################################## # The method to invoke on the scene that will set the view. view_method = Str ########################################################################### # 'Action' interface. ########################################################################### def perform(self, event): """ Perform the action. """ scene = self.scene_manager.current_scene if scene is not None: method = getattr(scene, self.view_method) method() return # These are all specific subclasses that invoke particular views. class ResetZoom(SetView): name = '&Reset Zoom' view_method = 'reset_zoom' class IsometricView(SetView): name = '&Isometric View' view_method = 'isometric_view' class XPlusView(SetView): name = '&X+ View' view_method = 'x_plus_view' class XMinusView(SetView): name = '&X- View' view_method = 'x_minus_view' class YPlusView(SetView): name = '&Y+ View' view_method = 'y_plus_view' class YMinusView(SetView): name = '&Y- View' view_method = 'y_minus_view' class ZPlusView(SetView): name = '&Z+ View' view_method = 'z_plus_view' class ZMinusView(SetView): name = '&Z- View' view_method = 'z_minus_view' #### EOF ###################################################################### mayavi-4.1.0/tvtk/plugins/scene/ui/__init__.py0000644000175100001440000000013711674464502022271 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/tvtk/plugins/scene/ui/scene_ui_action_set.py0000644000175100001440000001053011674464502024532 0ustar ischnellusers00000000000000""" The default action set for the scene UI plugin. """ # Author: Prabhu Ramachandran # Copyright (c) 2006, Enthought, Inc. # License: BSD Style. # Enthought library imports. from envisage.ui.action.api import Action, ActionSet, Group, Menu # This package PKG = '.'.join(__name__.split('.')[:-1]) #### Groups ################################################################### scene_group = Group( id='TVTKSceneGroup', path='MenuBar/File', before='ExitGroup' ) view_group = Group( id='TVTKViewGroup', path='MenuBar/Tools', before='PreferencesGroup' ) #### Menus #################################################################### new_menu = Menu( name='&New', path='MenuBar/File', group='TVTKSceneGroup' ) save_scene_as_menu = Menu( id='SaveSceneAs', name="Sa&ve Scene As", path='MenuBar/File', group='TVTKSceneGroup', after='New' ) #### Actions ################################################################## new_scene = Action( class_name = PKG + '.actions.NewScene', path = 'MenuBar/File/New', group='additions' ) #### Save actions #### save_scene = Action( class_name = PKG + '.actions.SaveScene', path = 'MenuBar/File', group='TVTKSceneGroup', after='SaveSceneAs' ) save_scene_to_png = Action( class_name = PKG + '.actions.SaveSceneToPNG', path = 'MenuBar/File/SaveSceneAs' ) save_scene_to_jpeg = Action( class_name = PKG + '.actions.SaveSceneToJPEG', path = 'MenuBar/File/SaveSceneAs' ) save_scene_to_bmp = Action( class_name = PKG + '.actions.SaveSceneToBMP', path = 'MenuBar/File/SaveSceneAs' ) save_scene_to_tiff = Action( class_name = PKG + '.actions.SaveSceneToTIFF', path = 'MenuBar/File/SaveSceneAs' ) save_scene_to_ps = Action( class_name = PKG + '.actions.SaveSceneToPS', path = 'MenuBar/File/SaveSceneAs' ) save_scene_to_gl2ps = Action( class_name = PKG + '.actions.SaveSceneToGL2PS', path = 'MenuBar/File/SaveSceneAs' ) save_scene_to_rib = Action( class_name = PKG + '.actions.SaveSceneToRIB', path = 'MenuBar/File/SaveSceneAs' ) save_scene_to_oogl = Action( class_name = PKG + '.actions.SaveSceneToOOGL', path = 'MenuBar/File/SaveSceneAs' ) save_scene_to_iv = Action( class_name = PKG + '.actions.SaveSceneToIV', path = 'MenuBar/File/SaveSceneAs' ) save_scene_to_vrml = Action( class_name = PKG + '.actions.SaveSceneToVRML', path = 'MenuBar/File/SaveSceneAs' ) save_scene_to_obj = Action( class_name = PKG + '.actions.SaveSceneToOBJ', path = 'MenuBar/File/SaveSceneAs' ) #### View actions #### reset_zoom = Action( class_name = PKG + '.actions.ResetZoom', path = 'MenuBar/Tools', group='TVTKViewGroup' ) isometric_view = Action( class_name = PKG + '.actions.IsometricView', path = 'MenuBar/Tools', group='TVTKViewGroup' ) x_plus_view = Action( class_name = PKG + '.actions.XPlusView', path = 'MenuBar/Tools', group='TVTKViewGroup' ) x_minus_view = Action( class_name = PKG + '.actions.XMinusView', path = 'MenuBar/Tools', group='TVTKViewGroup' ) y_plus_view = Action( class_name = PKG + '.actions.YPlusView', path = 'MenuBar/Tools', group='TVTKViewGroup' ) y_minus_view = Action( class_name = PKG + '.actions.YMinusView', path = 'MenuBar/Tools', group='TVTKViewGroup' ) z_plus_view = Action( class_name = PKG + '.actions.ZPlusView', path = 'MenuBar/Tools', group='TVTKViewGroup' ) z_minus_view = Action( class_name = PKG + '.actions.ZMinusView', path = 'MenuBar/Tools', group='TVTKViewGroup' ) class SceneUIActionSet(ActionSet): """ The default action set for the scene UI plugin. """ groups = [scene_group, view_group] menus = [new_menu, save_scene_as_menu] actions = [ new_scene, # Save actions. save_scene, save_scene_to_png, save_scene_to_jpeg, save_scene_to_bmp, save_scene_to_tiff, save_scene_to_ps, save_scene_to_gl2ps, save_scene_to_rib, save_scene_to_oogl, save_scene_to_iv, save_scene_to_vrml, save_scene_to_obj, # Scene actions. reset_zoom, isometric_view, x_plus_view, x_minus_view, y_plus_view, y_minus_view, z_plus_view, z_minus_view ] #### EOF ###################################################################### mayavi-4.1.0/tvtk/plugins/scene/ui/scene_ui_plugin.py0000644000175100001440000000272311674464502023705 0ustar ischnellusers00000000000000""" A TVTK render window scene UI plugin. """ # Enthought library imports. from envisage.api import Plugin from traits.api import List class SceneUIPlugin(Plugin): """ A TVTK render window scene UI plugin. This is the plugin that contributes actions, menus, preferences pages etc. """ # Extension point Ids. ACTION_SETS = 'envisage.ui.workbench.action_sets' PREFERENCES_PAGES = 'envisage.ui.workbench.preferences_pages' #### 'IPlugin' interface ################################################## # The plugin's name (suitable for displaying to the user). name = 'TVTK Scene UI Plugin' # Our ID. id = 'tvtk.scene_ui' #### Extension points offered by this plugin ############################## # None. #### Contributions to extension points made by this plugin ################ action_sets = List(contributes_to=ACTION_SETS) def _action_sets_default(self): """ Trait initializer. """ from tvtk.plugins.scene.ui.scene_ui_action_set import ( SceneUIActionSet ) return [SceneUIActionSet] preferences_pages = List(contributes_to=PREFERENCES_PAGES) def _preferences_pages_default(self): """ Trait initializer. """ from tvtk.plugins.scene.ui.scene_preferences_page import ( ScenePreferencesPage ) return [ScenePreferencesPage] #### EOF ###################################################################### mayavi-4.1.0/tvtk/plugins/scene/ui/scene_preferences_page.py0000644000175100001440000000407011674464502025204 0ustar ischnellusers00000000000000""" Preferences page for a TVTK scene. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from apptools.preferences.ui.api import PreferencesPage from traits.api import Range from traitsui.api import View, Group, Item from tvtk.tvtk_base import false_bool_trait, vtk_color_trait class ScenePreferencesPage(PreferencesPage): """ Preferences page for a TVTK scene. """ #### 'PreferencesPage' interface ########################################## # The page's category (e.g. 'General/Appearance'). The empty string means # that this is a top-level page. category = '' # The page's help identifier (optional). If a help Id *is* provided then # there will be a 'Help' button shown on the preference page. help_id = '' # The page name (this is what is shown in the preferences dialog. name = 'TVTK Scene' # The path to the preferences node that contains the preferences. preferences_path = 'tvtk.scene' #### Preferences ########################################################## # Turn on/off stereo rendering. Note that this is useful only at startup # and not at runtime. stereo = false_bool_trait( desc='specifies if stereo rendering is turned on' ) # The magnification to use when dumping the screen to an image. magnification = Range( 1, 2048, 1, desc='specifies the magnification to use while generating images' ) # The background color of the renderer. background_color = vtk_color_trait((0.5, 0.5, 0.5)) # The foreground color of the renderer. foreground_color = vtk_color_trait((1.0, 1.0, 1.0)) #### Traits UI views ###################################################### traits_view = View( Group( Item(name='background_color'), Item(name='foreground_color'), Item(name='stereo'), Item(name='magnification') ) ) #### EOF ###################################################################### mayavi-4.1.0/tvtk/plugins/scene/i_scene_manager.py0000644000175100001440000000130211674464502023207 0ustar ischnellusers00000000000000""" The interface for TVTK scene managers. """ # Enthought library imports. from tvtk.pyface.tvtk_scene import TVTKScene from pyface.workbench.api import WorkbenchWindow from traits.api import Interface, List, Instance class ISceneManager(Interface): """ The interface for TVTK scene managerss. """ # The currently active scene (None, if no scene is active). current_scene = Instance(TVTKScene) # A list of all open scenes. scenes = List(TVTKScene) # The workbench window that the manager is in (there is one scene manager # per workbench window). window = Instance(WorkbenchWindow) #### EOF ###################################################################### mayavi-4.1.0/tvtk/plugins/scene/preferences.ini0000644000175100001440000000017011674464502022542 0ustar ischnellusers00000000000000[tvtk.scene] stereo = False magnification = 1 background_color = "(0.5, 0.5, 0.5)" foreground_color = "(1.0, 1.0, 1.0)" mayavi-4.1.0/tvtk/plugins/scene/scene_manager.py0000644000175100001440000000530411674464502022705 0ustar ischnellusers00000000000000""" Manage the TVTK scenes. """ # Enthought library imports. from tvtk.pyface.tvtk_scene import TVTKScene from pyface.workbench.api import WorkbenchWindow from traits.api import HasTraits, List, Instance, Property from traits.api import implements, on_trait_change from tvtk.plugins.scene.scene_editor import SceneEditor # Local imports. from i_scene_manager import ISceneManager class SceneManager(HasTraits): """ Manage the TVTK scenes. """ implements(ISceneManager) #### 'SceneManager' interface ############################################# # The currently active scene (None, if no scene is active). current_scene = Property(Instance(TVTKScene)) # A list of all open scenes. scenes = List(TVTKScene) # The workbench window that the manager is in (there is one scene manager # per workbench window). window = Instance(WorkbenchWindow) #### Private interface #################################################### # Shadow trait for the 'current_scene' property. _current_scene = Instance(TVTKScene) ########################################################################### # 'SceneManager' interface. ########################################################################### #### Trait properties ##################################################### def _get_current_scene(self): """ Property getter. """ scene_count = len(self.scenes) if scene_count == 0: scene = None elif scene_count == 1: scene = self.scenes[0] else: scene = self._current_scene return scene def _set_current_scene(self, scene): """ Property setter. """ self._current_scene = scene return #### Trait change handlers ################################################ @on_trait_change('window:editor_opened') def _on_editor_opened(self, obj, trait_name, old, new): """ Dynamic trait change handler. """ if isinstance(new, SceneEditor): self.scenes.append(new.scene) return @on_trait_change('window:editor_closing') def _on_editor_closed(self, obj, trait_name, old, new): """ Dynamic trait change handler. """ if isinstance(new, SceneEditor): self.scenes.remove(new.scene) return @on_trait_change('window:active_editor') def _on_active_editor_changed(self, obj, trait_name, old, new): """ Dynamic trait change handler. """ if isinstance(new, SceneEditor): self.current_scene = new.scene else: self.current_scene = None return #### EOF ###################################################################### mayavi-4.1.0/tvtk/plugins/scene/scene_editor.py0000644000175100001440000000646611674464502022573 0ustar ischnellusers00000000000000""" A TVTK scene editor. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from apptools.preferences.api import get_default_preferences from tvtk.pyface.tvtk_scene import TVTKScene from tvtk.pyface.api import DecoratedScene from pyface.workbench.api import Editor from traits.api import Instance #### Handy functions ########################################################## def _id_generator(): """ Return an ever-increasing number useful for creating unique Ids. """ n = 1 while True: yield(n) n += 1 _id_generator = _id_generator() class SceneEditor(Editor): """ A TVTK scene editor. """ #### 'SceneEditor' interface ############################################## # The TVTK scene object. scene = Instance(TVTKScene) ########################################################################### # 'IWorkbenchPart' interface. ########################################################################### #### Trait initializers ################################################### def _id_default(self): """ Trait initializer. """ return self.name def _name_default(self): """ Trait initializer. """ return 'TVTK Scene %d' % (_id_generator.next()) #### Methods ############################################################## def create_control(self, parent): """ Create the toolkit-specific control that represents the editor. """ # We hold a reference to the scene itself to make sure it does not get # garbage collected (because we only return the scene's 'control' not # the scene itself). The scene is also referenced by the scene manager. self.scene = self._create_decorated_scene(parent) self.scene.render() return self.scene.control def destroy_control(self): """ Destroy the toolkit-specific control that represents the editor. """ if self.scene is not None: # Close the scene to cleanly shut it down. self.scene.close() # Call the parent method. return super(SceneEditor, self).destroy_control() ########################################################################### # Private interface. ########################################################################### def _create_decorated_scene(self, parent): """ Create a new decorated scene. """ pref = get_default_preferences() stereo = eval(pref.get('tvtk.scene.stereo')) scene = DecoratedScene(parent, stereo=stereo) # Set the scene's traits to preference values. scene.magnification = \ eval(pref.get('tvtk.scene.magnification')) fg = eval(pref.get('tvtk.scene.foreground_color')) bg = eval(pref.get('tvtk.scene.background_color')) scene.foreground = fg scene.background = bg # FIXME: This seems necessary for some strange reason, if not # the actual background of the renderer never gets set even # though the renderer and the scene's background are synced. scene.renderer.background = scene.background return scene #### EOF ###################################################################### mayavi-4.1.0/tvtk/plugins/scene/__init__.py0000644000175100001440000000013711674464502021654 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/tvtk/plugins/scene/scene_plugin.py0000644000175100001440000000264111674464502022572 0ustar ischnellusers00000000000000""" The TVTK render window scene plugin. """ # Enthought library imports. from envisage.api import Plugin, ServiceOffer from traits.api import List # This module's package. PKG = '.'.join(__name__.split('.')[:-1]) class ScenePlugin(Plugin): """ The TVTK render window scene plugin. """ # Extension point Ids. PREFERENCES = 'envisage.preferences' SERVICE_OFFERS = 'envisage.ui.workbench.service_offers' #### 'IPlugin' interface ################################################## # The plugin's name (suitable for displaying to the user). name = 'TVTK Scene Plugin' # Our ID. id = 'tvtk.scene' #### Extension points offered by this plugin ############################## # None. #### Contributions to extension points made by this plugin ################ preferences = List(contributes_to=PREFERENCES) def _preferences_default(self): """ Trait initializer. """ return ['pkgfile://%s/preferences.ini' % PKG] service_offers = List(contributes_to=SERVICE_OFFERS) def _service_offers_default(self): """ Trait initializer. """ scene_manager_service_offer = ServiceOffer( protocol = PKG + '.i_scene_manager.ISceneManager', factory = PKG + '.scene_manager.SceneManager', ) return [scene_manager_service_offer] #### EOF ###################################################################### mayavi-4.1.0/tvtk/plugins/browser/0000755000175100001440000000000011674464502020130 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/plugins/browser/browser_view.py0000644000175100001440000000555311674464502023227 0ustar ischnellusers00000000000000""" The TVTK pipeline browser view. """ # Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. # Enthought library imports. from pyface.workbench.api import View from traits.api import Instance, on_trait_change class BrowserView(View): """ The TVTK pipeline browser view. """ #### 'IWorkbenchPart' interface ########################################### # The part's name (displayed to the user). name = 'TVTK Pipeline Browser' #### 'IView' interface #################################################### # The position of the view relative to the item specified in the # 'relative_to' trait. position = 'left' #### 'BrowserView' interface ############################################## # The pipeline browser instance that we are a view of. browser = Instance('tvtk.pipeline.browser.PipelineBrowser') # The scene manager. scene_manager = Instance( 'tvtk.plugins.scene.i_scene_manager.ISceneManager' ) ########################################################################### # 'IWorkbenchPart' interface. ########################################################################### def create_control(self, parent): """ Create the toolkit-specific control that represents the view. """ from tvtk.pipeline.browser import PipelineBrowser self.browser = PipelineBrowser() self.browser.show(parent=parent) return self.browser.ui.control ########################################################################### # Private interface. ########################################################################### #### Trait change handlers ################################################ @on_trait_change('scene_manager:scenes_items') def _on_scenes_changed(self, event): """ Dynamic trait change handler. This is called when scenes are added/removed from the scene manager, it is used to add and remove objects from the pipeline. """ # Scenes that were removed. map(self._remove_scene, event.removed) # Scenes that were added. map(self._add_scene, event.added) return #### Methods ############################################################## def _add_scene(self, scene): """ Add the specified scene to the pipeline browser. """ self.browser.renwins.append(scene) self.browser.root_object.append(scene.render_window) return def _remove_scene(self, scene): """ Remove the specified scene from the pipeline browser. """ if scene in self.browser.renwins: self.browser.renwins.remove(scene) self.browser.root_object.remove(scene.render_window) return #### EOF ###################################################################### mayavi-4.1.0/tvtk/plugins/browser/browser_plugin.py0000644000175100001440000000344711674464502023553 0ustar ischnellusers00000000000000""" The TVTK pipeline browser plugin. """ # Enthought library imports. from envisage.api import Plugin from traits.api import List class BrowserPlugin(Plugin): """ The TVTK pipeline browser plugin. """ # Extension point Ids. VIEWS = 'envisage.ui.workbench.views' #### 'IPlugin' interface ################################################## # The plugin's name (suitable for displaying to the user). name = 'TVTK Pipeline Browser' # Our ID. id = 'tvtk.browser' #### Extension points offered by this plugin ############################## # None. #### Contributions to extension points made by this plugin ################ views = List(contributes_to=VIEWS) def _views_default(self): """ Trait initializer. """ return [self._browser_view_factory] ########################################################################### # Private interface. ########################################################################### def _browser_view_factory(self, window, **traits): """ Factory method for browser views. """ from tvtk.plugins.browser.browser_view import ( BrowserView ) browser_view = BrowserView( scene_manager = self._get_scene_manager(window), window = window, **traits ) return browser_view def _get_scene_manager(self, window): """ Lookup the window's scene manager service. """ # Get the scene manager (a 'per window' service, so we look it up via # the window!). from tvtk.plugins.scene.i_scene_manager import ( ISceneManager ) return window.get_service(ISceneManager) #### EOF ###################################################################### mayavi-4.1.0/tvtk/plugins/browser/__init__.py0000644000175100001440000000013711674464502022242 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/tvtk/plugins/__init__.py0000644000175100001440000000013211674464502020552 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. mayavi-4.1.0/tvtk/__init__.py0000644000175100001440000000222111674464502017072 0ustar ischnellusers00000000000000# Author: Prabhu Ramachandran # License: BSD style # Copyright (c) 2004, Enthought, Inc. """ A Traits-based wrapper for the Visualization Toolkit. Part of the Mayavi project of the Enthought Tool Suite. """ from os.path import exists, join, dirname, isdir # The tvtk wrapper code is all typically inside one zip file. We try to # find this file and put it in __path__ and then create the 'tvtk' module # wrapper from that. If the ZIP file is extracted into a tvtk_classes # directory the ZIP file is not used and the tvtk_classes directory is # inserted into sys.path and the directory contents are used for the tvtk # classes -- note that you must have the following structure # tvtk_classes/tvtk_classes/__init__.py. This is handy for tools like # pydev (Eclipse). # We add the path to the local __path__ here, in the __init__, so that # the unpickler can directly unpickle the TVTK classes. _zip = join(dirname(__file__), 'tvtk_classes.zip') tvtk_class_dir = join(dirname(__file__), 'tvtk_classes') if exists(tvtk_class_dir) and isdir(tvtk_class_dir): # Nothing to do, it will imported anyhow. pass elif exists(_zip): __path__.append(_zip) mayavi-4.1.0/tvtk/vtk_module.py0000644000175100001440000000112611674464502017507 0ustar ischnellusers00000000000000"""Abstracts all VTK related modules into one module. This makes it trivial to support local VTK classes that a user may have built. By default it imports all of VTK and then looks for a tvtk_local module and imports everything from that. In order to add local classes to the TVTK build one may simply provide a tvtk_local.py module somewhere with any classes that need to be wrapped. """ # Author: Prabhu Ramachandran # Copyright (c) 2007, Enthought, Inc. # License: BSD Style. from vtk import * try: from tvtk_local import * except ImportError: pass mayavi-4.1.0/tvtk/tvtk_base_handler.py0000644000175100001440000000735011674464502021022 0ustar ischnellusers00000000000000""" Handler and UI elements for tvtk objects. """ # Author: Vibha Srinivasan # Copyright (c) 2008, Enthought, Inc. # License: BSD Style. from traits.api import HasTraits, Str, Instance, Property, Button, List, Enum from traitsui.api import Handler, UIInfo, TableEditor, View, Item from traitsui.table_column import ObjectColumn from traits.trait_base \ import user_name_for, xgetattr class TraitsViewObject(HasTraits): """ Wrapper for all items to be included in the full traits view of the TVTKBase object. """ # Trait name (name of the trait that is to be included in the view). name = Str # The TVTKBase object for which we are building a view. parent = Instance(HasTraits) class ValueColumn(ObjectColumn): """ Column to display the trait value for each trait of a tvtk object. """ def get_object ( self, object ): """ Returns the actual object being edited. Overridden here to return the tvtk object (which is the parent trait of object). """ return object.parent def target_name ( self, object ): """ Returns the target object and name for the column. Overridden here to return the trait name (which is the object's name trait) rather than the column's name trait. """ name = object.name object = self.get_object( object ) col = name.rfind( '.' ) if col < 0: return ( object, name ) return ( xgetattr( object, name[ :col ] ), name[ col + 1: ] ) def get_raw_value ( self, object ): """ Gets the unformatted value of the column for a specified object. Overridden here to return the trait name (which is the object's name trait) rather than the column's name trait. """ try: target, name = self.target_name( object ) return xgetattr( target, name ) except: return None """ A handler for the TVTKBase object. """ class TVTKBaseHandler(Handler): """ A handler for the TVTKBase object. """ # A reference to the UIInfo object. info = Instance(UIInfo) # Type of view (of info.object) to display. view_type = Enum(['Basic', 'Advanced']) # The view for the object (that is, info.object) view = Property(depends_on='view_type') # List of TraitsViewObject items, where each item contains information # about the trait to display as a row in a table editor. _full_traits_list = Property(List, editor = TableEditor(columns = [ObjectColumn(name='name'), ValueColumn(name='value')])) def init_info(self, info): """ Informs the handler what the UIInfo object for a View will be. Overridden here to save a reference to the info object. """ self.info = info return def _get__full_traits_list(self): """ Returns a list of objects to be included in the table editor for the full traits view. """ return [TraitsViewObject(name=name, parent = self.info.object) for name in self.info.object._full_traitnames_list_] def _get_view(self): """ Returns the view (for info.object) to be displayed in the InstanceEditor. """ if self.view_type == "Basic": view = self.info.object.trait_view('view') else: view = self.info.object.trait_view('full_traits_view') # This method is called when the default traits view for the object is # displayed. The default traits view already has a title, so do not # display a title for the contained view. view.title = '' return view #### EOF ################################################################### mayavi-4.1.0/tvtk/code_gen.py0000644000175100001440000001772711674464502017117 0ustar ischnellusers00000000000000"""This module generates tvtk (Traited VTK) classes from the VTK-Python API. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2007, Enthought, Inc. # License: BSD Style. import vtk import os import os.path import zipfile import tempfile import shutil import glob from optparse import OptionParser # Local imports -- these should be relative imports since these are # imported before the package is installed. from common import get_tvtk_name, camel2enthought from wrapper_gen import WrapperGenerator from special_gen import HelperGenerator ###################################################################### # `TVTKGenerator` ###################################################################### class TVTKGenerator: """Generates all the TVTK code.""" def __init__(self, out_dir=''): """Initializes the instance. Parameters ---------- - out_dir - `string` The output directory to generate code in. The directory is created if it does not exist. A directory called `tvtk_classes` is created inside this directory and all the code is written here. Any existing code there is blindly overwritten. If no out_dir is specified, a temporary one is created using `tempfile.mkdtemp`. """ if not out_dir: out_dir = tempfile.mkdtemp() self.out_dir = os.path.join(out_dir, 'tvtk_classes') if not os.path.exists(self.out_dir): os.makedirs(self.out_dir) self.zip_name = 'tvtk_classes.zip' self.wrap_gen = WrapperGenerator() self.helper_gen = HelperGenerator() ################################################################# # `TVTKGenerator` interface. ################################################################# def generate_code(self): """Generate all the wrapper code in `self.out_dir`. """ out_dir = self.out_dir helper_gen = self.helper_gen wrap_gen = self.wrap_gen # Create an __init__.py file f = open(os.path.join(out_dir, '__init__.py'), 'w') f.close() # Crete a vtk_version.py file that contains VTK build # information. v = vtk.vtkVersion() vtk_version = v.GetVTKVersion()[:3] vtk_src_version = v.GetVTKSourceVersion() code ="vtk_build_version = \'%s\'\n"%(vtk_version) code += "vtk_build_src_version = \'%s\'\n"%(vtk_src_version) f = open(os.path.join(out_dir, 'vtk_version.py'), 'w') f.write(code) f.close() # Write the helper code header. helper_file = open(os.path.join(out_dir, 'tvtk_helper.py'), 'w') helper_gen.write_prelims(helper_file) # Write the wrapper files. tree = wrap_gen.get_tree().tree #classes = dir(vtk) classes = [x.name for x in wrap_gen.get_tree() \ if x.name.startswith('vtk') and \ not issubclass(getattr(vtk, x.name), object) ] for nodes in tree: for node in nodes: if node.name in classes: tvtk_name = get_tvtk_name(node.name) self._write_wrapper_class(node, tvtk_name) helper_gen.add_class(tvtk_name, helper_file) helper_file.close() def write_wrapper_classes(self, names): """Given VTK class names in the list `names`, write out the wrapper classes to a suitable file. This is a convenience method so one can generate a just a few of the wrapper classes if desired. This is useful when debugging. Please note that the method also generates code for all the ancestors of the specified classes. """ # Wrappers for the ancesors are generated in order to get the # _updateable_traits_ information correctly. nodes = [] for name in names: node = self.wrap_gen.get_tree().get_node(name) if node is None: print 'ERROR: Cannot find class: %s'%name nodes.append(node) # Get ancestors. for node in nodes[:]: anc = node.get_ancestors() for i in anc: if i not in nodes: nodes.insert(0, i) # Sort them as per their level. nodes.sort(lambda x, y: cmp(x.level, y.level)) # Write code. for node in nodes: tvtk_name = get_tvtk_name(node.name) self._write_wrapper_class(node, tvtk_name) def build_zip(self, include_src=False): """Build the zip file (with name `self.zip_name`) in the current directory. Parameters ---------- - include_src : `bool` (default: False) If True, also includes all the ``*.py`` files in the ZIP file. By default only the ``*.pyc`` files are included. """ cwd = os.getcwd() d = os.path.dirname(self.out_dir) os.chdir(d) z = zipfile.PyZipFile(self.zip_name, 'w', zipfile.ZIP_DEFLATED) if include_src: l = glob.glob(os.path.join('tvtk_classes', '*.py')) for x in l: fname = os.path.basename(x) z.write(x, 'tvtk_classes/%s'%fname) z.writepy('tvtk_classes') z.close() if os.path.exists(cwd + "/" + self.zip_name): os.unlink(cwd + "/" + self.zip_name) shutil.move(self.zip_name, cwd) os.chdir(cwd) def clean(self): """Delete the temporary directory where the code has been generated. """ tmp_dir = os.path.dirname(self.out_dir) d = os.listdir(tmp_dir) ok = 0 if len(d) == 1 and d[0] == 'tvtk_classes': ok = 1 if ok: shutil.rmtree(tmp_dir) else: print "Not removing directory:", tmp_dir print "It does not contain a tvtk_classes directory!" ################################################################# # Non-public interface. ################################################################# def _write_wrapper_class(self, node, tvtk_name): """Write the wrapper code to a file.""" # The only reason this method is separate is to generate code # for an individual class when debugging. fname = camel2enthought(tvtk_name) + '.py' out = open(os.path.join(self.out_dir, fname), 'w') self.wrap_gen.generate_code(node, out) out.close() ###################################################################### # Utility functions. ###################################################################### def main(): usage = """usage: %prog [options] [vtk_classes] The options are described below. An optional list of VTK classes for which code is to be generated may be specified. If none are specified code will be generated for all the VTK classes. """ parser = OptionParser(usage) parser.add_option("-o", "--output-dir", action="store", type="string", dest="out_dir", default='', help="Output directory in which to generate code.") parser.add_option("-n", "--no-clean", action="store_false", dest="clean", default=True, help="Do not clean the temporary directory.") parser.add_option("-z", "--no-zipfile", action="store_false", dest="zip", default=True, help="Do not create a ZIP file.") parser.add_option("-s", "--source", action="store_true", dest="src", default=False, help="Include source files (*.py) in addition to *.pyc files in the ZIP file.") (options, args) = parser.parse_args() # Now do stuff. gen = TVTKGenerator(options.out_dir) if len(args) == 0: gen.generate_code() else: gen.write_wrapper_classes(args) if options.zip: gen.build_zip(options.src) if options.clean: gen.clean() if __name__ == '__main__': main() mayavi-4.1.0/tvtk/view/0000755000175100001440000000000011674464502015736 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/view/__init__.py0000644000175100001440000000000011674464502020035 0ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/view/parametric_function_source_view.py0000644000175100001440000000073411674464502024762 0ustar ischnellusers00000000000000from traitsui.api import View, HGroup, Item from tvtk.tvtk_base import TVTKBaseHandler view = View((['generate_texture_coordinates'], ['scalar_mode'], HGroup(Item('u_resolution', label = 'u'), Item('v_resolution', label = 'v'), Item('w_resolution', label = 'w'), label = 'Resolution', show_border = True)), handler = TVTKBaseHandler, title='Edit ParametricFunctionSource properties', scrollable=True, buttons=['OK', 'Cancel']) mayavi-4.1.0/tvtk/src/0000755000175100001440000000000011674464502015553 5ustar ischnellusers00000000000000mayavi-4.1.0/tvtk/src/array_ext.pyx0000644000175100001440000001136311674464502020317 0ustar ischnellusers00000000000000""" A Pyrex extension module for numpy. Currently this extension module allows us to massage a 2D scipy array into a form usable as a `vtkIdTypeArray`. This is then used to set the cells of a `vtkCellArray` instance. """ # Author: Prabhu Ramachandran # Copyright (c) 2005-2007, Enthought, Inc. # License: BSD Style. import numpy ###################################################################### # External declarations. ###################################################################### # Expose various external interfaces needed subsequently. cdef extern from "numpy/arrayobject.h": ctypedef int intp struct PyArray_Descr: int type_num, elsize char type ctypedef extern class numpy.ndarray [object PyArrayObject]: cdef char *data cdef int nd cdef intp *dimensions cdef intp *strides cdef object base cdef PyArray_Descr *descr cdef int flags void import_array() import_array() ###################################################################### # Internal C functions. ###################################################################### cdef c_set_id_type_array(ndarray id_array, ndarray out_array): # This function sets the data of the passed 2D `id_array` into the # passed out_array such that out_array can be used as a # `vtkIdTypeArray`. # # `id_array` need not be contiguous. # # No type or size checking is done here. All that is done in the # Python function upstream that calls this. cdef int cell_length, dim0 cdef int *id_data cdef int *out_data cdef int stride0, stride1 cell_length = id_array.dimensions[1]; dim0 = id_array.dimensions[0] id_data = id_array.data out_data = out_array.data stride0 = id_array.strides[0]/sizeof(int) stride1 = id_array.strides[1]/sizeof(int) cdef int i, j, in_idx, out_idx for i from 0 <= i < dim0: in_idx = i*stride0 out_idx = i*cell_length + i out_data[out_idx] = cell_length for j from 0 <= j < cell_length: out_data[out_idx + j + 1] = id_data[in_idx + j*stride1] cdef c_set_id_type_array_long(ndarray id_array, ndarray out_array): # This function sets the data of the passed 2D `id_array` into the # passed out_array such that out_array can be used as a # `vtkIdTypeArray`. # # `id_array` need not be contiguous. # # No type or size checking is done here. All that is done in the # Python function upstream that calls this. cdef Py_ssize_t cell_length, dim0 cdef Py_ssize_t *id_data cdef Py_ssize_t *out_data cdef Py_ssize_t stride0, stride1 cell_length = id_array.dimensions[1]; dim0 = id_array.dimensions[0] id_data = id_array.data out_data = out_array.data stride0 = id_array.strides[0]/sizeof(Py_ssize_t) stride1 = id_array.strides[1]/sizeof(Py_ssize_t) cdef int i, j, in_idx, out_idx for i from 0 <= i < dim0: in_idx = i*stride0 out_idx = i*cell_length + i out_data[out_idx] = cell_length for j from 0 <= j < cell_length: out_data[out_idx + j + 1] = id_data[in_idx + j*stride1] ###################################################################### # Exported (externally visible) functions. ###################################################################### def set_id_type_array(id_array, out_array): """Given a 2D Int array (`id_array`), and a contiguous 1D numarray array (`out_array`) having the correct size, this function sets the data from `id_array` into `out_array` so that it can be used in place of a `vtkIdTypeArray` in order to set the cells of a `vtkCellArray`. Note that if `shape = id_array.shape` then `size(out_array) == shape[0]*(shape[1] + 1)` should be true. If not you'll get an `AssertionError`. `id_array` need not be contiguous but `out_array` must be. """ import vtk VTK_ID_TYPE_SIZE = vtk.vtkIdTypeArray().GetDataTypeSize() assert numpy.issubdtype(id_array.dtype, numpy.signedinteger) and \ id_array.dtype.itemsize == VTK_ID_TYPE_SIZE assert out_array.flags.contiguous == 1, \ "out_array must be contiguous." shp = id_array.shape assert len(shp) == 2, "id_array must be a two dimensional array." sz = numpy.size(out_array) e_sz = shp[0]*(shp[1]+1) assert sz == e_sz, \ "out_array size is incorrect, expected: %s, given: %s"%(e_sz, sz) if VTK_ID_TYPE_SIZE == 4: c_set_id_type_array(id_array, out_array) elif VTK_ID_TYPE_SIZE == 8: c_set_id_type_array_long(id_array, out_array) else: raise ValueError('Unsupported VTK_ID_TYPE_SIZE=%d'\ %VTK_ID_TYPE_SIZE) mayavi-4.1.0/tvtk/src/array_ext.c0000644000175100001440000027544011674464502017731 0ustar ischnellusers00000000000000/* Generated by Cython 0.14.1 on Sun Feb 13 21:50:50 2011 */ #define PY_SSIZE_T_CLEAN #include "Python.h" #ifndef Py_PYTHON_H #error Python headers needed to compile C extensions, please install development version of Python. #else #include /* For offsetof */ #ifndef offsetof #define offsetof(type, member) ( (size_t) & ((type*)0) -> member ) #endif #if !defined(WIN32) && !defined(MS_WINDOWS) #ifndef __stdcall #define __stdcall #endif #ifndef __cdecl #define __cdecl #endif #ifndef __fastcall #define __fastcall #endif #endif #ifndef DL_IMPORT #define DL_IMPORT(t) t #endif #ifndef DL_EXPORT #define DL_EXPORT(t) t #endif #ifndef PY_LONG_LONG #define PY_LONG_LONG LONG_LONG #endif #if PY_VERSION_HEX < 0x02040000 #define METH_COEXIST 0 #define PyDict_CheckExact(op) (Py_TYPE(op) == &PyDict_Type) #define PyDict_Contains(d,o) PySequence_Contains(d,o) #endif #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN #define PY_FORMAT_SIZE_T "" #define PyInt_FromSsize_t(z) PyInt_FromLong(z) #define PyInt_AsSsize_t(o) PyInt_AsLong(o) #define PyNumber_Index(o) PyNumber_Int(o) #define PyIndex_Check(o) PyNumber_Check(o) #define PyErr_WarnEx(category, message, stacklevel) PyErr_Warn(category, message) #endif #if PY_VERSION_HEX < 0x02060000 #define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt) #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) #define PyVarObject_HEAD_INIT(type, size) \ PyObject_HEAD_INIT(type) size, #define PyType_Modified(t) typedef struct { void *buf; PyObject *obj; Py_ssize_t len; Py_ssize_t itemsize; int readonly; int ndim; char *format; Py_ssize_t *shape; Py_ssize_t *strides; Py_ssize_t *suboffsets; void *internal; } Py_buffer; #define PyBUF_SIMPLE 0 #define PyBUF_WRITABLE 0x0001 #define PyBUF_FORMAT 0x0004 #define PyBUF_ND 0x0008 #define PyBUF_STRIDES (0x0010 | PyBUF_ND) #define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) #define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) #define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) #define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) #endif #if PY_MAJOR_VERSION < 3 #define __Pyx_BUILTIN_MODULE_NAME "__builtin__" #else #define __Pyx_BUILTIN_MODULE_NAME "builtins" #endif #if PY_MAJOR_VERSION >= 3 #define Py_TPFLAGS_CHECKTYPES 0 #define Py_TPFLAGS_HAVE_INDEX 0 #endif #if (PY_VERSION_HEX < 0x02060000) || (PY_MAJOR_VERSION >= 3) #define Py_TPFLAGS_HAVE_NEWBUFFER 0 #endif #if PY_MAJOR_VERSION >= 3 #define PyBaseString_Type PyUnicode_Type #define PyStringObject PyUnicodeObject #define PyString_Type PyUnicode_Type #define PyString_Check PyUnicode_Check #define PyString_CheckExact PyUnicode_CheckExact #endif #if PY_VERSION_HEX < 0x02060000 #define PyBytesObject PyStringObject #define PyBytes_Type PyString_Type #define PyBytes_Check PyString_Check #define PyBytes_CheckExact PyString_CheckExact #define PyBytes_FromString PyString_FromString #define PyBytes_FromStringAndSize PyString_FromStringAndSize #define PyBytes_FromFormat PyString_FromFormat #define PyBytes_DecodeEscape PyString_DecodeEscape #define PyBytes_AsString PyString_AsString #define PyBytes_AsStringAndSize PyString_AsStringAndSize #define PyBytes_Size PyString_Size #define PyBytes_AS_STRING PyString_AS_STRING #define PyBytes_GET_SIZE PyString_GET_SIZE #define PyBytes_Repr PyString_Repr #define PyBytes_Concat PyString_Concat #define PyBytes_ConcatAndDel PyString_ConcatAndDel #endif #if PY_VERSION_HEX < 0x02060000 #define PySet_Check(obj) PyObject_TypeCheck(obj, &PySet_Type) #define PyFrozenSet_Check(obj) PyObject_TypeCheck(obj, &PyFrozenSet_Type) #endif #ifndef PySet_CheckExact #define PySet_CheckExact(obj) (Py_TYPE(obj) == &PySet_Type) #endif #define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type) #if PY_MAJOR_VERSION >= 3 #define PyIntObject PyLongObject #define PyInt_Type PyLong_Type #define PyInt_Check(op) PyLong_Check(op) #define PyInt_CheckExact(op) PyLong_CheckExact(op) #define PyInt_FromString PyLong_FromString #define PyInt_FromUnicode PyLong_FromUnicode #define PyInt_FromLong PyLong_FromLong #define PyInt_FromSize_t PyLong_FromSize_t #define PyInt_FromSsize_t PyLong_FromSsize_t #define PyInt_AsLong PyLong_AsLong #define PyInt_AS_LONG PyLong_AS_LONG #define PyInt_AsSsize_t PyLong_AsSsize_t #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask #endif #if PY_MAJOR_VERSION >= 3 #define PyBoolObject PyLongObject #endif #if PY_MAJOR_VERSION >= 3 #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y) #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y) #else #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y) #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceDivide(x,y) #endif #if (PY_MAJOR_VERSION < 3) || (PY_VERSION_HEX >= 0x03010300) #define __Pyx_PySequence_GetSlice(obj, a, b) PySequence_GetSlice(obj, a, b) #define __Pyx_PySequence_SetSlice(obj, a, b, value) PySequence_SetSlice(obj, a, b, value) #define __Pyx_PySequence_DelSlice(obj, a, b) PySequence_DelSlice(obj, a, b) #else #define __Pyx_PySequence_GetSlice(obj, a, b) (unlikely(!(obj)) ? \ (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), (PyObject*)0) : \ (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_GetSlice(obj, a, b)) : \ (PyErr_Format(PyExc_TypeError, "'%.200s' object is unsliceable", (obj)->ob_type->tp_name), (PyObject*)0))) #define __Pyx_PySequence_SetSlice(obj, a, b, value) (unlikely(!(obj)) ? \ (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), -1) : \ (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_SetSlice(obj, a, b, value)) : \ (PyErr_Format(PyExc_TypeError, "'%.200s' object doesn't support slice assignment", (obj)->ob_type->tp_name), -1))) #define __Pyx_PySequence_DelSlice(obj, a, b) (unlikely(!(obj)) ? \ (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), -1) : \ (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_DelSlice(obj, a, b)) : \ (PyErr_Format(PyExc_TypeError, "'%.200s' object doesn't support slice deletion", (obj)->ob_type->tp_name), -1))) #endif #if PY_MAJOR_VERSION >= 3 #define PyMethod_New(func, self, klass) ((self) ? PyMethod_New(func, self) : PyInstanceMethod_New(func)) #endif #if PY_VERSION_HEX < 0x02050000 #define __Pyx_GetAttrString(o,n) PyObject_GetAttrString((o),((char *)(n))) #define __Pyx_SetAttrString(o,n,a) PyObject_SetAttrString((o),((char *)(n)),(a)) #define __Pyx_DelAttrString(o,n) PyObject_DelAttrString((o),((char *)(n))) #else #define __Pyx_GetAttrString(o,n) PyObject_GetAttrString((o),(n)) #define __Pyx_SetAttrString(o,n,a) PyObject_SetAttrString((o),(n),(a)) #define __Pyx_DelAttrString(o,n) PyObject_DelAttrString((o),(n)) #endif #if PY_VERSION_HEX < 0x02050000 #define __Pyx_NAMESTR(n) ((char *)(n)) #define __Pyx_DOCSTR(n) ((char *)(n)) #else #define __Pyx_NAMESTR(n) (n) #define __Pyx_DOCSTR(n) (n) #endif #ifdef __cplusplus #define __PYX_EXTERN_C extern "C" #else #define __PYX_EXTERN_C extern #endif #if defined(WIN32) || defined(MS_WINDOWS) #define _USE_MATH_DEFINES #endif #include #define __PYX_HAVE_API__array_ext #include "numpy/arrayobject.h" #ifdef PYREX_WITHOUT_ASSERTIONS #define CYTHON_WITHOUT_ASSERTIONS #endif /* inline attribute */ #ifndef CYTHON_INLINE #if defined(__GNUC__) #define CYTHON_INLINE __inline__ #elif defined(_MSC_VER) #define CYTHON_INLINE __inline #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define CYTHON_INLINE inline #else #define CYTHON_INLINE #endif #endif /* unused attribute */ #ifndef CYTHON_UNUSED # if defined(__GNUC__) # if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) # define CYTHON_UNUSED __attribute__ ((__unused__)) # else # define CYTHON_UNUSED # endif # elif defined(__ICC) || defined(__INTEL_COMPILER) # define CYTHON_UNUSED __attribute__ ((__unused__)) # else # define CYTHON_UNUSED # endif #endif typedef struct {PyObject **p; char *s; const long n; const char* encoding; const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; /*proto*/ /* Type Conversion Predeclarations */ #define __Pyx_PyBytes_FromUString(s) PyBytes_FromString((char*)s) #define __Pyx_PyBytes_AsUString(s) ((unsigned char*) PyBytes_AsString(s)) #define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False)) static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x); static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*); static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t); static CYTHON_INLINE size_t __Pyx_PyInt_AsSize_t(PyObject*); #define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) #ifdef __GNUC__ /* Test for GCC > 2.95 */ #if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #else /* __GNUC__ > 2 ... */ #define likely(x) (x) #define unlikely(x) (x) #endif /* __GNUC__ > 2 ... */ #else /* __GNUC__ */ #define likely(x) (x) #define unlikely(x) (x) #endif /* __GNUC__ */ static PyObject *__pyx_m; static PyObject *__pyx_b; static PyObject *__pyx_empty_tuple; static PyObject *__pyx_empty_bytes; static int __pyx_lineno; static int __pyx_clineno = 0; static const char * __pyx_cfilenm= __FILE__; static const char *__pyx_filename; static const char *__pyx_f[] = { "array_ext.pyx", }; /* Type declarations */ #ifndef CYTHON_REFNANNY #define CYTHON_REFNANNY 0 #endif #if CYTHON_REFNANNY typedef struct { void (*INCREF)(void*, PyObject*, int); void (*DECREF)(void*, PyObject*, int); void (*GOTREF)(void*, PyObject*, int); void (*GIVEREF)(void*, PyObject*, int); void* (*SetupContext)(const char*, int, const char*); void (*FinishContext)(void**); } __Pyx_RefNannyAPIStruct; static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL; static __Pyx_RefNannyAPIStruct * __Pyx_RefNannyImportAPI(const char *modname) { PyObject *m = NULL, *p = NULL; void *r = NULL; m = PyImport_ImportModule((char *)modname); if (!m) goto end; p = PyObject_GetAttrString(m, (char *)"RefNannyAPI"); if (!p) goto end; r = PyLong_AsVoidPtr(p); end: Py_XDECREF(p); Py_XDECREF(m); return (__Pyx_RefNannyAPIStruct *)r; } #define __Pyx_RefNannySetupContext(name) void *__pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__) #define __Pyx_RefNannyFinishContext() __Pyx_RefNanny->FinishContext(&__pyx_refnanny) #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), __LINE__) #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), __LINE__) #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), __LINE__) #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), __LINE__) #define __Pyx_XDECREF(r) do { if((r) != NULL) {__Pyx_DECREF(r);} } while(0) #else #define __Pyx_RefNannySetupContext(name) #define __Pyx_RefNannyFinishContext() #define __Pyx_INCREF(r) Py_INCREF(r) #define __Pyx_DECREF(r) Py_DECREF(r) #define __Pyx_GOTREF(r) #define __Pyx_GIVEREF(r) #define __Pyx_XDECREF(r) Py_XDECREF(r) #endif /* CYTHON_REFNANNY */ #define __Pyx_XGIVEREF(r) do { if((r) != NULL) {__Pyx_GIVEREF(r);} } while(0) #define __Pyx_XGOTREF(r) do { if((r) != NULL) {__Pyx_GOTREF(r);} } while(0) static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name); /*proto*/ static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact, Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); /*proto*/ static void __Pyx_RaiseDoubleKeywordsError( const char* func_name, PyObject* kw_name); /*proto*/ static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject **argnames[], PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, const char* function_name); /*proto*/ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) { PyObject *r; if (!j) return NULL; r = PyObject_GetItem(o, j); Py_DECREF(j); return r; } #define __Pyx_GetItemInt_List(o, i, size, to_py_func) (((size) <= sizeof(Py_ssize_t)) ? \ __Pyx_GetItemInt_List_Fast(o, i) : \ __Pyx_GetItemInt_Generic(o, to_py_func(i))) static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i) { if (likely(o != Py_None)) { if (likely((0 <= i) & (i < PyList_GET_SIZE(o)))) { PyObject *r = PyList_GET_ITEM(o, i); Py_INCREF(r); return r; } else if ((-PyList_GET_SIZE(o) <= i) & (i < 0)) { PyObject *r = PyList_GET_ITEM(o, PyList_GET_SIZE(o) + i); Py_INCREF(r); return r; } } return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); } #define __Pyx_GetItemInt_Tuple(o, i, size, to_py_func) (((size) <= sizeof(Py_ssize_t)) ? \ __Pyx_GetItemInt_Tuple_Fast(o, i) : \ __Pyx_GetItemInt_Generic(o, to_py_func(i))) static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i) { if (likely(o != Py_None)) { if (likely((0 <= i) & (i < PyTuple_GET_SIZE(o)))) { PyObject *r = PyTuple_GET_ITEM(o, i); Py_INCREF(r); return r; } else if ((-PyTuple_GET_SIZE(o) <= i) & (i < 0)) { PyObject *r = PyTuple_GET_ITEM(o, PyTuple_GET_SIZE(o) + i); Py_INCREF(r); return r; } } return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); } #define __Pyx_GetItemInt(o, i, size, to_py_func) (((size) <= sizeof(Py_ssize_t)) ? \ __Pyx_GetItemInt_Fast(o, i) : \ __Pyx_GetItemInt_Generic(o, to_py_func(i))) static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i) { PyObject *r; if (PyList_CheckExact(o) && ((0 <= i) & (i < PyList_GET_SIZE(o)))) { r = PyList_GET_ITEM(o, i); Py_INCREF(r); } else if (PyTuple_CheckExact(o) && ((0 <= i) & (i < PyTuple_GET_SIZE(o)))) { r = PyTuple_GET_ITEM(o, i); Py_INCREF(r); } else if (Py_TYPE(o)->tp_as_sequence && Py_TYPE(o)->tp_as_sequence->sq_item && (likely(i >= 0))) { r = PySequence_GetItem(o, i); } else { r = __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); } return r; } static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/ static CYTHON_INLINE void __Pyx_ErrRestore(PyObject *type, PyObject *value, PyObject *tb); /*proto*/ static CYTHON_INLINE void __Pyx_ErrFetch(PyObject **type, PyObject **value, PyObject **tb); /*proto*/ static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb); /*proto*/ static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); /*proto*/ static CYTHON_INLINE unsigned char __Pyx_PyInt_AsUnsignedChar(PyObject *); static CYTHON_INLINE unsigned short __Pyx_PyInt_AsUnsignedShort(PyObject *); static CYTHON_INLINE unsigned int __Pyx_PyInt_AsUnsignedInt(PyObject *); static CYTHON_INLINE char __Pyx_PyInt_AsChar(PyObject *); static CYTHON_INLINE short __Pyx_PyInt_AsShort(PyObject *); static CYTHON_INLINE int __Pyx_PyInt_AsInt(PyObject *); static CYTHON_INLINE signed char __Pyx_PyInt_AsSignedChar(PyObject *); static CYTHON_INLINE signed short __Pyx_PyInt_AsSignedShort(PyObject *); static CYTHON_INLINE signed int __Pyx_PyInt_AsSignedInt(PyObject *); static CYTHON_INLINE int __Pyx_PyInt_AsLongDouble(PyObject *); static CYTHON_INLINE unsigned long __Pyx_PyInt_AsUnsignedLong(PyObject *); static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_PyInt_AsUnsignedLongLong(PyObject *); static CYTHON_INLINE long __Pyx_PyInt_AsLong(PyObject *); static CYTHON_INLINE PY_LONG_LONG __Pyx_PyInt_AsLongLong(PyObject *); static CYTHON_INLINE signed long __Pyx_PyInt_AsSignedLong(PyObject *); static CYTHON_INLINE signed PY_LONG_LONG __Pyx_PyInt_AsSignedLongLong(PyObject *); static PyTypeObject *__Pyx_ImportType(const char *module_name, const char *class_name, long size, int strict); /*proto*/ static PyObject *__Pyx_ImportModule(const char *name); /*proto*/ static void __Pyx_AddTraceback(const char *funcname); /*proto*/ static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/ /* Module declarations from numpy */ /* Module declarations from array_ext */ static PyTypeObject *__pyx_ptype_9array_ext_ndarray = 0; static PyObject *__pyx_f_9array_ext_c_set_id_type_array(PyArrayObject *, PyArrayObject *); /*proto*/ static PyObject *__pyx_f_9array_ext_c_set_id_type_array_long(PyArrayObject *, PyArrayObject *); /*proto*/ #define __Pyx_MODULE_NAME "array_ext" static int __pyx_module_is_main_array_ext = 0; /* Implementation of array_ext */ static PyObject *__pyx_builtin_ValueError; static char __pyx_k_1[] = "out_array must be contiguous."; static char __pyx_k_2[] = "id_array must be a two dimensional array."; static char __pyx_k_3[] = "out_array size is incorrect, expected: %s, given: %s"; static char __pyx_k_4[] = "Unsupported VTK_ID_TYPE_SIZE=%d"; static char __pyx_k_5[] = "\nA Pyrex extension module for numpy. Currently this extension module\nallows us to massage a 2D scipy array into a form usable as a\n`vtkIdTypeArray`. This is then used to set the cells of a\n`vtkCellArray` instance.\n"; static char __pyx_k__vtk[] = "vtk"; static char __pyx_k__data[] = "data"; static char __pyx_k__size[] = "size"; static char __pyx_k__dtype[] = "dtype"; static char __pyx_k__flags[] = "flags"; static char __pyx_k__numpy[] = "numpy"; static char __pyx_k__shape[] = "shape"; static char __pyx_k__strides[] = "strides"; static char __pyx_k____main__[] = "__main__"; static char __pyx_k____test__[] = "__test__"; static char __pyx_k__id_array[] = "id_array"; static char __pyx_k__itemsize[] = "itemsize"; static char __pyx_k__array_ext[] = "array_ext"; static char __pyx_k__out_array[] = "out_array"; static char __pyx_k__ValueError[] = "ValueError"; static char __pyx_k__contiguous[] = "contiguous"; static char __pyx_k__dimensions[] = "dimensions"; static char __pyx_k__issubdtype[] = "issubdtype"; static char __pyx_k__signedinteger[] = "signedinteger"; static char __pyx_k__vtkIdTypeArray[] = "vtkIdTypeArray"; static char __pyx_k__GetDataTypeSize[] = "GetDataTypeSize"; static char __pyx_k__set_id_type_array[] = "set_id_type_array"; static PyObject *__pyx_kp_s_1; static PyObject *__pyx_kp_s_2; static PyObject *__pyx_kp_s_3; static PyObject *__pyx_kp_s_4; static PyObject *__pyx_n_s__GetDataTypeSize; static PyObject *__pyx_n_s__ValueError; static PyObject *__pyx_n_s____main__; static PyObject *__pyx_n_s____test__; static PyObject *__pyx_n_s__array_ext; static PyObject *__pyx_n_s__contiguous; static PyObject *__pyx_n_s__data; static PyObject *__pyx_n_s__dimensions; static PyObject *__pyx_n_s__dtype; static PyObject *__pyx_n_s__flags; static PyObject *__pyx_n_s__id_array; static PyObject *__pyx_n_s__issubdtype; static PyObject *__pyx_n_s__itemsize; static PyObject *__pyx_n_s__numpy; static PyObject *__pyx_n_s__out_array; static PyObject *__pyx_n_s__set_id_type_array; static PyObject *__pyx_n_s__shape; static PyObject *__pyx_n_s__signedinteger; static PyObject *__pyx_n_s__size; static PyObject *__pyx_n_s__strides; static PyObject *__pyx_n_s__vtk; static PyObject *__pyx_n_s__vtkIdTypeArray; static PyObject *__pyx_int_1; static PyObject *__pyx_int_4; static PyObject *__pyx_int_8; /* "array_ext.pyx":43 * ###################################################################### * * cdef c_set_id_type_array(ndarray id_array, ndarray out_array): # <<<<<<<<<<<<<< * # This function sets the data of the passed 2D `id_array` into the * # passed out_array such that out_array can be used as a */ static PyObject *__pyx_f_9array_ext_c_set_id_type_array(PyArrayObject *__pyx_v_id_array, PyArrayObject *__pyx_v_out_array) { int __pyx_v_cell_length; int __pyx_v_dim0; int *__pyx_v_id_data; int *__pyx_v_out_data; int __pyx_v_stride0; int __pyx_v_stride1; int __pyx_v_i; int __pyx_v_j; int __pyx_v_in_idx; int __pyx_v_out_idx; PyObject *__pyx_r = NULL; size_t __pyx_t_1; int __pyx_t_2; int __pyx_t_3; __Pyx_RefNannySetupContext("c_set_id_type_array"); /* "array_ext.pyx":58 * cdef int stride0, stride1 * * cell_length = id_array.dimensions[1]; # <<<<<<<<<<<<<< * dim0 = id_array.dimensions[0] * id_data = id_array.data */ __pyx_v_cell_length = (__pyx_v_id_array->dimensions[1]); /* "array_ext.pyx":59 * * cell_length = id_array.dimensions[1]; * dim0 = id_array.dimensions[0] # <<<<<<<<<<<<<< * id_data = id_array.data * out_data = out_array.data */ __pyx_v_dim0 = (__pyx_v_id_array->dimensions[0]); /* "array_ext.pyx":60 * cell_length = id_array.dimensions[1]; * dim0 = id_array.dimensions[0] * id_data = id_array.data # <<<<<<<<<<<<<< * out_data = out_array.data * stride0 = id_array.strides[0]/sizeof(int) */ __pyx_v_id_data = ((int *)__pyx_v_id_array->data); /* "array_ext.pyx":61 * dim0 = id_array.dimensions[0] * id_data = id_array.data * out_data = out_array.data # <<<<<<<<<<<<<< * stride0 = id_array.strides[0]/sizeof(int) * stride1 = id_array.strides[1]/sizeof(int) */ __pyx_v_out_data = ((int *)__pyx_v_out_array->data); /* "array_ext.pyx":62 * id_data = id_array.data * out_data = out_array.data * stride0 = id_array.strides[0]/sizeof(int) # <<<<<<<<<<<<<< * stride1 = id_array.strides[1]/sizeof(int) * */ __pyx_t_1 = (sizeof(int)); if (unlikely(__pyx_t_1 == 0)) { PyErr_Format(PyExc_ZeroDivisionError, "integer division or modulo by zero"); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 62; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_v_stride0 = ((__pyx_v_id_array->strides[0]) / __pyx_t_1); /* "array_ext.pyx":63 * out_data = out_array.data * stride0 = id_array.strides[0]/sizeof(int) * stride1 = id_array.strides[1]/sizeof(int) # <<<<<<<<<<<<<< * * cdef int i, j, in_idx, out_idx */ __pyx_t_1 = (sizeof(int)); if (unlikely(__pyx_t_1 == 0)) { PyErr_Format(PyExc_ZeroDivisionError, "integer division or modulo by zero"); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_v_stride1 = ((__pyx_v_id_array->strides[1]) / __pyx_t_1); /* "array_ext.pyx":66 * * cdef int i, j, in_idx, out_idx * for i from 0 <= i < dim0: # <<<<<<<<<<<<<< * in_idx = i*stride0 * out_idx = i*cell_length + i */ __pyx_t_2 = __pyx_v_dim0; for (__pyx_v_i = 0; __pyx_v_i < __pyx_t_2; __pyx_v_i++) { /* "array_ext.pyx":67 * cdef int i, j, in_idx, out_idx * for i from 0 <= i < dim0: * in_idx = i*stride0 # <<<<<<<<<<<<<< * out_idx = i*cell_length + i * out_data[out_idx] = cell_length */ __pyx_v_in_idx = (__pyx_v_i * __pyx_v_stride0); /* "array_ext.pyx":68 * for i from 0 <= i < dim0: * in_idx = i*stride0 * out_idx = i*cell_length + i # <<<<<<<<<<<<<< * out_data[out_idx] = cell_length * for j from 0 <= j < cell_length: */ __pyx_v_out_idx = ((__pyx_v_i * __pyx_v_cell_length) + __pyx_v_i); /* "array_ext.pyx":69 * in_idx = i*stride0 * out_idx = i*cell_length + i * out_data[out_idx] = cell_length # <<<<<<<<<<<<<< * for j from 0 <= j < cell_length: * out_data[out_idx + j + 1] = id_data[in_idx + j*stride1] */ (__pyx_v_out_data[__pyx_v_out_idx]) = __pyx_v_cell_length; /* "array_ext.pyx":70 * out_idx = i*cell_length + i * out_data[out_idx] = cell_length * for j from 0 <= j < cell_length: # <<<<<<<<<<<<<< * out_data[out_idx + j + 1] = id_data[in_idx + j*stride1] * */ __pyx_t_3 = __pyx_v_cell_length; for (__pyx_v_j = 0; __pyx_v_j < __pyx_t_3; __pyx_v_j++) { /* "array_ext.pyx":71 * out_data[out_idx] = cell_length * for j from 0 <= j < cell_length: * out_data[out_idx + j + 1] = id_data[in_idx + j*stride1] # <<<<<<<<<<<<<< * * cdef c_set_id_type_array_long(ndarray id_array, ndarray out_array): */ (__pyx_v_out_data[((__pyx_v_out_idx + __pyx_v_j) + 1)]) = (__pyx_v_id_data[(__pyx_v_in_idx + (__pyx_v_j * __pyx_v_stride1))]); } } __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_AddTraceback("array_ext.c_set_id_type_array"); __pyx_r = 0; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "array_ext.pyx":73 * out_data[out_idx + j + 1] = id_data[in_idx + j*stride1] * * cdef c_set_id_type_array_long(ndarray id_array, ndarray out_array): # <<<<<<<<<<<<<< * # This function sets the data of the passed 2D `id_array` into the * # passed out_array such that out_array can be used as a */ static PyObject *__pyx_f_9array_ext_c_set_id_type_array_long(PyArrayObject *__pyx_v_id_array, PyArrayObject *__pyx_v_out_array) { Py_ssize_t __pyx_v_cell_length; Py_ssize_t __pyx_v_dim0; Py_ssize_t *__pyx_v_id_data; Py_ssize_t *__pyx_v_out_data; Py_ssize_t __pyx_v_stride0; Py_ssize_t __pyx_v_stride1; int __pyx_v_i; int __pyx_v_j; int __pyx_v_in_idx; int __pyx_v_out_idx; PyObject *__pyx_r = NULL; size_t __pyx_t_1; Py_ssize_t __pyx_t_2; Py_ssize_t __pyx_t_3; __Pyx_RefNannySetupContext("c_set_id_type_array_long"); /* "array_ext.pyx":87 * cdef Py_ssize_t stride0, stride1 * * cell_length = id_array.dimensions[1]; # <<<<<<<<<<<<<< * dim0 = id_array.dimensions[0] * id_data = id_array.data */ __pyx_v_cell_length = (__pyx_v_id_array->dimensions[1]); /* "array_ext.pyx":88 * * cell_length = id_array.dimensions[1]; * dim0 = id_array.dimensions[0] # <<<<<<<<<<<<<< * id_data = id_array.data * out_data = out_array.data */ __pyx_v_dim0 = (__pyx_v_id_array->dimensions[0]); /* "array_ext.pyx":89 * cell_length = id_array.dimensions[1]; * dim0 = id_array.dimensions[0] * id_data = id_array.data # <<<<<<<<<<<<<< * out_data = out_array.data * stride0 = id_array.strides[0]/sizeof(Py_ssize_t) */ __pyx_v_id_data = ((Py_ssize_t *)__pyx_v_id_array->data); /* "array_ext.pyx":90 * dim0 = id_array.dimensions[0] * id_data = id_array.data * out_data = out_array.data # <<<<<<<<<<<<<< * stride0 = id_array.strides[0]/sizeof(Py_ssize_t) * stride1 = id_array.strides[1]/sizeof(Py_ssize_t) */ __pyx_v_out_data = ((Py_ssize_t *)__pyx_v_out_array->data); /* "array_ext.pyx":91 * id_data = id_array.data * out_data = out_array.data * stride0 = id_array.strides[0]/sizeof(Py_ssize_t) # <<<<<<<<<<<<<< * stride1 = id_array.strides[1]/sizeof(Py_ssize_t) * */ __pyx_t_1 = (sizeof(Py_ssize_t)); if (unlikely(__pyx_t_1 == 0)) { PyErr_Format(PyExc_ZeroDivisionError, "integer division or modulo by zero"); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 91; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_v_stride0 = ((__pyx_v_id_array->strides[0]) / __pyx_t_1); /* "array_ext.pyx":92 * out_data = out_array.data * stride0 = id_array.strides[0]/sizeof(Py_ssize_t) * stride1 = id_array.strides[1]/sizeof(Py_ssize_t) # <<<<<<<<<<<<<< * * cdef int i, j, in_idx, out_idx */ __pyx_t_1 = (sizeof(Py_ssize_t)); if (unlikely(__pyx_t_1 == 0)) { PyErr_Format(PyExc_ZeroDivisionError, "integer division or modulo by zero"); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 92; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_v_stride1 = ((__pyx_v_id_array->strides[1]) / __pyx_t_1); /* "array_ext.pyx":95 * * cdef int i, j, in_idx, out_idx * for i from 0 <= i < dim0: # <<<<<<<<<<<<<< * in_idx = i*stride0 * out_idx = i*cell_length + i */ __pyx_t_2 = __pyx_v_dim0; for (__pyx_v_i = 0; __pyx_v_i < __pyx_t_2; __pyx_v_i++) { /* "array_ext.pyx":96 * cdef int i, j, in_idx, out_idx * for i from 0 <= i < dim0: * in_idx = i*stride0 # <<<<<<<<<<<<<< * out_idx = i*cell_length + i * out_data[out_idx] = cell_length */ __pyx_v_in_idx = (__pyx_v_i * __pyx_v_stride0); /* "array_ext.pyx":97 * for i from 0 <= i < dim0: * in_idx = i*stride0 * out_idx = i*cell_length + i # <<<<<<<<<<<<<< * out_data[out_idx] = cell_length * for j from 0 <= j < cell_length: */ __pyx_v_out_idx = ((__pyx_v_i * __pyx_v_cell_length) + __pyx_v_i); /* "array_ext.pyx":98 * in_idx = i*stride0 * out_idx = i*cell_length + i * out_data[out_idx] = cell_length # <<<<<<<<<<<<<< * for j from 0 <= j < cell_length: * out_data[out_idx + j + 1] = id_data[in_idx + j*stride1] */ (__pyx_v_out_data[__pyx_v_out_idx]) = __pyx_v_cell_length; /* "array_ext.pyx":99 * out_idx = i*cell_length + i * out_data[out_idx] = cell_length * for j from 0 <= j < cell_length: # <<<<<<<<<<<<<< * out_data[out_idx + j + 1] = id_data[in_idx + j*stride1] * */ __pyx_t_3 = __pyx_v_cell_length; for (__pyx_v_j = 0; __pyx_v_j < __pyx_t_3; __pyx_v_j++) { /* "array_ext.pyx":100 * out_data[out_idx] = cell_length * for j from 0 <= j < cell_length: * out_data[out_idx + j + 1] = id_data[in_idx + j*stride1] # <<<<<<<<<<<<<< * * ###################################################################### */ (__pyx_v_out_data[((__pyx_v_out_idx + __pyx_v_j) + 1)]) = (__pyx_v_id_data[(__pyx_v_in_idx + (__pyx_v_j * __pyx_v_stride1))]); } } __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_AddTraceback("array_ext.c_set_id_type_array_long"); __pyx_r = 0; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* "array_ext.pyx":106 * ###################################################################### * * def set_id_type_array(id_array, out_array): # <<<<<<<<<<<<<< * """Given a 2D Int array (`id_array`), and a contiguous 1D numarray * array (`out_array`) having the correct size, this function sets */ static PyObject *__pyx_pf_9array_ext_set_id_type_array(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static char __pyx_doc_9array_ext_set_id_type_array[] = "Given a 2D Int array (`id_array`), and a contiguous 1D numarray\n array (`out_array`) having the correct size, this function sets\n the data from `id_array` into `out_array` so that it can be used\n in place of a `vtkIdTypeArray` in order to set the cells of a\n `vtkCellArray`.\n\n Note that if `shape = id_array.shape` then `size(out_array) ==\n shape[0]*(shape[1] + 1)` should be true. If not you'll get an\n `AssertionError`.\n\n `id_array` need not be contiguous but `out_array` must be.\n "; static PyMethodDef __pyx_mdef_9array_ext_set_id_type_array = {__Pyx_NAMESTR("set_id_type_array"), (PyCFunction)__pyx_pf_9array_ext_set_id_type_array, METH_VARARGS|METH_KEYWORDS, __Pyx_DOCSTR(__pyx_doc_9array_ext_set_id_type_array)}; static PyObject *__pyx_pf_9array_ext_set_id_type_array(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { PyObject *__pyx_v_id_array = 0; PyObject *__pyx_v_out_array = 0; PyObject *__pyx_v_vtk; PyObject *__pyx_v_VTK_ID_TYPE_SIZE; PyObject *__pyx_v_shp; PyObject *__pyx_v_sz; PyObject *__pyx_v_e_sz; PyObject *__pyx_r = NULL; PyObject *__pyx_t_1 = NULL; PyObject *__pyx_t_2 = NULL; PyObject *__pyx_t_3 = NULL; PyObject *__pyx_t_4 = NULL; int __pyx_t_5; int __pyx_t_6; int __pyx_t_7; Py_ssize_t __pyx_t_8; static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__id_array,&__pyx_n_s__out_array,0}; __Pyx_RefNannySetupContext("set_id_type_array"); __pyx_self = __pyx_self; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args = PyDict_Size(__pyx_kwds); PyObject* values[2] = {0,0}; switch (PyTuple_GET_SIZE(__pyx_args)) { case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); case 0: break; default: goto __pyx_L5_argtuple_error; } switch (PyTuple_GET_SIZE(__pyx_args)) { case 0: values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__id_array); if (likely(values[0])) kw_args--; else goto __pyx_L5_argtuple_error; case 1: values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__out_array); if (likely(values[1])) kw_args--; else { __Pyx_RaiseArgtupleInvalid("set_id_type_array", 1, 2, 2, 1); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, PyTuple_GET_SIZE(__pyx_args), "set_id_type_array") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; __pyx_clineno = __LINE__; goto __pyx_L3_error;} } __pyx_v_id_array = values[0]; __pyx_v_out_array = values[1]; } else if (PyTuple_GET_SIZE(__pyx_args) != 2) { goto __pyx_L5_argtuple_error; } else { __pyx_v_id_array = PyTuple_GET_ITEM(__pyx_args, 0); __pyx_v_out_array = PyTuple_GET_ITEM(__pyx_args, 1); } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("set_id_type_array", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; __pyx_clineno = __LINE__; goto __pyx_L3_error;} __pyx_L3_error:; __Pyx_AddTraceback("array_ext.set_id_type_array"); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_v_vtk = Py_None; __Pyx_INCREF(Py_None); __pyx_v_VTK_ID_TYPE_SIZE = Py_None; __Pyx_INCREF(Py_None); __pyx_v_shp = Py_None; __Pyx_INCREF(Py_None); __pyx_v_sz = Py_None; __Pyx_INCREF(Py_None); __pyx_v_e_sz = Py_None; __Pyx_INCREF(Py_None); /* "array_ext.pyx":119 * `id_array` need not be contiguous but `out_array` must be. * """ * import vtk # <<<<<<<<<<<<<< * VTK_ID_TYPE_SIZE = vtk.vtkIdTypeArray().GetDataTypeSize() * assert numpy.issubdtype(id_array.dtype, numpy.signedinteger) and \ */ __pyx_t_1 = __Pyx_Import(((PyObject *)__pyx_n_s__vtk), 0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 119; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_v_vtk); __pyx_v_vtk = __pyx_t_1; __pyx_t_1 = 0; /* "array_ext.pyx":120 * """ * import vtk * VTK_ID_TYPE_SIZE = vtk.vtkIdTypeArray().GetDataTypeSize() # <<<<<<<<<<<<<< * assert numpy.issubdtype(id_array.dtype, numpy.signedinteger) and \ * id_array.dtype.itemsize == VTK_ID_TYPE_SIZE */ __pyx_t_1 = PyObject_GetAttr(__pyx_v_vtk, __pyx_n_s__vtkIdTypeArray); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 120; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_empty_tuple), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 120; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_t_1 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__GetDataTypeSize); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 120; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_empty_tuple), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 120; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(__pyx_v_VTK_ID_TYPE_SIZE); __pyx_v_VTK_ID_TYPE_SIZE = __pyx_t_2; __pyx_t_2 = 0; /* "array_ext.pyx":121 * import vtk * VTK_ID_TYPE_SIZE = vtk.vtkIdTypeArray().GetDataTypeSize() * assert numpy.issubdtype(id_array.dtype, numpy.signedinteger) and \ # <<<<<<<<<<<<<< * id_array.dtype.itemsize == VTK_ID_TYPE_SIZE * */ #ifndef CYTHON_WITHOUT_ASSERTIONS __pyx_t_2 = __Pyx_GetName(__pyx_m, __pyx_n_s__numpy); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 121; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_1 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__issubdtype); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 121; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; __pyx_t_2 = PyObject_GetAttr(__pyx_v_id_array, __pyx_n_s__dtype); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 121; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_2); __pyx_t_3 = __Pyx_GetName(__pyx_m, __pyx_n_s__numpy); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 121; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_4 = PyObject_GetAttr(__pyx_t_3, __pyx_n_s__signedinteger); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 121; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 121; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(((PyObject *)__pyx_t_3)); PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_2); __Pyx_GIVEREF(__pyx_t_2); PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_4); __Pyx_GIVEREF(__pyx_t_4); __pyx_t_2 = 0; __pyx_t_4 = 0; __pyx_t_4 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_t_3), NULL); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 121; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_3)); __pyx_t_3 = 0; __pyx_t_5 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely(__pyx_t_5 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 121; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; if (__pyx_t_5) { /* "array_ext.pyx":122 * VTK_ID_TYPE_SIZE = vtk.vtkIdTypeArray().GetDataTypeSize() * assert numpy.issubdtype(id_array.dtype, numpy.signedinteger) and \ * id_array.dtype.itemsize == VTK_ID_TYPE_SIZE # <<<<<<<<<<<<<< * * assert out_array.flags.contiguous == 1, \ */ __pyx_t_4 = PyObject_GetAttr(__pyx_v_id_array, __pyx_n_s__dtype); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 122; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __pyx_t_3 = PyObject_GetAttr(__pyx_t_4, __pyx_n_s__itemsize); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 122; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = PyObject_RichCompare(__pyx_t_3, __pyx_v_VTK_ID_TYPE_SIZE, Py_EQ); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 122; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 122; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_7 = __pyx_t_6; } else { __pyx_t_7 = __pyx_t_5; } if (unlikely(!__pyx_t_7)) { PyErr_SetNone(PyExc_AssertionError); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 121; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } #endif /* "array_ext.pyx":124 * id_array.dtype.itemsize == VTK_ID_TYPE_SIZE * * assert out_array.flags.contiguous == 1, \ # <<<<<<<<<<<<<< * "out_array must be contiguous." * */ #ifndef CYTHON_WITHOUT_ASSERTIONS __pyx_t_4 = PyObject_GetAttr(__pyx_v_out_array, __pyx_n_s__flags); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 124; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __pyx_t_3 = PyObject_GetAttr(__pyx_t_4, __pyx_n_s__contiguous); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 124; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = PyObject_RichCompare(__pyx_t_3, __pyx_int_1, Py_EQ); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 124; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_t_7 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 124; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; if (unlikely(!__pyx_t_7)) { PyErr_SetObject(PyExc_AssertionError, ((PyObject *)__pyx_kp_s_1)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 124; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } #endif /* "array_ext.pyx":127 * "out_array must be contiguous." * * shp = id_array.shape # <<<<<<<<<<<<<< * assert len(shp) == 2, "id_array must be a two dimensional array." * */ __pyx_t_4 = PyObject_GetAttr(__pyx_v_id_array, __pyx_n_s__shape); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 127; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_v_shp); __pyx_v_shp = __pyx_t_4; __pyx_t_4 = 0; /* "array_ext.pyx":128 * * shp = id_array.shape * assert len(shp) == 2, "id_array must be a two dimensional array." # <<<<<<<<<<<<<< * * sz = numpy.size(out_array) */ #ifndef CYTHON_WITHOUT_ASSERTIONS __pyx_t_8 = PyObject_Length(__pyx_v_shp); if (unlikely(__pyx_t_8 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 128; __pyx_clineno = __LINE__; goto __pyx_L1_error;} if (unlikely(!(__pyx_t_8 == 2))) { PyErr_SetObject(PyExc_AssertionError, ((PyObject *)__pyx_kp_s_2)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 128; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } #endif /* "array_ext.pyx":130 * assert len(shp) == 2, "id_array must be a two dimensional array." * * sz = numpy.size(out_array) # <<<<<<<<<<<<<< * e_sz = shp[0]*(shp[1]+1) * assert sz == e_sz, \ */ __pyx_t_4 = __Pyx_GetName(__pyx_m, __pyx_n_s__numpy); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 130; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __pyx_t_3 = PyObject_GetAttr(__pyx_t_4, __pyx_n_s__size); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 130; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 130; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(((PyObject *)__pyx_t_4)); __Pyx_INCREF(__pyx_v_out_array); PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_v_out_array); __Pyx_GIVEREF(__pyx_v_out_array); __pyx_t_1 = PyObject_Call(__pyx_t_3, ((PyObject *)__pyx_t_4), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 130; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0; __Pyx_DECREF(__pyx_v_sz); __pyx_v_sz = __pyx_t_1; __pyx_t_1 = 0; /* "array_ext.pyx":131 * * sz = numpy.size(out_array) * e_sz = shp[0]*(shp[1]+1) # <<<<<<<<<<<<<< * assert sz == e_sz, \ * "out_array size is incorrect, expected: %s, given: %s"%(e_sz, sz) */ __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_shp, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 131; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_shp, 1, sizeof(long), PyInt_FromLong); if (!__pyx_t_4) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 131; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __pyx_t_3 = PyNumber_Add(__pyx_t_4, __pyx_int_1); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 131; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = PyNumber_Multiply(__pyx_t_1, __pyx_t_3); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 131; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __Pyx_DECREF(__pyx_v_e_sz); __pyx_v_e_sz = __pyx_t_4; __pyx_t_4 = 0; /* "array_ext.pyx":132 * sz = numpy.size(out_array) * e_sz = shp[0]*(shp[1]+1) * assert sz == e_sz, \ # <<<<<<<<<<<<<< * "out_array size is incorrect, expected: %s, given: %s"%(e_sz, sz) * */ #ifndef CYTHON_WITHOUT_ASSERTIONS __pyx_t_4 = PyObject_RichCompare(__pyx_v_sz, __pyx_v_e_sz, Py_EQ); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 132; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_4); __pyx_t_7 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 132; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; if (unlikely(!__pyx_t_7)) { /* "array_ext.pyx":133 * e_sz = shp[0]*(shp[1]+1) * assert sz == e_sz, \ * "out_array size is incorrect, expected: %s, given: %s"%(e_sz, sz) # <<<<<<<<<<<<<< * * if VTK_ID_TYPE_SIZE == 4: */ __pyx_t_4 = PyTuple_New(2); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 133; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(((PyObject *)__pyx_t_4)); __Pyx_INCREF(__pyx_v_e_sz); PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_v_e_sz); __Pyx_GIVEREF(__pyx_v_e_sz); __Pyx_INCREF(__pyx_v_sz); PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_v_sz); __Pyx_GIVEREF(__pyx_v_sz); __pyx_t_3 = PyNumber_Remainder(((PyObject *)__pyx_kp_s_3), ((PyObject *)__pyx_t_4)); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 133; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(((PyObject *)__pyx_t_3)); __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0; PyErr_SetObject(PyExc_AssertionError, ((PyObject *)__pyx_t_3)); __Pyx_DECREF(((PyObject *)__pyx_t_3)); __pyx_t_3 = 0; {__pyx_filename = __pyx_f[0]; __pyx_lineno = 132; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } #endif /* "array_ext.pyx":135 * "out_array size is incorrect, expected: %s, given: %s"%(e_sz, sz) * * if VTK_ID_TYPE_SIZE == 4: # <<<<<<<<<<<<<< * c_set_id_type_array(id_array, out_array) * elif VTK_ID_TYPE_SIZE == 8: */ __pyx_t_3 = PyObject_RichCompare(__pyx_v_VTK_ID_TYPE_SIZE, __pyx_int_4, Py_EQ); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 135; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __pyx_t_7 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 135; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (__pyx_t_7) { /* "array_ext.pyx":136 * * if VTK_ID_TYPE_SIZE == 4: * c_set_id_type_array(id_array, out_array) # <<<<<<<<<<<<<< * elif VTK_ID_TYPE_SIZE == 8: * c_set_id_type_array_long(id_array, out_array) */ if (!(likely(((__pyx_v_id_array) == Py_None) || likely(__Pyx_TypeTest(__pyx_v_id_array, __pyx_ptype_9array_ext_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 136; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_3 = __pyx_v_id_array; __Pyx_INCREF(__pyx_t_3); if (!(likely(((__pyx_v_out_array) == Py_None) || likely(__Pyx_TypeTest(__pyx_v_out_array, __pyx_ptype_9array_ext_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 136; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_4 = __pyx_v_out_array; __Pyx_INCREF(__pyx_t_4); __pyx_t_1 = __pyx_f_9array_ext_c_set_id_type_array(((PyArrayObject *)__pyx_t_3), ((PyArrayObject *)__pyx_t_4)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 136; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; goto __pyx_L6; } /* "array_ext.pyx":137 * if VTK_ID_TYPE_SIZE == 4: * c_set_id_type_array(id_array, out_array) * elif VTK_ID_TYPE_SIZE == 8: # <<<<<<<<<<<<<< * c_set_id_type_array_long(id_array, out_array) * else: */ __pyx_t_1 = PyObject_RichCompare(__pyx_v_VTK_ID_TYPE_SIZE, __pyx_int_8, Py_EQ); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 137; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); __pyx_t_7 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_7 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 137; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; if (__pyx_t_7) { /* "array_ext.pyx":138 * c_set_id_type_array(id_array, out_array) * elif VTK_ID_TYPE_SIZE == 8: * c_set_id_type_array_long(id_array, out_array) # <<<<<<<<<<<<<< * else: * raise ValueError('Unsupported VTK_ID_TYPE_SIZE=%d'\ */ if (!(likely(((__pyx_v_id_array) == Py_None) || likely(__Pyx_TypeTest(__pyx_v_id_array, __pyx_ptype_9array_ext_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 138; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_1 = __pyx_v_id_array; __Pyx_INCREF(__pyx_t_1); if (!(likely(((__pyx_v_out_array) == Py_None) || likely(__Pyx_TypeTest(__pyx_v_out_array, __pyx_ptype_9array_ext_ndarray))))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 138; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_t_4 = __pyx_v_out_array; __Pyx_INCREF(__pyx_t_4); __pyx_t_3 = __pyx_f_9array_ext_c_set_id_type_array_long(((PyArrayObject *)__pyx_t_1), ((PyArrayObject *)__pyx_t_4)); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 138; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; goto __pyx_L6; } /*else*/ { /* "array_ext.pyx":141 * else: * raise ValueError('Unsupported VTK_ID_TYPE_SIZE=%d'\ * %VTK_ID_TYPE_SIZE) # <<<<<<<<<<<<<< * */ __pyx_t_3 = PyNumber_Remainder(((PyObject *)__pyx_kp_s_4), __pyx_v_VTK_ID_TYPE_SIZE); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 141; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(((PyObject *)__pyx_t_3)); __pyx_t_4 = PyTuple_New(1); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 140; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(((PyObject *)__pyx_t_4)); PyTuple_SET_ITEM(__pyx_t_4, 0, ((PyObject *)__pyx_t_3)); __Pyx_GIVEREF(((PyObject *)__pyx_t_3)); __pyx_t_3 = 0; __pyx_t_3 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_t_4), NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 140; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(((PyObject *)__pyx_t_4)); __pyx_t_4 = 0; __Pyx_Raise(__pyx_t_3, 0, 0); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; {__pyx_filename = __pyx_f[0]; __pyx_lineno = 140; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_L6:; __pyx_r = Py_None; __Pyx_INCREF(Py_None); goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_XDECREF(__pyx_t_2); __Pyx_XDECREF(__pyx_t_3); __Pyx_XDECREF(__pyx_t_4); __Pyx_AddTraceback("array_ext.set_id_type_array"); __pyx_r = NULL; __pyx_L0:; __Pyx_DECREF(__pyx_v_vtk); __Pyx_DECREF(__pyx_v_VTK_ID_TYPE_SIZE); __Pyx_DECREF(__pyx_v_shp); __Pyx_DECREF(__pyx_v_sz); __Pyx_DECREF(__pyx_v_e_sz); __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyMethodDef __pyx_methods[] = { {0, 0, 0, 0} }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef __pyx_moduledef = { PyModuleDef_HEAD_INIT, __Pyx_NAMESTR("array_ext"), __Pyx_DOCSTR(__pyx_k_5), /* m_doc */ -1, /* m_size */ __pyx_methods /* m_methods */, NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL /* m_free */ }; #endif static __Pyx_StringTabEntry __pyx_string_tab[] = { {&__pyx_kp_s_1, __pyx_k_1, sizeof(__pyx_k_1), 0, 0, 1, 0}, {&__pyx_kp_s_2, __pyx_k_2, sizeof(__pyx_k_2), 0, 0, 1, 0}, {&__pyx_kp_s_3, __pyx_k_3, sizeof(__pyx_k_3), 0, 0, 1, 0}, {&__pyx_kp_s_4, __pyx_k_4, sizeof(__pyx_k_4), 0, 0, 1, 0}, {&__pyx_n_s__GetDataTypeSize, __pyx_k__GetDataTypeSize, sizeof(__pyx_k__GetDataTypeSize), 0, 0, 1, 1}, {&__pyx_n_s__ValueError, __pyx_k__ValueError, sizeof(__pyx_k__ValueError), 0, 0, 1, 1}, {&__pyx_n_s____main__, __pyx_k____main__, sizeof(__pyx_k____main__), 0, 0, 1, 1}, {&__pyx_n_s____test__, __pyx_k____test__, sizeof(__pyx_k____test__), 0, 0, 1, 1}, {&__pyx_n_s__array_ext, __pyx_k__array_ext, sizeof(__pyx_k__array_ext), 0, 0, 1, 1}, {&__pyx_n_s__contiguous, __pyx_k__contiguous, sizeof(__pyx_k__contiguous), 0, 0, 1, 1}, {&__pyx_n_s__data, __pyx_k__data, sizeof(__pyx_k__data), 0, 0, 1, 1}, {&__pyx_n_s__dimensions, __pyx_k__dimensions, sizeof(__pyx_k__dimensions), 0, 0, 1, 1}, {&__pyx_n_s__dtype, __pyx_k__dtype, sizeof(__pyx_k__dtype), 0, 0, 1, 1}, {&__pyx_n_s__flags, __pyx_k__flags, sizeof(__pyx_k__flags), 0, 0, 1, 1}, {&__pyx_n_s__id_array, __pyx_k__id_array, sizeof(__pyx_k__id_array), 0, 0, 1, 1}, {&__pyx_n_s__issubdtype, __pyx_k__issubdtype, sizeof(__pyx_k__issubdtype), 0, 0, 1, 1}, {&__pyx_n_s__itemsize, __pyx_k__itemsize, sizeof(__pyx_k__itemsize), 0, 0, 1, 1}, {&__pyx_n_s__numpy, __pyx_k__numpy, sizeof(__pyx_k__numpy), 0, 0, 1, 1}, {&__pyx_n_s__out_array, __pyx_k__out_array, sizeof(__pyx_k__out_array), 0, 0, 1, 1}, {&__pyx_n_s__set_id_type_array, __pyx_k__set_id_type_array, sizeof(__pyx_k__set_id_type_array), 0, 0, 1, 1}, {&__pyx_n_s__shape, __pyx_k__shape, sizeof(__pyx_k__shape), 0, 0, 1, 1}, {&__pyx_n_s__signedinteger, __pyx_k__signedinteger, sizeof(__pyx_k__signedinteger), 0, 0, 1, 1}, {&__pyx_n_s__size, __pyx_k__size, sizeof(__pyx_k__size), 0, 0, 1, 1}, {&__pyx_n_s__strides, __pyx_k__strides, sizeof(__pyx_k__strides), 0, 0, 1, 1}, {&__pyx_n_s__vtk, __pyx_k__vtk, sizeof(__pyx_k__vtk), 0, 0, 1, 1}, {&__pyx_n_s__vtkIdTypeArray, __pyx_k__vtkIdTypeArray, sizeof(__pyx_k__vtkIdTypeArray), 0, 0, 1, 1}, {0, 0, 0, 0, 0, 0, 0} }; static int __Pyx_InitCachedBuiltins(void) { __pyx_builtin_ValueError = __Pyx_GetName(__pyx_b, __pyx_n_s__ValueError); if (!__pyx_builtin_ValueError) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 140; __pyx_clineno = __LINE__; goto __pyx_L1_error;} return 0; __pyx_L1_error:; return -1; } static int __Pyx_InitCachedConstants(void) { __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants"); __Pyx_RefNannyFinishContext(); return 0; } static int __Pyx_InitGlobals(void) { if (__Pyx_InitStrings(__pyx_string_tab) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; __pyx_int_1 = PyInt_FromLong(1); if (unlikely(!__pyx_int_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; __pyx_int_4 = PyInt_FromLong(4); if (unlikely(!__pyx_int_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; __pyx_int_8 = PyInt_FromLong(8); if (unlikely(!__pyx_int_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; return 0; __pyx_L1_error:; return -1; } #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC initarray_ext(void); /*proto*/ PyMODINIT_FUNC initarray_ext(void) #else PyMODINIT_FUNC PyInit_array_ext(void); /*proto*/ PyMODINIT_FUNC PyInit_array_ext(void) #endif { PyObject *__pyx_t_1 = NULL; #if CYTHON_REFNANNY void* __pyx_refnanny = NULL; __Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny"); if (!__Pyx_RefNanny) { PyErr_Clear(); __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny"); if (!__Pyx_RefNanny) Py_FatalError("failed to import 'refnanny' module"); } __pyx_refnanny = __Pyx_RefNanny->SetupContext("PyMODINIT_FUNC PyInit_array_ext(void)", __LINE__, __FILE__); #endif __pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_empty_tuple)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_empty_bytes = PyBytes_FromStringAndSize("", 0); if (unlikely(!__pyx_empty_bytes)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} #ifdef __pyx_binding_PyCFunctionType_USED if (__pyx_binding_PyCFunctionType_init() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} #endif /*--- Library function declarations ---*/ /*--- Threads initialization code ---*/ #if defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS #ifdef WITH_THREAD /* Python build with threading support? */ PyEval_InitThreads(); #endif #endif /*--- Module creation code ---*/ #if PY_MAJOR_VERSION < 3 __pyx_m = Py_InitModule4(__Pyx_NAMESTR("array_ext"), __pyx_methods, __Pyx_DOCSTR(__pyx_k_5), 0, PYTHON_API_VERSION); #else __pyx_m = PyModule_Create(&__pyx_moduledef); #endif if (!__pyx_m) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; #if PY_MAJOR_VERSION < 3 Py_INCREF(__pyx_m); #endif __pyx_b = PyImport_AddModule(__Pyx_NAMESTR(__Pyx_BUILTIN_MODULE_NAME)); if (!__pyx_b) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; if (__Pyx_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; /*--- Initialize various global constants etc. ---*/ if (unlikely(__Pyx_InitGlobals() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} if (__pyx_module_is_main_array_ext) { if (__Pyx_SetAttrString(__pyx_m, "__name__", __pyx_n_s____main__) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; } /*--- Builtin init code ---*/ if (unlikely(__Pyx_InitCachedBuiltins() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /*--- Constants init code ---*/ if (unlikely(__Pyx_InitCachedConstants() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /*--- Global init code ---*/ /*--- Function export code ---*/ /*--- Type init code ---*/ __pyx_ptype_9array_ext_ndarray = __Pyx_ImportType("numpy", "ndarray", sizeof(PyArrayObject), 0); if (unlikely(!__pyx_ptype_9array_ext_ndarray)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /*--- Type import code ---*/ /*--- Function import code ---*/ /*--- Execution code ---*/ /* "array_ext.pyx":12 * # License: BSD Style. * * import numpy # <<<<<<<<<<<<<< * * ###################################################################### */ __pyx_t_1 = __Pyx_Import(((PyObject *)__pyx_n_s__numpy), 0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 12; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); if (PyObject_SetAttr(__pyx_m, __pyx_n_s__numpy, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 12; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; /* "array_ext.pyx":37 * void import_array() * * import_array() # <<<<<<<<<<<<<< * * ###################################################################### */ import_array(); /* "array_ext.pyx":106 * ###################################################################### * * def set_id_type_array(id_array, out_array): # <<<<<<<<<<<<<< * """Given a 2D Int array (`id_array`), and a contiguous 1D numarray * array (`out_array`) having the correct size, this function sets */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_9array_ext_set_id_type_array, NULL, __pyx_n_s__array_ext); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(__pyx_t_1); if (PyObject_SetAttr(__pyx_m, __pyx_n_s__set_id_type_array, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; /* "array_ext.pyx":1 * """ # <<<<<<<<<<<<<< * A Pyrex extension module for numpy. Currently this extension module * allows us to massage a 2D scipy array into a form usable as a */ __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_GOTREF(((PyObject *)__pyx_t_1)); if (PyObject_SetAttr(__pyx_m, __pyx_n_s____test__, ((PyObject *)__pyx_t_1)) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0; goto __pyx_L0; __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); if (__pyx_m) { __Pyx_AddTraceback("init array_ext"); Py_DECREF(__pyx_m); __pyx_m = 0; } else if (!PyErr_Occurred()) { PyErr_SetString(PyExc_ImportError, "init array_ext"); } __pyx_L0:; __Pyx_RefNannyFinishContext(); #if PY_MAJOR_VERSION < 3 return; #else return __pyx_m; #endif } /* Runtime support code */ static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name) { PyObject *result; result = PyObject_GetAttr(dict, name); if (!result) PyErr_SetObject(PyExc_NameError, name); return result; } static void __Pyx_RaiseArgtupleInvalid( const char* func_name, int exact, Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found) { Py_ssize_t num_expected; const char *number, *more_or_less; if (num_found < num_min) { num_expected = num_min; more_or_less = "at least"; } else { num_expected = num_max; more_or_less = "at most"; } if (exact) { more_or_less = "exactly"; } number = (num_expected == 1) ? "" : "s"; PyErr_Format(PyExc_TypeError, #if PY_VERSION_HEX < 0x02050000 "%s() takes %s %d positional argument%s (%d given)", #else "%s() takes %s %zd positional argument%s (%zd given)", #endif func_name, more_or_less, num_expected, number, num_found); } static void __Pyx_RaiseDoubleKeywordsError( const char* func_name, PyObject* kw_name) { PyErr_Format(PyExc_TypeError, #if PY_MAJOR_VERSION >= 3 "%s() got multiple values for keyword argument '%U'", func_name, kw_name); #else "%s() got multiple values for keyword argument '%s'", func_name, PyString_AS_STRING(kw_name)); #endif } static int __Pyx_ParseOptionalKeywords( PyObject *kwds, PyObject **argnames[], PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, const char* function_name) { PyObject *key = 0, *value = 0; Py_ssize_t pos = 0; PyObject*** name; PyObject*** first_kw_arg = argnames + num_pos_args; while (PyDict_Next(kwds, &pos, &key, &value)) { name = first_kw_arg; while (*name && (**name != key)) name++; if (*name) { values[name-argnames] = value; } else { #if PY_MAJOR_VERSION < 3 if (unlikely(!PyString_CheckExact(key)) && unlikely(!PyString_Check(key))) { #else if (unlikely(!PyUnicode_CheckExact(key)) && unlikely(!PyUnicode_Check(key))) { #endif goto invalid_keyword_type; } else { for (name = first_kw_arg; *name; name++) { #if PY_MAJOR_VERSION >= 3 if (PyUnicode_GET_SIZE(**name) == PyUnicode_GET_SIZE(key) && PyUnicode_Compare(**name, key) == 0) break; #else if (PyString_GET_SIZE(**name) == PyString_GET_SIZE(key) && _PyString_Eq(**name, key)) break; #endif } if (*name) { values[name-argnames] = value; } else { /* unexpected keyword found */ for (name=argnames; name != first_kw_arg; name++) { if (**name == key) goto arg_passed_twice; #if PY_MAJOR_VERSION >= 3 if (PyUnicode_GET_SIZE(**name) == PyUnicode_GET_SIZE(key) && PyUnicode_Compare(**name, key) == 0) goto arg_passed_twice; #else if (PyString_GET_SIZE(**name) == PyString_GET_SIZE(key) && _PyString_Eq(**name, key)) goto arg_passed_twice; #endif } if (kwds2) { if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad; } else { goto invalid_keyword; } } } } } return 0; arg_passed_twice: __Pyx_RaiseDoubleKeywordsError(function_name, **name); goto bad; invalid_keyword_type: PyErr_Format(PyExc_TypeError, "%s() keywords must be strings", function_name); goto bad; invalid_keyword: PyErr_Format(PyExc_TypeError, #if PY_MAJOR_VERSION < 3 "%s() got an unexpected keyword argument '%s'", function_name, PyString_AsString(key)); #else "%s() got an unexpected keyword argument '%U'", function_name, key); #endif bad: return -1; } static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) { if (unlikely(!type)) { PyErr_Format(PyExc_SystemError, "Missing type object"); return 0; } if (likely(PyObject_TypeCheck(obj, type))) return 1; PyErr_Format(PyExc_TypeError, "Cannot convert %.200s to %.200s", Py_TYPE(obj)->tp_name, type->tp_name); return 0; } static CYTHON_INLINE void __Pyx_ErrRestore(PyObject *type, PyObject *value, PyObject *tb) { PyObject *tmp_type, *tmp_value, *tmp_tb; PyThreadState *tstate = PyThreadState_GET(); tmp_type = tstate->curexc_type; tmp_value = tstate->curexc_value; tmp_tb = tstate->curexc_traceback; tstate->curexc_type = type; tstate->curexc_value = value; tstate->curexc_traceback = tb; Py_XDECREF(tmp_type); Py_XDECREF(tmp_value); Py_XDECREF(tmp_tb); } static CYTHON_INLINE void __Pyx_ErrFetch(PyObject **type, PyObject **value, PyObject **tb) { PyThreadState *tstate = PyThreadState_GET(); *type = tstate->curexc_type; *value = tstate->curexc_value; *tb = tstate->curexc_traceback; tstate->curexc_type = 0; tstate->curexc_value = 0; tstate->curexc_traceback = 0; } #if PY_MAJOR_VERSION < 3 static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb) { Py_XINCREF(type); Py_XINCREF(value); Py_XINCREF(tb); /* First, check the traceback argument, replacing None with NULL. */ if (tb == Py_None) { Py_DECREF(tb); tb = 0; } else if (tb != NULL && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "raise: arg 3 must be a traceback or None"); goto raise_error; } /* Next, replace a missing value with None */ if (value == NULL) { value = Py_None; Py_INCREF(value); } #if PY_VERSION_HEX < 0x02050000 if (!PyClass_Check(type)) #else if (!PyType_Check(type)) #endif { /* Raising an instance. The value should be a dummy. */ if (value != Py_None) { PyErr_SetString(PyExc_TypeError, "instance exception may not have a separate value"); goto raise_error; } /* Normalize to raise , */ Py_DECREF(value); value = type; #if PY_VERSION_HEX < 0x02050000 if (PyInstance_Check(type)) { type = (PyObject*) ((PyInstanceObject*)type)->in_class; Py_INCREF(type); } else { type = 0; PyErr_SetString(PyExc_TypeError, "raise: exception must be an old-style class or instance"); goto raise_error; } #else type = (PyObject*) Py_TYPE(type); Py_INCREF(type); if (!PyType_IsSubtype((PyTypeObject *)type, (PyTypeObject *)PyExc_BaseException)) { PyErr_SetString(PyExc_TypeError, "raise: exception class must be a subclass of BaseException"); goto raise_error; } #endif } __Pyx_ErrRestore(type, value, tb); return; raise_error: Py_XDECREF(value); Py_XDECREF(type); Py_XDECREF(tb); return; } #else /* Python 3+ */ static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb) { if (tb == Py_None) { tb = 0; } else if (tb && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "raise: arg 3 must be a traceback or None"); goto bad; } if (value == Py_None) value = 0; if (PyExceptionInstance_Check(type)) { if (value) { PyErr_SetString(PyExc_TypeError, "instance exception may not have a separate value"); goto bad; } value = type; type = (PyObject*) Py_TYPE(value); } else if (!PyExceptionClass_Check(type)) { PyErr_SetString(PyExc_TypeError, "raise: exception class must be a subclass of BaseException"); goto bad; } PyErr_SetObject(type, value); if (tb) { PyThreadState *tstate = PyThreadState_GET(); PyObject* tmp_tb = tstate->curexc_traceback; if (tb != tmp_tb) { Py_INCREF(tb); tstate->curexc_traceback = tb; Py_XDECREF(tmp_tb); } } bad: return; } #endif static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list) { PyObject *py_import = 0; PyObject *empty_list = 0; PyObject *module = 0; PyObject *global_dict = 0; PyObject *empty_dict = 0; PyObject *list; py_import = __Pyx_GetAttrString(__pyx_b, "__import__"); if (!py_import) goto bad; if (from_list) list = from_list; else { empty_list = PyList_New(0); if (!empty_list) goto bad; list = empty_list; } global_dict = PyModule_GetDict(__pyx_m); if (!global_dict) goto bad; empty_dict = PyDict_New(); if (!empty_dict) goto bad; module = PyObject_CallFunctionObjArgs(py_import, name, global_dict, empty_dict, list, NULL); bad: Py_XDECREF(empty_list); Py_XDECREF(py_import); Py_XDECREF(empty_dict); return module; } static CYTHON_INLINE unsigned char __Pyx_PyInt_AsUnsignedChar(PyObject* x) { const unsigned char neg_one = (unsigned char)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(unsigned char) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(unsigned char)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to unsigned char" : "value too large to convert to unsigned char"); } return (unsigned char)-1; } return (unsigned char)val; } return (unsigned char)__Pyx_PyInt_AsUnsignedLong(x); } static CYTHON_INLINE unsigned short __Pyx_PyInt_AsUnsignedShort(PyObject* x) { const unsigned short neg_one = (unsigned short)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(unsigned short) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(unsigned short)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to unsigned short" : "value too large to convert to unsigned short"); } return (unsigned short)-1; } return (unsigned short)val; } return (unsigned short)__Pyx_PyInt_AsUnsignedLong(x); } static CYTHON_INLINE unsigned int __Pyx_PyInt_AsUnsignedInt(PyObject* x) { const unsigned int neg_one = (unsigned int)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(unsigned int) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(unsigned int)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to unsigned int" : "value too large to convert to unsigned int"); } return (unsigned int)-1; } return (unsigned int)val; } return (unsigned int)__Pyx_PyInt_AsUnsignedLong(x); } static CYTHON_INLINE char __Pyx_PyInt_AsChar(PyObject* x) { const char neg_one = (char)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(char) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(char)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to char" : "value too large to convert to char"); } return (char)-1; } return (char)val; } return (char)__Pyx_PyInt_AsLong(x); } static CYTHON_INLINE short __Pyx_PyInt_AsShort(PyObject* x) { const short neg_one = (short)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(short) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(short)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to short" : "value too large to convert to short"); } return (short)-1; } return (short)val; } return (short)__Pyx_PyInt_AsLong(x); } static CYTHON_INLINE int __Pyx_PyInt_AsInt(PyObject* x) { const int neg_one = (int)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(int) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(int)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to int" : "value too large to convert to int"); } return (int)-1; } return (int)val; } return (int)__Pyx_PyInt_AsLong(x); } static CYTHON_INLINE signed char __Pyx_PyInt_AsSignedChar(PyObject* x) { const signed char neg_one = (signed char)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(signed char) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(signed char)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to signed char" : "value too large to convert to signed char"); } return (signed char)-1; } return (signed char)val; } return (signed char)__Pyx_PyInt_AsSignedLong(x); } static CYTHON_INLINE signed short __Pyx_PyInt_AsSignedShort(PyObject* x) { const signed short neg_one = (signed short)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(signed short) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(signed short)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to signed short" : "value too large to convert to signed short"); } return (signed short)-1; } return (signed short)val; } return (signed short)__Pyx_PyInt_AsSignedLong(x); } static CYTHON_INLINE signed int __Pyx_PyInt_AsSignedInt(PyObject* x) { const signed int neg_one = (signed int)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(signed int) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(signed int)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to signed int" : "value too large to convert to signed int"); } return (signed int)-1; } return (signed int)val; } return (signed int)__Pyx_PyInt_AsSignedLong(x); } static CYTHON_INLINE int __Pyx_PyInt_AsLongDouble(PyObject* x) { const int neg_one = (int)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; if (sizeof(int) < sizeof(long)) { long val = __Pyx_PyInt_AsLong(x); if (unlikely(val != (long)(int)val)) { if (!unlikely(val == -1 && PyErr_Occurred())) { PyErr_SetString(PyExc_OverflowError, (is_unsigned && unlikely(val < 0)) ? "can't convert negative value to int" : "value too large to convert to int"); } return (int)-1; } return (int)val; } return (int)__Pyx_PyInt_AsLong(x); } static CYTHON_INLINE unsigned long __Pyx_PyInt_AsUnsignedLong(PyObject* x) { const unsigned long neg_one = (unsigned long)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to unsigned long"); return (unsigned long)-1; } return (unsigned long)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to unsigned long"); return (unsigned long)-1; } return PyLong_AsUnsignedLong(x); } else { return PyLong_AsLong(x); } } else { unsigned long val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (unsigned long)-1; val = __Pyx_PyInt_AsUnsignedLong(tmp); Py_DECREF(tmp); return val; } } static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_PyInt_AsUnsignedLongLong(PyObject* x) { const unsigned PY_LONG_LONG neg_one = (unsigned PY_LONG_LONG)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to unsigned PY_LONG_LONG"); return (unsigned PY_LONG_LONG)-1; } return (unsigned PY_LONG_LONG)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to unsigned PY_LONG_LONG"); return (unsigned PY_LONG_LONG)-1; } return PyLong_AsUnsignedLongLong(x); } else { return PyLong_AsLongLong(x); } } else { unsigned PY_LONG_LONG val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (unsigned PY_LONG_LONG)-1; val = __Pyx_PyInt_AsUnsignedLongLong(tmp); Py_DECREF(tmp); return val; } } static CYTHON_INLINE long __Pyx_PyInt_AsLong(PyObject* x) { const long neg_one = (long)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to long"); return (long)-1; } return (long)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to long"); return (long)-1; } return PyLong_AsUnsignedLong(x); } else { return PyLong_AsLong(x); } } else { long val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (long)-1; val = __Pyx_PyInt_AsLong(tmp); Py_DECREF(tmp); return val; } } static CYTHON_INLINE PY_LONG_LONG __Pyx_PyInt_AsLongLong(PyObject* x) { const PY_LONG_LONG neg_one = (PY_LONG_LONG)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to PY_LONG_LONG"); return (PY_LONG_LONG)-1; } return (PY_LONG_LONG)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to PY_LONG_LONG"); return (PY_LONG_LONG)-1; } return PyLong_AsUnsignedLongLong(x); } else { return PyLong_AsLongLong(x); } } else { PY_LONG_LONG val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (PY_LONG_LONG)-1; val = __Pyx_PyInt_AsLongLong(tmp); Py_DECREF(tmp); return val; } } static CYTHON_INLINE signed long __Pyx_PyInt_AsSignedLong(PyObject* x) { const signed long neg_one = (signed long)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to signed long"); return (signed long)-1; } return (signed long)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to signed long"); return (signed long)-1; } return PyLong_AsUnsignedLong(x); } else { return PyLong_AsLong(x); } } else { signed long val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (signed long)-1; val = __Pyx_PyInt_AsSignedLong(tmp); Py_DECREF(tmp); return val; } } static CYTHON_INLINE signed PY_LONG_LONG __Pyx_PyInt_AsSignedLongLong(PyObject* x) { const signed PY_LONG_LONG neg_one = (signed PY_LONG_LONG)-1, const_zero = 0; const int is_unsigned = neg_one > const_zero; #if PY_VERSION_HEX < 0x03000000 if (likely(PyInt_Check(x))) { long val = PyInt_AS_LONG(x); if (is_unsigned && unlikely(val < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to signed PY_LONG_LONG"); return (signed PY_LONG_LONG)-1; } return (signed PY_LONG_LONG)val; } else #endif if (likely(PyLong_Check(x))) { if (is_unsigned) { if (unlikely(Py_SIZE(x) < 0)) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value to signed PY_LONG_LONG"); return (signed PY_LONG_LONG)-1; } return PyLong_AsUnsignedLongLong(x); } else { return PyLong_AsLongLong(x); } } else { signed PY_LONG_LONG val; PyObject *tmp = __Pyx_PyNumber_Int(x); if (!tmp) return (signed PY_LONG_LONG)-1; val = __Pyx_PyInt_AsSignedLongLong(tmp); Py_DECREF(tmp); return val; } } #ifndef __PYX_HAVE_RT_ImportType #define __PYX_HAVE_RT_ImportType static PyTypeObject *__Pyx_ImportType(const char *module_name, const char *class_name, long size, int strict) { PyObject *py_module = 0; PyObject *result = 0; PyObject *py_name = 0; char warning[200]; py_module = __Pyx_ImportModule(module_name); if (!py_module) goto bad; #if PY_MAJOR_VERSION < 3 py_name = PyString_FromString(class_name); #else py_name = PyUnicode_FromString(class_name); #endif if (!py_name) goto bad; result = PyObject_GetAttr(py_module, py_name); Py_DECREF(py_name); py_name = 0; Py_DECREF(py_module); py_module = 0; if (!result) goto bad; if (!PyType_Check(result)) { PyErr_Format(PyExc_TypeError, "%s.%s is not a type object", module_name, class_name); goto bad; } if (!strict && ((PyTypeObject *)result)->tp_basicsize > size) { PyOS_snprintf(warning, sizeof(warning), "%s.%s size changed, may indicate binary incompatibility", module_name, class_name); #if PY_VERSION_HEX < 0x02050000 PyErr_Warn(NULL, warning); #else PyErr_WarnEx(NULL, warning, 0); #endif } else if (((PyTypeObject *)result)->tp_basicsize != size) { PyErr_Format(PyExc_ValueError, "%s.%s has the wrong size, try recompiling", module_name, class_name); goto bad; } return (PyTypeObject *)result; bad: Py_XDECREF(py_module); Py_XDECREF(result); return 0; } #endif #ifndef __PYX_HAVE_RT_ImportModule #define __PYX_HAVE_RT_ImportModule static PyObject *__Pyx_ImportModule(const char *name) { PyObject *py_name = 0; PyObject *py_module = 0; #if PY_MAJOR_VERSION < 3 py_name = PyString_FromString(name); #else py_name = PyUnicode_FromString(name); #endif if (!py_name) goto bad; py_module = PyImport_Import(py_name); Py_DECREF(py_name); return py_module; bad: Py_XDECREF(py_name); return 0; } #endif #include "compile.h" #include "frameobject.h" #include "traceback.h" static void __Pyx_AddTraceback(const char *funcname) { PyObject *py_srcfile = 0; PyObject *py_funcname = 0; PyObject *py_globals = 0; PyCodeObject *py_code = 0; PyFrameObject *py_frame = 0; #if PY_MAJOR_VERSION < 3 py_srcfile = PyString_FromString(__pyx_filename); #else py_srcfile = PyUnicode_FromString(__pyx_filename); #endif if (!py_srcfile) goto bad; if (__pyx_clineno) { #if PY_MAJOR_VERSION < 3 py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, __pyx_clineno); #else py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, __pyx_clineno); #endif } else { #if PY_MAJOR_VERSION < 3 py_funcname = PyString_FromString(funcname); #else py_funcname = PyUnicode_FromString(funcname); #endif } if (!py_funcname) goto bad; py_globals = PyModule_GetDict(__pyx_m); if (!py_globals) goto bad; py_code = PyCode_New( 0, /*int argcount,*/ #if PY_MAJOR_VERSION >= 3 0, /*int kwonlyargcount,*/ #endif 0, /*int nlocals,*/ 0, /*int stacksize,*/ 0, /*int flags,*/ __pyx_empty_bytes, /*PyObject *code,*/ __pyx_empty_tuple, /*PyObject *consts,*/ __pyx_empty_tuple, /*PyObject *names,*/ __pyx_empty_tuple, /*PyObject *varnames,*/ __pyx_empty_tuple, /*PyObject *freevars,*/ __pyx_empty_tuple, /*PyObject *cellvars,*/ py_srcfile, /*PyObject *filename,*/ py_funcname, /*PyObject *name,*/ __pyx_lineno, /*int firstlineno,*/ __pyx_empty_bytes /*PyObject *lnotab*/ ); if (!py_code) goto bad; py_frame = PyFrame_New( PyThreadState_GET(), /*PyThreadState *tstate,*/ py_code, /*PyCodeObject *code,*/ py_globals, /*PyObject *globals,*/ 0 /*PyObject *locals*/ ); if (!py_frame) goto bad; py_frame->f_lineno = __pyx_lineno; PyTraceBack_Here(py_frame); bad: Py_XDECREF(py_srcfile); Py_XDECREF(py_funcname); Py_XDECREF(py_code); Py_XDECREF(py_frame); } static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) { while (t->p) { #if PY_MAJOR_VERSION < 3 if (t->is_unicode) { *t->p = PyUnicode_DecodeUTF8(t->s, t->n - 1, NULL); } else if (t->intern) { *t->p = PyString_InternFromString(t->s); } else { *t->p = PyString_FromStringAndSize(t->s, t->n - 1); } #else /* Python 3+ has unicode identifiers */ if (t->is_unicode | t->is_str) { if (t->intern) { *t->p = PyUnicode_InternFromString(t->s); } else if (t->encoding) { *t->p = PyUnicode_Decode(t->s, t->n - 1, t->encoding, NULL); } else { *t->p = PyUnicode_FromStringAndSize(t->s, t->n - 1); } } else { *t->p = PyBytes_FromStringAndSize(t->s, t->n - 1); } #endif if (!*t->p) return -1; ++t; } return 0; } /* Type Conversion Functions */ static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { int is_true = x == Py_True; if (is_true | (x == Py_False) | (x == Py_None)) return is_true; else return PyObject_IsTrue(x); } static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) { PyNumberMethods *m; const char *name = NULL; PyObject *res = NULL; #if PY_VERSION_HEX < 0x03000000 if (PyInt_Check(x) || PyLong_Check(x)) #else if (PyLong_Check(x)) #endif return Py_INCREF(x), x; m = Py_TYPE(x)->tp_as_number; #if PY_VERSION_HEX < 0x03000000 if (m && m->nb_int) { name = "int"; res = PyNumber_Int(x); } else if (m && m->nb_long) { name = "long"; res = PyNumber_Long(x); } #else if (m && m->nb_int) { name = "int"; res = PyNumber_Long(x); } #endif if (res) { #if PY_VERSION_HEX < 0x03000000 if (!PyInt_Check(res) && !PyLong_Check(res)) { #else if (!PyLong_Check(res)) { #endif PyErr_Format(PyExc_TypeError, "__%s__ returned non-%s (type %.200s)", name, name, Py_TYPE(res)->tp_name); Py_DECREF(res); return NULL; } } else if (!PyErr_Occurred()) { PyErr_SetString(PyExc_TypeError, "an integer is required"); } return res; } static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { Py_ssize_t ival; PyObject* x = PyNumber_Index(b); if (!x) return -1; ival = PyInt_AsSsize_t(x); Py_DECREF(x); return ival; } static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) { #if PY_VERSION_HEX < 0x02050000 if (ival <= LONG_MAX) return PyInt_FromLong((long)ival); else { unsigned char *bytes = (unsigned char *) &ival; int one = 1; int little = (int)*(unsigned char*)&one; return _PyLong_FromByteArray(bytes, sizeof(size_t), little, 0); } #else return PyInt_FromSize_t(ival); #endif } static CYTHON_INLINE size_t __Pyx_PyInt_AsSize_t(PyObject* x) { unsigned PY_LONG_LONG val = __Pyx_PyInt_AsUnsignedLongLong(x); if (unlikely(val == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())) { return (size_t)-1; } else if (unlikely(val != (unsigned PY_LONG_LONG)(size_t)val)) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to size_t"); return (size_t)-1; } return (size_t)val; } #endif /* Py_PYTHON_H */ mayavi-4.1.0/tvtk/wrapper_gen.py0000644000175100001440000014563011674464502017660 0ustar ischnellusers00000000000000"""This module generates the tvtk (Traited VTK) wrapper classes for VTK classes. """ # Author: Prabhu Ramachandran # Copyright (c) 2004, Enthought, Inc. # License: BSD Style. import sys import vtk import types import textwrap import keyword import copy # Local imports (these are relative imports because the package is not # installed when these modules are imported). from common import get_tvtk_name, camel2enthought import vtk_parser import indenter import special_gen def clean_special_chars(s): """Given a string with a '\n' or '\r' it replaces it with a suitably escaped string. """ s1 = s.replace('\n', '\\n') s2 = s1.replace('\r', '\\r') return s2 ###################################################################### # `WrapperGenerator` class. ###################################################################### class WrapperGenerator: """Generates the wrapper code for all the TVTK classes. """ def __init__(self): self.indent = indenter.Indent() self.parser = vtk_parser.VTKMethodParser() self.special = special_gen.SpecialGenerator(self.indent) self.dm = indenter.VTKDocMassager() ################################################################# # `WrapperGenerator` interface. ################################################################# def get_tree(self): """Returns the parser's class tree.""" return self.parser.get_tree() def generate_code(self, node, out): """Generates the code for the given node in the parse tree along with an opened file-like object. Parameters ---------- - node A node in the ClassTree. - out : file-like object. Must support a `write` method. Code is written to it. """ self.indent.reset() self._write_prelims(node, out) # Write the class decl and __init__ self._gen_class_init(node, out) # Write the other methods. self._gen_methods(node, out) # Write any special code if available. self.special.generate_code(node, out) out.write('\n') ################################################################# # Non-public interface. ################################################################# def _write_prelims(self, node, out): """Write preliminary information given the node in the class tree, `node`, and output file-like object, `out`. """ prelim = """ # Automatically generated code: EDIT AT YOUR OWN RISK from traits import api as traits from traitsui import api as traitsui from tvtk import vtk_module as vtk from tvtk import tvtk_base from tvtk.tvtk_base_handler import TVTKBaseHandler from tvtk import messenger from tvtk.tvtk_base import deref_vtk from tvtk import array_handler from tvtk.array_handler import deref_array from tvtk.tvtk_classes.tvtk_helper import wrap_vtk """ out.write(self.indent.format(prelim)) def _gen_class_init(self, node, out): indent = self.indent klass = self.get_tree().get_class(node.name) vtk_class_name = klass.__name__ class_name = self._get_class_name(klass) if node.level == 0: base_name = 'tvtk_base.TVTKBase' else: base_name = self._get_class_name(klass.__bases__)[0] if base_name != 'object': # Import the base class. base_fname = camel2enthought(base_name) _imp = "from tvtk.tvtk_classes.%(base_fname)s import %(base_name)s"%locals() out.write(indent.format(_imp)) out.write('\n\n') # Write the class declaration. cdef = """ class %(class_name)s(%(base_name)s): """%locals() out.write(indent.format(cdef)) self.dm.write_class_doc(klass.__doc__, out, indent) indent.incr() # Write __init__ decl = """ def __init__(self, obj=None, update=True, **traits): tvtk_base.TVTKBase.__init__(self, vtk.%(vtk_class_name)s, obj, update, **traits) """%locals() out.write(indent.format(decl)) if 'vtk3DWidget' in [x.name for x in node.get_ancestors()]: # In this case we also update the traits on the # EndInteractionEvent. Note that we don't need to change decl = ''' def setup_observers(self): """Setup the observers for the object.""" super(%(class_name)s, self).setup_observers() tvtk_base._object_cache.setup_observers(self._vtk_obj, 'EndInteractionEvent', self.update_traits) '''%locals() out.write(indent.format(decl)) def _gen_methods(self, node, out): klass = self.get_tree().get_class(node.name) self.parser.parse(klass) if klass.__name__ == 'vtkCamera': # 'vtkCamera.Roll' has conflicting signatures -- # Get/SetRoll() plus an additional Roll() method. So we # wrap all of them as methods and not as traits. p = self.parser p.get_set_meths.pop('Roll') p.other_meths.extend(['GetRoll', 'SetRoll']) # ---------------------------------------- # Generate the code. # The return values are editable traits. toggle = self._gen_toggle_methods(klass, out) state = self._gen_state_methods(klass, out) get_set = self._gen_get_set_methods(klass, out) # These do not produce editable traits. self._gen_get_methods(klass, out) self._gen_other_methods(klass, out) # ---------------------------------------- # Now write out the _updateable_traits_ and View related code. # Store the data in the node after updating from parents. # Note that this data is generated and stored at run # time. This is the reason why the wrapper code for the # classes are generated in the reverse order of their depth in # the inheritance tree. data = {'toggle':toggle, 'state':state, 'get_set':get_set} if node.level != 0 and node.parents[0].name != 'object': pd = node.parents[0].data for i in data.keys(): data[i].update(pd[i]) node.data = data # ---------------------------------------- # Write out the updateable traits, this is used by # the `update_traits` method. ut = {} for i in data.values(): ut.update(i) junk = textwrap.fill(repr(tuple(ut.items()))) code = "\n_updateable_traits_ = \\" + "\n%s\n\n"%junk out.write(self.indent.format(code)) # ---------------------------------------- # Write out the full_traits_view and the more compact # traits_view # First copy the data over (we're going to edit it and don't # want the node's version to be changed). d = copy.deepcopy(data) # Add support for property trait delegation. #Commented out because of problems. #self._generate_delegates(node, d, out) toggle, state, get_set = d['toggle'], d['state'], d['get_set'] # Remove unwanted stuff. def _safe_remove(d, keys): for key in keys: try: del d[key] except KeyError: pass # No point having these in the GUI. _safe_remove(get_set, ['reference_count', 'progress']) class_name = get_tvtk_name(node.name) title = 'Edit %s properties'%class_name # Write the full_traits_view. # The full traits view displays all of the relevant traits in a table # editor. For this, we first write out the _full_traitnames_list: this # is used by the TVTKBaseHandler to build a TableEditor for all of # the (relevant) traits in the tvtk object. t_g = toggle.keys(); t_g.sort() s_g = state.keys(); s_g.sort() gs_g = get_set.keys(); gs_g.sort() junk = textwrap.fill("(%s)" % (t_g + s_g + gs_g)) code = "\n_full_traitnames_list_ = \\" + "\n%s\n\n"%junk out.write(self.indent.format(code)) # Start the trait_view() method. code = "\ndef trait_view(self, name=None, view_element=None):" out.write(self.indent.format(code)) self.indent.incr() code = "\nif view_element is not None or name not in (None, '', 'traits_view', 'full_traits_view', 'view'):" out.write(self.indent.format(code)) self.indent.incr() code = "\nreturn super(%s, self).trait_view(name, view_element)" % class_name out.write(self.indent.format(code)) self.indent.decr() # Now write the full traits view. code = "\nif name == 'full_traits_view':" out.write(self.indent.format(code)) self.indent.incr() item_contents = ( 'traitsui.Item("handler._full_traits_list",show_label=False)') junk = 'traitsui.View((%s),'% item_contents code = "\nfull_traits_view = \\" + \ "\n%s\ntitle=\'%s\', scrollable=True, resizable=True,"\ "\nhandler=TVTKBaseHandler,"\ "\nbuttons=['OK', 'Cancel'])"\ "\nreturn full_traits_view"%(junk, title) out.write(self.indent.format(code)) self.indent.decr() # Next, we write a compact traits_view (which we call 'view'), which # removes some generally unused items. code = "\nelif name == 'view':" out.write(self.indent.format(code)) self.indent.incr() _safe_remove(get_set, ['progress_text']) _safe_remove(toggle, ['abort_execute', 'release_data_flag', 'dragable', 'pickable', 'debug', 'global_warning_display']) t_g = toggle.keys(); t_g.sort() s_g = state.keys(); s_g.sort() gs_g = get_set.keys(); gs_g.sort() junk = textwrap.fill('traitsui.View((%s, %s, %s),'%(t_g, s_g, gs_g)) code = "\nview = \\" + \ "\n%s\ntitle=\'%s\', scrollable=True, resizable=True,"\ "\nhandler=TVTKBaseHandler,"\ "\nbuttons=['OK', 'Cancel'])"\ "\nreturn view"%(junk, title) out.write(self.indent.format(code)) self.indent.decr() # Finally, we write the default traits_view which includes a field # for specifying the view type (basic or advanced) and the # corresponding view (basic->view and advanced->full_traits_view) code = "\nelif name in (None, 'traits_view'):" out.write(self.indent.format(code)) self.indent.incr() viewtype_contents = ( 'traitsui.HGroup(traitsui.spring, "handler.view_type", ' +\ 'show_border=True)') view_contents = ( '\ntraitsui.Item("handler.info.object", ' +\ 'editor = traitsui.InstanceEditor(view_name="handler.view"), ' +\ 'style = "custom", show_label=False)') junk = 'traitsui.View((%s, %s),'% (viewtype_contents, view_contents) code = "\ntraits_view = \\" + \ "\n%s\ntitle=\'%s\', scrollable=True, resizable=True,"\ "\nhandler=TVTKBaseHandler,"\ "\nbuttons=['OK', 'Cancel'])"\ "\nreturn traits_view\n\n"%(junk, title) out.write(self.indent.format(code)) self.indent.decr() self.indent.decr() def _generate_delegates(self, node, n_data, out): """This method generates delegates for specific classes. It modifies the n_data dictionary.""" prop_name = {'vtkActor': 'vtkProperty', 'vtkActor2D': 'vtkProperty2D', 'vtkVolume': 'vtkVolumeProperty'} if node.name in prop_name.keys(): prop_node = self.get_tree().get_node(prop_name[node.name]) prop_data = prop_node.data # Update the data of the node so the view includes the # property traits. code = '' for key in n_data.keys(): props = prop_data[key] n_data[key].update(props) # Write the delegates. for p in props: code += '%s = tvtk_base.vtk_property_delegate\n'%p code += '\n' out.write(self.indent.format(code)) def _gen_toggle_methods(self, klass, out): meths = self.parser.get_toggle_methods() updateable_traits = {} for m in meths: name = self._reform_name(m) updateable_traits[name] = 'Get' + m t_def = 'tvtk_base.false_bool_trait' if meths[m]: t_def = 'tvtk_base.true_bool_trait' try: vtk_set_meth = getattr(klass, 'Set' + m) except AttributeError: # Broken VTK API (4.2) where sometimes GetProp and # PropOn/Off exist but no SetProp method is available. vtk_get_meth = getattr(klass, 'Get' + m) self._write_trait(out, name, t_def, vtk_get_meth, mapped=True, broken_bool=True) else: self._write_trait(out, name, t_def, vtk_set_meth, mapped=True) return updateable_traits def _gen_state_methods(self, klass, out): parser = self.parser indent = self.indent meths = parser.get_state_methods() updateable_traits = {} for m in meths.keys(): name = self._reform_name(m) updateable_traits[name] = 'Get' + m d = {} vtk_val = 0 for key, val in meths[m]: d[self._reform_name(key)] = val if isinstance(val, vtk.vtkObjectBase): vtk_val = 1 if (not hasattr(klass, 'Set' + m)): # Sometimes (very rarely) the VTK method is # inconsistent. For example in VTK-4.4 # vtkExtentTranslator::SetSplitMode does not exist. # In this case wrap it specially. vtk_val = 1 if vtk_val == 0 and m in ['DataScalarType', 'OutputScalarType', 'UpdateExtent']: vtk_val = 2 # Sometimes, some methods have default values that are # outside the specified choices. This is to special case # these. extra_val = None if vtk_val == 0 and klass.__name__ == 'vtkGenericEnSightReader' \ and m == 'ByteOrder': extra_val = 2 if vtk_val == 0 and klass.__name__ == 'vtkImageData' \ and m == 'ScalarType': extra_val = range(0, 22) if vtk_val == 0 and klass.__name__ == 'vtkImagePlaneWidget' \ and m == 'PlaneOrientation': extra_val = 3 if (vtk_val == 0) and (klass.__name__ == 'vtkThreshold') \ and (m == 'AttributeMode'): extra_val = -1 if (sys.platform == 'darwin') and (vtk_val == 0) \ and (klass.__name__ == 'vtkRenderWindow') \ and (m == 'StereoType'): extra_val = 0 if not vtk_val: default = self._reform_name(meths[m][0][0]) if extra_val is None: t_def = """traits.Trait('%(default)s', tvtk_base.TraitRevPrefixMap(%(d)s))"""\ %locals() elif hasattr(extra_val, '__iter__'): extra_val = str(extra_val)[1:-1] t_def = """traits.Trait('%(default)s', %(extra_val)s, tvtk_base.TraitRevPrefixMap(%(d)s))"""\ %locals() else: t_def = """traits.Trait('%(default)s', %(extra_val)s, tvtk_base.TraitRevPrefixMap(%(d)s))"""\ %locals() vtk_set_meth = getattr(klass, 'Set' + m) self._write_trait(out, name, t_def, vtk_set_meth, mapped=True) else: del updateable_traits[name] vtk_meth = getattr(klass, 'Get' + m) self._write_tvtk_method(out, vtk_meth) if vtk_val == 2: vtk_meth = getattr(klass, 'Set' + m) self._write_tvtk_method(out, vtk_meth) for key, val in meths[m][1:]: x = self._reform_name(key) vtk_meth = getattr(klass, 'Set%sTo%s'%(m, key)) decl = 'def set_%s_to_%s(self):'%(name, x) body = 'self._vtk_obj.Set%(m)sTo%(key)s()\n'%locals() self._write_generic_method(out, decl, vtk_meth, body) return updateable_traits def _gen_get_set_methods(self, klass, out): parser = self.parser meths = parser.get_get_set_methods() updateable_traits = {} for m in meths.keys(): name = self._reform_name(m) updateable_traits[name] = 'Get' + m vtk_get_meth = getattr(klass, 'Get' + m) vtk_set_meth = getattr(klass, 'Set' + m) if meths[m]: default, rng = meths[m] else: # In this case we could not get the defaults and range # since class has no known concrete subclass. This # happens in very rare circumstances and while the # below look like hacks, they are the best we can do. sig = parser.get_method_signature(vtk_get_meth) if sig[0][1] is None: ret = sig[0][0][0] if ret[:3] == 'vtk': default, rng = None, None elif ret == 'int': default, rng = 0, None elif ret == 'float': default, rng = 0.0, None elif ret == 'string': default, rng = '', None else: default, rng = None, None else: self._write_tvtk_method(out, vtk_get_meth, sig) self._write_tvtk_method(out, vtk_set_meth) continue if m == 'Output': self._write_get_output_method(klass, out, set=True) del updateable_traits['output'] elif m == 'Source': # Special cased because vtkGlyph3D.GetSource/SetSource # etc. have a special structure to support multiple # sources. del updateable_traits['source'] self._write_get_source_method(klass, out) elif m == 'Input': # In VTK > 4.5, Set/GetInput have multiple signatures. del updateable_traits['input'] self._write_get_input_method(klass, out) elif m == 'InputConnection': del updateable_traits['input_connection'] self._write_get_input_connection_method(klass, out) elif m.endswith('FileName'): t_def = 'tvtk_base.vtk_file_name("")' self._write_trait(out, name, t_def, vtk_set_meth, mapped=False) elif m.endswith('FilePrefix'): t_def = 'tvtk_base.vtk_file_prefix("")' self._write_trait(out, name, t_def, vtk_set_meth, mapped=False) elif rng is None: typ = type(default) number_map = {types.IntType: 'traits.Int', types.FloatType: 'traits.Float', types.LongType: 'traits.Long'} if typ in number_map: t_name = number_map[typ] t_def = '%(t_name)s(%(default)s, enter_set=True, '\ 'auto_set=False)'%locals() self._write_trait(out, name, t_def, vtk_set_meth, mapped=False) elif m in ['AreaLabelArrayName'] and \ klass.__name__ == 'vtkTreeAreaView': # A special case for the vtkTreeAreaView which # returns a default None value for what ought to be # a string. This is perhaps best fixed in the VTK # Python wrapper but thats just too much work. t_def = "traits.Trait('%(default)s', None, "\ "traits.String('%(default)s', enter_set=True, "\ "auto_set=False))"%locals() self._write_trait(out, name, t_def, vtk_set_meth, mapped=False) elif typ in types.StringTypes: if '\n' in default or '\r' in default: default = clean_special_chars(default) if default == '\x00': default = '' t_def = 'traits.String("%(default)s", '%locals() elif default == '"': t_def = "traits.String('%(default)s', "%locals() elif default == "'": t_def = '''traits.String("%(default)s", '''%locals() else: t_def = 'traits.String(r"%(default)s", '%locals() t_def += 'enter_set=True, auto_set=False)' self._write_trait(out, name, t_def, vtk_set_meth, mapped=False) elif typ in (types.TupleType,): if (name.find('color') > -1 or \ name.find('background') > -1) and \ len(default) == 3: # This is a color. force = 'False' if klass.__name__ in ['vtkProperty', 'vtkLight']: # These two classes are special because if # you change one color the GetColor # changes value so we must force an # update. force = 'True' t_def = 'tvtk_base.vtk_color_trait(%(default)s)'%locals() self._write_trait(out, name, t_def, vtk_set_meth, mapped=False, force_update=force) else: # Some other tuple shape = (len(default),) if type(default[0]) is int: dtype = 'int' else: dtype = 'float' t_def = 'traits.Array('\ 'shape=%(shape)s, value=%(default)s, '\ 'dtype=%(dtype)s, '\ 'enter_set=True, auto_set=False, '\ 'cols=3)'%locals() self._write_trait(out, name, t_def, vtk_set_meth, mapped=False) elif default is None or \ isinstance(default, vtk.vtkObjectBase): g_sig = parser.get_method_signature(vtk_get_meth) s_sig = parser.get_method_signature(vtk_set_meth) # Bunch of hacks to work around issues. #print g_sig, vtk_get_meth, klass.__name__ if len(g_sig) == 0: g_sig = [([None], None)] if len(s_sig) == 0: s_sig = [([None], [None])] g_sig = [([None], None)] elif s_sig[0][1] is None or s_sig[0][1] == '': s_sig[0] = list(s_sig[0]) s_sig[0][1] = [None] if g_sig[0][0][0] == 'string': # If the get method really returns a string # wrap it as such. t_def = 'traits.Trait(None, None, '\ 'traits.String(enter_set=True, auto_set=False))' self._write_trait(out, name, t_def, vtk_set_meth, mapped=False) else: if (g_sig[0][1] is None) and (len(s_sig[0][1]) == 1): # Get needs no args and Set needs one arg self._write_property(out, name, vtk_get_meth, vtk_set_meth) else: # Get has args or Set needs many args. self._write_tvtk_method(out, vtk_get_meth, g_sig) self._write_tvtk_method(out, vtk_set_meth, s_sig) del updateable_traits[name] elif typ is types.BooleanType: t_def = 'traits.Bool(%(default)s)'%locals() self._write_trait(out, name, t_def, vtk_set_meth, mapped=False) else: print "%s:"%klass.__name__, print "Ignoring method: Get/Set%s"%m print "default: %s, range: None"%default del updateable_traits[name] else: # Has a specified range of valid values. if klass.__name__ == 'vtkCubeAxesActor2D' and \ name == 'inertia': # VTK bug. Inconsistent API! rng = (float(rng[0]), float(rng[1])) # If the default is just a little off from the range # then extend the range. if (default < rng[0]) and (rng[0] - default) < 2: rng = (default, rng[1]) if (default > rng[1]) and (default - rng[1]) < 2: rng = (rng[0], default) # Sometimes the default is not in the valid range to # perhaps indicate that the class is not initialized if (default < rng[0]) or (default > rng[1]): t_def = 'traits.Trait(%(default)s, %(default)s, '\ 'traits.Range%(rng)s'%locals() t_def = t_def[:-1] + ', enter_set=True, auto_set=False))' else: t_def = 'traits.Trait(%(default)s, '\ 'traits.Range%(rng)s'%locals() t_def = t_def[:-1] + ', enter_set=True, auto_set=False))' self._write_trait(out, name, t_def, vtk_set_meth, mapped=False) return updateable_traits def _gen_get_methods(self, klass, out): parser = self.parser meths = parser.get_get_methods() for m in meths: vtk_get_meth = getattr(klass, m) if m == 'GetOutput': # GetOutput is special. self._write_get_output_method(klass, out, set=False) elif m == 'GetInput': # GetInput is special. self._write_pure_get_input_method(klass, out) elif m == 'GetOutputPort': # This method sometimes prints warnings so we handle # it specially.GetInput is special. self._write_pure_get_output_port_method(klass, out) else: name = self._reform_name(m[3:]) sig = parser.get_method_signature(vtk_get_meth) simple_get = 0 if len(sig) == 1 and sig[0][1] is None: simple_get = 1 elif len(sig) > 1: for i in sig: if i[1] is None: simple_get = 1 break if simple_get: self._write_property(out, name, vtk_get_meth, None) else: # Cannot be represented as a simple property, # so we wrap it as a plain old method. self._write_tvtk_method(out, vtk_get_meth, sig) def _gen_other_methods(self, klass, out): parser = self.parser meths = parser.get_other_methods() for m in meths: vtk_meth = getattr(klass, m) self._write_tvtk_method(out, vtk_meth) ################################################################# # Private utility methods. ################################################################# def _reform_name(self, name, method=False): """Converts a VTK name to an Enthought style one. If `method` is True it does not touch names that are all upper case.""" if name == 'TeX': # Special case for some of the names. TeX occurs in the # vtkGL2PSExporter class. return 'tex' if name.isupper() and method: # All upper case names should remain the same since they # are usually special methods. return name res = camel2enthought(name) if keyword.iskeyword(res): return res + '_' else: return res def _get_class_name(self, klasses): """Returns renamed VTK classes as per TVTK naming style.""" ret = [] if type(klasses) in (types.ListType, types.TupleType): return [get_tvtk_name(x.__name__) \ for x in klasses] else: return get_tvtk_name(klasses.__name__) def _find_type(self, val): """Given `val` which is extracted from the method call signature, this returns the type of the value. Right now this is in ['vtk', 'array', 'basic']. 'vtk' refers to a VTK type, 'array' to a vtkDataArray/vtkCellArray/vtkPoints/vtkIdList, 'basic' refers to a non-VTK, basic Python type. """ _arr_types = ['Array', 'vtkPoints', 'vtkIdList'] s = repr(val) if s.find('vtk') > -1: for i in _arr_types: if s.find(i) > -1: return 'array' return 'vtk' else: return 'basic' def _find_arg_type(self, sig): """Given a method signature `sig`, this finds the argument types. It uses the `_find_type` method to obtain its result. If no arguments are present in *all* of the signatures, then it returns `None`. """ if len(sig) == 1: if sig[0][1] is None: return None args = [s[1] for s in sig] return self._find_type(args) def _find_return_type(self, sig): """Given a method signature `sig`, this finds the return types. """ rets = [s[0] for s in sig] return self._find_type(rets) def _find_sig_type(self, sig): """Given a method signature `sig`, this finds the return and argument types using. This is a convenience type and returns a tuple containing (ret_type, arg_type). """ return self._find_return_type(sig), self._find_arg_type(sig) def _find_array_arg_sig(self, sig): """Returns a list of argument signatures from the signature information for a method. """ return [s[1] for s in sig] ################################################################# # The following methods do the writing. ################################################################# def _write_get_output_method(self, klass, out, set=False): """Write the get_output method. This method is special and needs special care. `klass` is the class for which the method is being wrapped, `out` is the output file. If `set` is True, a set_output method is also wrapped. This defaults to False. """ vtk_get_meth = getattr(klass, 'GetOutput') sig = self.parser.get_method_signature(vtk_get_meth) # First write out a property. doc = "Output of this source, i.e. the result of `get_output()`." if set: trait_def = """ def _get_output(self): return wrap_vtk(self._vtk_obj.GetOutput()) def _set_output(self, obj): old_val = self._get_output() self._wrap_call(self._vtk_obj.SetOutput, deref_vtk(obj)) self.trait_property_changed('output', old_val, obj) output = traits.Property(_get_output, _set_output, help=\"%(doc)s\") """%locals() else: trait_def = """ def _get_output(self): return wrap_vtk(self._vtk_obj.GetOutput()) output = traits.Property(_get_output, help=\"%(doc)s\") """%locals() out.write(self.indent.format(trait_def)) # Now write the generic method. if len(sig) == 1: decl = "def get_output(self):" body = "return wrap_vtk(self._vtk_obj.GetOutput())" else: decl = "def get_output(self, idx=None):" body = """ if idx is None: return wrap_vtk(self._vtk_obj.GetOutput()) else: return wrap_vtk(self._vtk_obj.GetOutput(idx)) """ self._write_generic_method(out, decl, vtk_get_meth, body) if set: decl = "def set_output(self, obj):" body = "old_val = self._get_output()\n" body += "self._wrap_call(self._vtk_obj.SetOutput, deref_vtk(obj))\n" body += "self.trait_property_changed('output', old_val, obj)\n" vtk_set_meth = getattr(klass, 'SetOutput') self._write_generic_method(out, decl, vtk_set_meth, body) def _write_get_source_method(self, klass, out): """Write the set/get_source method. This method needs special care. `klass` is the class for which the method is being wrapped, `out` is the output file. """ vtk_get_meth = getattr(klass, 'GetSource') vtk_set_meth = getattr(klass, 'SetSource') set_sig = self.parser.get_method_signature(vtk_set_meth) if len(set_sig) > 1: # Special case. First write the property for the first source. doc = "The first source of this object, i.e. the result of `get_source(0)`." trait_def = """ def _get_source(self): return wrap_vtk(self._vtk_obj.GetSource(0)) def _set_source(self, obj): old_val = self._get_source() self._wrap_call(self._vtk_obj.SetSource, deref_vtk(obj)) self.trait_property_changed('source', old_val, obj) source = traits.Property(_get_source, _set_source, help=\"%(doc)s\") """%locals() out.write(self.indent.format(trait_def)) # Now wrap the set_source and get_source. self._write_tvtk_method(out, vtk_get_meth) self._write_tvtk_method(out, vtk_set_meth, set_sig) else: self._write_property(out, 'source', vtk_get_meth, vtk_set_meth) def _write_pure_get_output_port_method(self, klass, out): """Handle the GetOutputPort method so that it does not print unnecessary warning messages. `klass` is the class for which the method is being wrapped, `out` is the output file. """ vtk_get_meth = getattr(klass, 'GetOutputPort') t_def = """ def _get_output_port(self): if self._vtk_obj.GetNumberOfOutputPorts(): return wrap_vtk(self._vtk_obj.GetOutputPort()) else: return None """%locals() indent = self.indent out.write(indent.format(t_def)) t_def = """output_port = traits.Property(_get_output_port, help=\\""" out.write(indent.format(t_def)) doc = vtk_get_meth.__doc__ self.dm.write_trait_doc(doc, out, indent) # Close the function definition. out.write(indent.format(')')) out.write('\n') def _write_pure_get_input_method(self, klass, out): """Write the get_input method when the class only has the getter and no setter. `klass` is the class for which the method is being wrapped, `out` is the output file. """ vtk_get_meth = getattr(klass, 'GetInput') get_sig = self.parser.get_method_signature(vtk_get_meth) if len(get_sig) > 1: # Special case. First write the property for the first input. doc = "The first input of this object, i.e. the result of `get_input(0)`." trait_def = """ def _get_input(self): try: return wrap_vtk(self._vtk_obj.GetInput(0)) except TypeError: return wrap_vtk(self._vtk_obj.GetInput()) input = traits.Property(_get_input, help=\"%(doc)s\") """%locals() out.write(self.indent.format(trait_def)) # Now wrap the get_input with args. self._write_tvtk_method(out, vtk_get_meth) else: self._write_property(out, 'input', vtk_get_meth, None) def _write_get_input_method(self, klass, out): """Write the set/get_input method. This method needs special care. `klass` is the class for which the method is being wrapped, `out` is the output file. """ vtk_get_meth = getattr(klass, 'GetInput') vtk_set_meth = getattr(klass, 'SetInput') set_sig = self.parser.get_method_signature(vtk_set_meth) if len(set_sig) > 1: # Special case. First write the property for the first input. doc = "The first input of this object, i.e. the result of `get_input(0)`." trait_def = """ def _get_input(self): try: return wrap_vtk(self._vtk_obj.GetInput(0)) except TypeError: return wrap_vtk(self._vtk_obj.GetInput()) def _set_input(self, obj): old_val = self._get_input() self._wrap_call(self._vtk_obj.SetInput, deref_vtk(obj)) self.trait_property_changed('input', old_val, obj) input = traits.Property(_get_input, _set_input, help=\"%(doc)s\") """%locals() out.write(self.indent.format(trait_def)) # Now wrap the set_input and get_input. self._write_tvtk_method(out, vtk_get_meth) self._write_tvtk_method(out, vtk_set_meth, set_sig) else: self._write_property(out, 'input', vtk_get_meth, vtk_set_meth) def _write_get_input_connection_method(self, klass, out): """Write the set/get_input_connection method. This method needs needs to be wrapped as a property and a method for convenience. `klass` is the class for which the method is being wrapped, `out` is the output file. """ vtk_get_meth = getattr(klass, 'GetInputConnection') vtk_set_meth = getattr(klass, 'SetInputConnection') doc = "The first input connection for this object, i.e. the result of `get_input_connection(0, 0)`." trait_def = """ def _get_input_connection(self): if self._vtk_obj.GetTotalNumberOfInputConnections(): return wrap_vtk(self._vtk_obj.GetInputConnection(0, 0)) else: return None def _set_input_connection(self, obj): old_val = self._get_input_connection() self._wrap_call(self._vtk_obj.SetInputConnection, deref_vtk(obj)) self.trait_property_changed('input_connection', old_val, obj) input_connection = traits.Property(_get_input_connection, _set_input_connection, help=\"%(doc)s\") """%locals() out.write(self.indent.format(trait_def)) # Now wrap the set_input_connection and get_input_connection. self._write_tvtk_method(out, vtk_get_meth) self._write_tvtk_method(out, vtk_set_meth) def _write_tvtk_method(self, out, vtk_meth, sig=None): """Write a generic tvtk_method to `out`. Parameters ---------- - out : file like object - vtk_meth : VTK method The VTK method to wrap. A docstring is extracted from this. - sig : Signature of vtk_method (default: None) If None, this is computed. If not, the passed signature information is used. """ if not sig: sig = self.parser.get_method_signature(vtk_meth) # Figure out if we really need to wrap the return and deref # the args. ret_type, arg_type = self._find_sig_type(sig) vtk_m_name = vtk_meth.__name__ name = self._reform_name(vtk_m_name, method=True) if keyword.iskeyword(name): name = name + '_' method_affects_input = vtk_m_name in ['AddInput', 'RemoveInput', 'RemoveAllInputs', 'SetInputByNumber'] if arg_type is None: decl = 'def %s(self):'%name body = "" if method_affects_input: body += "old_val = self._get_input()\n" if ret_type in ['vtk', 'array']: body += "ret = wrap_vtk(self._vtk_obj.%s())\n"\ %vtk_m_name else: body += "ret = self._vtk_obj.%s()\n"\ %vtk_m_name if method_affects_input: body += "self.trait_property_changed('input', old_val, self._get_input())\n" body += "return ret\n\n" else: decl = 'def %s(self, *args):'%name if arg_type == 'vtk': body = "" if method_affects_input: body += "old_val = self._get_input()\n" body += "my_args = [deref_vtk(x) for x in args]\n"\ "ret = self._wrap_call(self._vtk_obj.%s, *my_args)\n"\ %vtk_m_name if method_affects_input: body += "self.trait_property_changed('input', old_val, self._get_input())\n" elif arg_type == 'array': arr_sig = self._find_array_arg_sig(sig) body = "my_args = deref_array(args, %s)\n"\ "ret = self._wrap_call(self._vtk_obj.%s, *my_args)\n"\ %(arr_sig, vtk_m_name) else: body = "ret = self._wrap_call(self._vtk_obj.%s, *args)\n"\ %vtk_m_name if ret_type in ['vtk', 'array']: body += "return wrap_vtk(ret)\n" else: body += "return ret\n" self._write_generic_method(out, decl, vtk_meth, body) def _write_generic_method(self, out, decl, vtk_doc_meth, body): """Write out a method given the declaration, `decl`, the VTK method, `vtk_doc_meth`, from which the docstring is to be extracted and the code body, `body`. Each of these is set as per the current indentation level. output is written to the `out` object. `vtk_doc_meth` could also be a string, in which case the string is used directly. """ if type(vtk_doc_meth) in types.StringTypes: doc = vtk_doc_meth else: # Must be a method so get the docstring. doc = self.dm.get_method_doc(vtk_doc_meth.__doc__) indent = self.indent out.write(indent.format(decl)) indent.incr() if doc: out.write(indent.format('"""\n' + doc + '"""\n')) out.write(indent.format(body)) out.write('\n') indent.decr() def _write_trait(self, out, t_name, t_def, vtk_set_meth, mapped, force_update=None, broken_bool=False): """Write out a complete trait definition to `out`. Parameters ---------- - out : File like object. - t_name : `string`: Name of the trait. - t_def : `string` : Trait definition. - vtk_set_meth : VTK setter method. - mapped : `bool` : Specifies if the trait is mapped. - force_update : `string` or `None` : force_update argument. - broken_bool : `bool` (default: `False`) If `True` the bool method does not have a 'Set' method and must be handled specially. In this case make sure that the vtk_set_meth points to the 'Get' method. """ changed = '_%s_changed'%t_name vtk_m_name = vtk_set_meth.__name__ map_str = '' if mapped: map_str = '_' force_str = '' if force_update is not None: force_str = ', %s'%force_update # Fixing the trait definition in order to handle the help trait. if t_def.endswith(')'): t_def = t_def[:-1] + ', help=\\' else: t_def += '(help=\\' trait_def = '%(t_name)s = %(t_def)s'%locals() if broken_bool: msg = "If broken_bool is true, make sure vtk_set_meth "\ "is of form 'GetProp'" assert vtk_m_name[:3] == 'Get', msg vtk_on_name = vtk_m_name[3:] + 'On' vtk_off_name = vtk_m_name[3:] + 'Off' changed_def = """ def %(changed)s(self, old_val, new_val): def _bool_change(val, obj=self._vtk_obj): if val: obj.%(vtk_on_name)s() else: obj.%(vtk_off_name)s() self._do_change(_bool_change, self.%(t_name)s%(map_str)s%(force_str)s) """%locals() else: changed_def = """ def %(changed)s(self, old_val, new_val): self._do_change(self._vtk_obj.%(vtk_m_name)s, self.%(t_name)s%(map_str)s%(force_str)s) """%locals() indent = self.indent # First write the trait definition. out.write(indent.format(trait_def)) # Write the help docs. doc = vtk_set_meth.__doc__ self.dm.write_trait_doc(doc, out, indent) # End the function definition. out.write(indent.format(')')) # Write the handler method. out.write(indent.format(changed_def)) out.write('\n') def _write_property(self, out, t_name, vtk_get_meth, vtk_set_meth, multi_arg=False): """Writes out a traited property to `out` given the trait name, `t_name`, the VTK get method, `vtk_get_meth` an optional VTK set method for read-write traits as, `vtk_set_meth` plus a boolean value for `multi_arg`. If `multi_arg` is True, the setter is treated as if it accepts a list of parameters. If not the setter is treated as if it accepts a single parameter. """ indent = self.indent getter = '_get_%s'%t_name vtk_get_name = vtk_get_meth.__name__ sig = self.parser.get_method_signature(vtk_get_meth) ret_type = self._find_return_type(sig) if ret_type in ['vtk', 'array']: trait_def = """ def %(getter)s(self): return wrap_vtk(self._vtk_obj.%(vtk_get_name)s()) """%locals() else: trait_def = """ def %(getter)s(self): return self._vtk_obj.%(vtk_get_name)s() """%locals() out.write(indent.format(trait_def)) if vtk_set_meth: setter = '_set_%s'%t_name vtk_set_name = vtk_set_meth.__name__ sig = self.parser.get_method_signature(vtk_set_meth) arg_type = self._find_arg_type(sig) if multi_arg: if arg_type == 'vtk': trait_def = """ def %(setter)s(self, *args): old_val = self.%(getter)s() my_args = [deref_vtk(x) for x in args] self._wrap_call(self._vtk_obj.%(vtk_set_name)s, *my_args) self.trait_property_changed('%(t_name)s', old_val, args) """%locals() elif arg_type == 'array': arr_sig = self._find_array_arg_sig(sig) trait_def = """ def %(setter)s(self, *args): old_val = self.%(getter)s() my_args = deref_array(args, %(arr_sig)s) self._wrap_call(self._vtk_obj.%(vtk_set_name)s, *my_args) self.trait_property_changed('%(t_name)s', old_val, args) """%locals() else: trait_def = """ def %(setter)s(self, *args): old_val = self.%(getter)s() self._wrap_call(self._vtk_obj.%(vtk_set_name)s, *args) self.trait_property_changed('%(t_name)s', old_val, args) """%locals() else: if arg_type == 'vtk': trait_def = """ def %(setter)s(self, arg): old_val = self.%(getter)s() self._wrap_call(self._vtk_obj.%(vtk_set_name)s, deref_vtk(arg)) self.trait_property_changed('%(t_name)s', old_val, arg) """%locals() elif arg_type == 'array': arr_sig = self._find_array_arg_sig(sig) trait_def = """ def %(setter)s(self, arg): old_val = self.%(getter)s() my_arg = deref_array([arg], %(arr_sig)s) self._wrap_call(self._vtk_obj.%(vtk_set_name)s, my_arg[0]) self.trait_property_changed('%(t_name)s', old_val, arg) """%locals() else: trait_def = """ def %(setter)s(self, arg): old_val = self.%(getter)s() self._wrap_call(self._vtk_obj.%(vtk_set_name)s, arg) self.trait_property_changed('%(t_name)s', old_val, arg) """%locals() out.write(indent.format(trait_def)) t_def = "traits.Property(%(getter)s, %(setter)s, help=\\"%locals() else: t_def = "traits.Property(%(getter)s, help=\\"%locals() trait_def = """%(t_name)s = %(t_def)s"""%locals() out.write(indent.format(trait_def)) doc = vtk_get_meth.__doc__ self.dm.write_trait_doc(doc, out, indent) # Close the function definition. out.write(indent.format(')')) out.write('\n') mayavi-4.1.0/DEVELOPING.rst0000644000175100001440000000120611674464502016161 0ustar ischnellusers00000000000000 Building documentation ========================= In the top-level directory, run:: python setup.py build_docs Uploading docs on the web page ================================= The docs live on their own gh-pages git branch. So to update, starting from the root of the project here is what you do:: # Recompile docs cd docs/ make html # Copy to other branch cd .. git checkout gh-pages cp -r docs/build/tvtk/html/* tvtk/ cp -r docs/build/mayavi/html/* mayavi/ git add tvtk mayavi # Commit and push new docs git commit -a -m "Updated docs" git push # Return to master branch git checkout master mayavi-4.1.0/README.rst0000644000175100001440000001045111674464502015464 0ustar ischnellusers00000000000000======================================================= MayaVi2: 3D visualization of scientific data in Python ======================================================= http://github.enthought.com/mayavi/mayavi Vision ====== MayaVi2_ seeks to provide easy and interactive visualization of 3D data. It does this by the following: - an (optional) rich user interface with dialogs to interact with all data and objects in the visualization. - a simple and clean scripting interface in Python, including one-liners, a-la mlab, or object-oriented programming interface. - harnesses the power of the VTK toolkit without forcing you to learn it. Additionally Mayavi2 strives to be a reusable tool that can be embedded in your applications in different ways or combined with the envisage application-building framework to assemble domain-specific tools. Mayavi is part of the Enthought Tool Suite (ETS). Features =========== MayaVi2 is a general purpose, cross-platform tool for 2-D and 3-D scientific data visualization. Its features include: * Visualization of scalar, vector and tensor data in 2 and 3 dimensions * Easy scriptability using Python * Easy extendability via custom sources, modules, and data filters * Reading several file formats: VTK (legacy and XML), PLOT3D, etc. * Saving of visualizations * Saving rendered visualization in a variety of image formats * Convenient functionality for rapid scientific plotting via mlab (see mlab documentation) * See the MayaVi2 Users Guide for more information. Unlike its predecessor MayaVi1_, Mayavi2 has been designed with scriptability and extensibility in mind from the ground up. While the mayavi2 application is usable by itself, it may be used as an Envisage plugin which allows it to be embedded in user applications natively. Alternatively, it may be used as a visualization engine for any application. .. _MayaVi1: http://mayavi.sf.net Quick start =========== If you are new to mayavi it is a good idea to read the users guide which should introduce you to how to install and use it. The user guide is available in the `docs` directory and also available from the mayavi home page. If you have installed `mayavi` as described in the previous section you should be able to launch the `mayavi2` application and also run any of the examples in the examples directory. Getting the package =================== General Build and Installation instructions for ETS are available here: http://github.enthought.com/mayavi/mayavi/installation.html Source tarballs for all stable ETS packages are available at http://code.enthought.com/enstaller/eggs/source Documentation ============== More documentation is available in the online user manual, http://github.enthought.com/mayavi/mayavi or in `docs` directory of the sources. This includes a man page for the `mayavi2` application, a users guide in HTML and PDF format and documentation for `mlab`. Examples ======== Examples are all in the `examples` directory of the source or the SVN checkout. The docs and examples do not ship with the binary eggs. The examples directory also contains some sample data. Test suite ========== The test suite may be run like so (on a bash shell):: cd tests for i in test*.py; do python $i; done Use a similar line for your particular shell. Bug tracker, mailing list etc. ============================== The bug tracker is available as part of the trac interface here: https://svn.enthought.com/enthought/ To submit a bug you will necessarily have to register at the site. Click on the "register" link at the top right on the above page to register. Or login if you already have registered. Once you are registered you may file a bug by creating a new ticket. Alternatively, you can post on the enthought-dev@mail.enthought.com mailing list. Authors and Contributors ======================== * Core contribtuors: Prabhu Ramachandran: primary author. Gal Varoquaux: mlab, icons, many general improvements and maintainance. * Support and code contributions from Enthought Inc. * Patches from many people (see the release notes), including K K Rai and R A Ambareesha for tensor support, parametric source and image data. Many thanks to all those who have submitted bug reports and suggestions for further enhancements. mayavi-4.1.0/scripts/0000755000175100001440000000000011674464502015463 5ustar ischnellusers00000000000000mayavi-4.1.0/scripts/gen_lut_previews.py0000644000175100001440000000147011674464502021420 0ustar ischnellusers00000000000000""" Script to generate the preview images for the mayavi2 LUTs. Requires ImageMagick. """ import os from mayavi import mlab from mayavi.core.lut_manager import lut_mode_list, lut_image_dir import numpy as np # Create some data X = np.arange(0, 255) X = X * np.ones((200, 1)) mlab.clf() image = mlab.imshow(X.T) mlab.view(0, 0, 118) # Make a preview for each possible lut for lut in lut_mode_list(): filebasename = os.path.join(lut_image_dir, lut.lower()) if not lut == 'file': image.module_manager.scalar_lut_manager.lut_mode = lut mlab.savefig(filebasename + '.png', size=(80, 20)) #os.system('convert %s.png %s.gif &' %(filebasename, filebasename)) os.system('montage -geometry -0-0 -label "%s" %s.png %s.gif &' % (lut, filebasename, filebasename) ) mayavi-4.1.0/scripts/cm2lut.py0000755000175100001440000000161411674464502017250 0ustar ischnellusers00000000000000#!/usr/bin/env python """ Script used to create lut lists used by mayavi from matplotlib colormaps. This requires matlplotlib to be installed and should not be ran by the user, but only once in a while to synchronize with MPL developpement. """ # Authors: Frederic Petit , # Gael Varoquaux # Copyright (c) 2007-2009, Enthought, Inc. # License: BSD Style. import os import numpy as np from matplotlib.cm import datad, get_cmap from mayavi.core import lut as destination_module from apptools.persistence import state_pickler target_dir = os.path.dirname(destination_module.__file__) values = np.linspace(0., 1., 256) lut_dic = {} for name in datad.keys(): if name.endswith('_r'): continue lut_dic[name] = get_cmap(name)(values.copy()) out_name = os.path.join(target_dir, 'pylab_luts.pkl') state_pickler.dump(lut_dic, out_name) mayavi-4.1.0/LICENSE_COLORBREWER.txt0000644000175100001440000000363211674464502017470 0ustar ischnellusers00000000000000Apache-Style Software License for ColorBrewer Color Schemes Version 1.1 Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions as source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes color specifications and designs developed by Cynthia Brewer (http://colorbrewer.org/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 3. The name "ColorBrewer" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact Cynthia Brewer at cbrewer@psu.edu. 4. Products derived from this software may not be called "ColorBrewer", nor may "ColorBrewer" appear in their name, without prior written permission of Cynthia Brewer. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CYNTHIA BREWER, MARK HARROWER, OR THE PENNSYLVANIA STATE UNIVERSITY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mayavi-4.1.0/image_LICENSE_CP.txt0000644000175100001440000004671611674464502017361 0ustar ischnellusers00000000000000License The Crystal Project are released under LGPL. GNU General Public License. 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a. The modified work must itself be a software library. b. You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c. You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d. If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a. Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) . b. Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c. Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d. If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e. Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a. Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b. Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. No Warranty 15. Because the library is licensed free of charge, there is no warranty for the library, to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide the library "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the library is with you. Should the library prove defective, you assume the cost of all necessary servicing, repair or correction. 16. In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute the library as permitted above, be liable to you for damages, including any general, special, incidental or consequential damages arising out of the use or inability to use the library (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties or a failure of the library to operate with any other software), even if such holder or other party has been advised of the possibility of such damages. mayavi-4.1.0/setup.py0000644000175100001440000004166511674464502015522 0ustar ischnellusers00000000000000#!/usr/bin/env python # # Copyright (c) 2008-2011 by Enthought, Inc. # All rights reserved. """ The Mayavi scientific data 3-dimensional visualizer. The Mayavi *project* includes two related *packages* for 3-dimensional visualization: - **Mayavi2**: A tool for easy and interactive visualization of data. - **TVTK**: A Traits-based wrapper for the Visualization Toolkit, a popular open-source visualization library. These libraries operate at different levels of abstraction. TVTK manipulates visualization objects, while Mayavi2 lets you operate on your data, and then see the results. Most users either use the Mayavi user interface or program to its scripting interface; you probably don't need to interact with TVTK unless you want to create a new Mayavi module. Mayavi2 ------- Mayavi2 seeks to provide easy and interactive visualization of 3-D data. It offers: - An (optional) rich user interface with dialogs to interact with all data and objects in the visualization. - A simple and clean scripting interface in Python, including one-liners, or an object-oriented programming interface. - The power of the VTK toolkit, harnessed through these interfaces, without forcing you to learn it. Additionally Mayavi2 is a reusable tool that can be embedded in your applications in different ways or combined with the Envisage application-building framework to assemble domain-specific tools. TVTK ---- TVTK wraps VTK objects to provide a convenient, Pythonic API, while supporting Traits attributes and NumPy/SciPy arrays. TVTK is implemented mostly in pure Python, except for a small extension module. Developers typically use TVTK to write Mayavi modules, and then use Mayavi to interact with visualizations or create applications. Prerequisites ------------- You must have the following libraries installed before installing the Mayavi project: * `Numpy `_ version 1.1.1 or later * `VTK `_ version 5.0 or later * `wxPython `_ version 2.8 or later * `configobj `_ """ # NOTE: Setuptools must be imported BEFORE numpy.distutils or else # numpy.distutils does the Wrong(TM) thing. import setuptools from setuptools import Command import numpy import os import subprocess import shutil import re import sys import traceback from os.path import (abspath, basename, dirname, exists, getmtime, isdir, join, split, splitext) from numpy.distutils.command import build, install_data from distutils import log from setuptools.command import develop, install_scripts info = {} execfile(join('mayavi', '__init__.py'), info) DEFAULT_HTML_TARGET_DIR = join('build', 'docs', 'html') DEFAULT_INPUT_DIR = join('docs', 'source',) class GenDocs(Command): description = \ "This command generates generated part of the documentation " \ "when needed. It's run automatically before a build_docs, and that's " \ "the only time it needs to be run." user_options = [ ('None', None, 'this command has no options'), ] def latest_modified(self, the_path, filetypes='', ignore_dirs=''): """Traverses a path looking for the most recently modified file Parameters ---------- the_path : string Contains path to be traversed or filename to be inspected. filetypes : string Regular expression pattern of files to examine. If specified, other files are ignored. Otherwise, all files are examined. ignore_dirs : string Regular expression pattern of directories to be ignored. If ignore specified, all directories are walked. Returns ------- latest_time : float Modification time of latest_path. latest_path : string Most recently modified file. Description ----------- """ file_re = re.compile(filetypes) dir_re = re.compile(ignore_dirs) if not exists(the_path): return 0, the_path if isdir(the_path): latest_time = 0 latest_path = the_path for root, dirs, files in os.walk(the_path): if ignore_dirs != '': # This needs to iterate over a copy of the list. Otherwise, # as things get removed from the original list, the indices # become invalid. for dir in dirs[:]: if dir_re.search(dir): dirs.remove(dir) for file in files: if filetypes != '': if not file_re.search(file): continue current_file_time = getmtime(join(root, file)) if current_file_time > latest_time: latest_time = current_file_time latest_path = join(root, file) return latest_time, latest_path else: return getmtime(the_path), the_path def mlab_reference(self): """ If mayavi is installed, run the mlab_reference generator. """ # XXX: This is really a hack: the script is not made to be used # for different projects, but it ended up being. This part is # mayavi-specific. mlab_ref_dir = join(DEFAULT_INPUT_DIR, 'mayavi','auto') source_path = 'mayavi' sources = '(\.py)|(\.rst)$' excluded_dirs = '^\.' target_path = mlab_ref_dir target_time = self.latest_modified(target_path, ignore_dirs=excluded_dirs)[0] if (self.latest_modified(source_path, filetypes=sources, ignore_dirs=excluded_dirs)[0] > target_time or self.latest_modified('mlab_reference.py')[0] > target_time or not exists(join('docs', 'source', 'mayavi', 'auto', 'mlab_reference.rst')) ): try: from mayavi import mlab from mayavi.tools import auto_doc print "Generating the mlab reference documentation" os.system('python mlab_reference.py') except: pass def example_files(self): """ Generate the documentation files for the examples. """ mlab_ref_dir = join(DEFAULT_INPUT_DIR, 'mayavi','auto') source_path = join('examples', 'mayavi') sources = '(\.py)|(\.rst)$' excluded_dirs = '^\.' target_path = mlab_ref_dir target_time = self.latest_modified(target_path, ignore_dirs=excluded_dirs)[0] script_file_name = join('docs', 'source', 'render_examples.py') if (self.latest_modified(source_path, filetypes=sources, ignore_dirs=excluded_dirs)[0] > target_time or self.latest_modified(script_file_name)[0] > target_time or not exists(join('docs', 'source', 'mayavi', 'auto', 'examples.rst')) ): try: from mayavi import mlab from mayavi.tools import auto_doc print "Generating the example list" subprocess.call('python %s' % basename(script_file_name), shell=True, cwd=dirname(script_file_name)) except: pass def run(self): self.mlab_reference() self.example_files() def initialize_options(self): pass def finalize_options(self): pass class BuildDocs(Command): description = \ "This command generates the documentation by running Sphinx. " \ "It then zips the docs into an html.zip file." user_options = [ ('None', None, 'this command has no options'), ] def make_docs(self): if os.name == 'nt': print "Please impelemnt sphinx building on windows here." else: subprocess.call(['make', 'html'], cwd='docs') def run(self): self.make_docs() def initialize_options(self): pass def finalize_options(self): pass # Functions to generate the docs def list_doc_projects(): """ List the different source directories under DEFAULT_INPUT_DIR for which we have docs. """ source_dir = join(abspath(dirname(__file__)), DEFAULT_INPUT_DIR) source_list = os.listdir(source_dir) # Check to make sure we're using non-hidden directories. source_dirs = [listing for listing in source_list if isdir(join(source_dir, listing)) and not listing.startswith('.')] return source_dirs def list_docs_data_files(project): """ List the files to add to a project by inspecting the documentation directory. This works only if called after the build step, as the files have to be built. returns a list of (install_dir, [data_files, ]) tuples. """ project_target_dir = join(DEFAULT_HTML_TARGET_DIR, project) return_list = [] for root, dirs, files in os.walk(project_target_dir, topdown=True): # Modify inplace the list of directories to walk dirs[:] = [d for d in dirs if not d.startswith('.')] if len(files) == 0: continue install_dir = root.replace(project_target_dir, join(project, 'html')) return_list.append((install_dir, [join(root, f) for f in files])) return return_list # Our custom distutils hooks def build_tvtk_classes_zip(): tvtk_dir = 'tvtk' sys.path.insert(0, tvtk_dir) from setup import gen_tvtk_classes_zip gen_tvtk_classes_zip() sys.path.remove(tvtk_dir) class MyBuild(build.build): """ A build hook to generate the documentation. We sub-class numpy.distutils' build command because we're relying on numpy.distutils' setup method to build python extensions. """ def run(self): build_tvtk_classes_zip() build.build.run(self) self.run_command('gen_docs') try: self.run_command('build_docs') except: log.warn("Couldn't build documentation:\n%s" % traceback.format_exception(*sys.exc_info())) class MyDevelop(develop.develop): """ A hook to have the docs rebuilt during develop. Subclassing setuptools' command because numpy.distutils doesn't have an implementation. """ def run(self): self.run_command('gen_docs') try: self.run_command('build_docs') except: log.warn("Could not build documentation:\n%s" % traceback.format_exception(*sys.exc_info())) # Make sure that the 'build_src' command will # always be inplace when we do a 'develop'. self.reinitialize_command('build_src', inplace=1) # tvtk_classes.zip always need to be created on 'develop'. build_tvtk_classes_zip() develop.develop.run(self) class MyInstallData(install_data.install_data): """ An install hook to copy the generated documentation. We subclass numpy.distutils' command because we're relying on numpy.distutils' setup method to build python extensions. """ def run(self): install_data_command = self.get_finalized_command('install_data') for project in list_doc_projects(): install_data_command.data_files.extend( list_docs_data_files(project)) # make sure tvtk_classes.zip always get created before putting it # in the install data. build_tvtk_classes_zip() tvtk_dir = 'tvtk' install_data_command.data_files.append( (tvtk_dir, [join(tvtk_dir, 'tvtk_classes.zip')])) install_data.install_data.run(self) class MyInstallScripts(install_scripts.install_scripts): """ Hook to rename the mayavi script to a MayaVi.pyw script on win32. Subclassing setuptools' command because numpy.distutils doesn't have an implementation. """ def run(self): install_scripts.install_scripts.run(self) if os.name != 'posix': # Rename